track_run_iterator_unittest.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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.
4
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"
13
14// The sum of the elements in a vector initialized with SumAscending,
15// less the value of the last element.
16static const int kSumAscending1 = 45;
17
18static const int kAudioScale = 48000;
19static const int kVideoScale = 25;
20
21static const uint8 kAuxInfo[] = {
22  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
23  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
24  0x00, 0x02,
25  0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
26  0x00, 0x03, 0x00, 0x00, 0x00, 0x04
27};
28
29static const char kIv1[] = {
30  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
31  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
32};
33
34static const uint8 kKeyId[] = {
35  0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
36  0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
37};
38
39static const uint8 kCencSampleGroupKeyId[] = {
40  0x46, 0x72, 0x61, 0x67, 0x53, 0x61, 0x6d, 0x70,
41  0x6c, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b
42};
43
44namespace media {
45namespace mp4 {
46
47class TrackRunIteratorTest : public testing::Test {
48 public:
49  TrackRunIteratorTest() {
50    CreateMovie();
51  }
52
53 protected:
54  Movie moov_;
55  LogCB log_cb_;
56  scoped_ptr<TrackRunIterator> iter_;
57
58  void CreateMovie() {
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;
69    desc1.type = kAudio;
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 =
74        false;
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;
82    desc2.type = kVideo;
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;
91
92    moov_.tracks[2].header.track_id = 3;
93    moov_.tracks[2].media.information.sample_table.description.type = kHint;
94  }
95
96  uint32 ToSampleFlags(const std::string& str) {
97    CHECK_EQ(str.length(), 2u);
98
99    SampleDependsOn sample_depends_on = kSampleDependsOnReserved;
100    bool is_non_sync_sample = false;
101    switch(str[0]) {
102      case 'U':
103        sample_depends_on = kSampleDependsOnUnknown;
104        break;
105      case 'O':
106        sample_depends_on = kSampleDependsOnOthers;
107        break;
108      case 'N':
109        sample_depends_on = kSampleDependsOnNoOther;
110        break;
111      default:
112        CHECK(false) << "Invalid sample dependency character '"
113                     << str[0] << "'";
114        break;
115    }
116
117    switch(str[1]) {
118      case 'S':
119        is_non_sync_sample = false;
120        break;
121      case 'N':
122        is_non_sync_sample = true;
123        break;
124      default:
125        CHECK(false) << "Invalid sync sample character '"
126                     << str[1] << "'";
127        break;
128    }
129    uint32 flags = static_cast<uint32>(sample_depends_on) << 24;
130    if (is_non_sync_sample)
131      flags |= kSampleIsNonSyncSample;
132    return flags;
133  }
134
135  void SetFlagsOnSamples(const std::string& sample_info,
136                         TrackFragmentRun* trun) {
137    // US - SampleDependsOnUnknown & IsSyncSample
138    // UN - SampleDependsOnUnknown & IsNonSyncSample
139    // OS - SampleDependsOnOthers & IsSyncSample
140    // ON - SampleDependsOnOthers & IsNonSyncSample
141    // NS - SampleDependsOnNoOthers & IsSyncSample
142    // NN - SampleDependsOnNoOthers & IsNonSyncSample
143    std::vector<std::string> flags_data;
144    base::SplitString(sample_info, ' ', &flags_data);
145
146    if (flags_data.size() == 1u) {
147      // Simulates the first_sample_flags_present set scenario,
148      // where only one sample_flag value is set and the default
149      // flags are used for everything else.
150      ASSERT_GE(trun->sample_count, flags_data.size());
151    } else {
152      ASSERT_EQ(trun->sample_count, flags_data.size());
153    }
154
155    trun->sample_flags.resize(flags_data.size());
156    for (size_t i = 0; i < flags_data.size(); i++)
157      trun->sample_flags[i] = ToSampleFlags(flags_data[i]);
158  }
159
160  std::string KeyframeAndRAPInfo(TrackRunIterator* iter) {
161    CHECK(iter->IsRunValid());
162    std::stringstream ss;
163    ss << iter->track_id();
164
165    while (iter->IsSampleValid()) {
166      ss << " " << (iter->is_keyframe() ? "K" : "P");
167      if (iter->is_random_access_point())
168        ss << "R";
169      iter->AdvanceSample();
170    }
171
172    return ss.str();
173  }
174
175  MovieFragment CreateFragment() {
176    MovieFragment moof;
177    moof.tracks.resize(2);
178    moof.tracks[0].decode_time.decode_time = 0;
179    moof.tracks[0].header.track_id = 1;
180    moof.tracks[0].header.has_default_sample_flags = true;
181    moof.tracks[0].header.default_sample_flags = ToSampleFlags("US");
182    moof.tracks[0].header.default_sample_duration = 1024;
183    moof.tracks[0].header.default_sample_size = 4;
184    moof.tracks[0].runs.resize(2);
185    moof.tracks[0].runs[0].sample_count = 10;
186    moof.tracks[0].runs[0].data_offset = 100;
187    SetAscending(&moof.tracks[0].runs[0].sample_sizes);
188
189    moof.tracks[0].runs[1].sample_count = 10;
190    moof.tracks[0].runs[1].data_offset = 10000;
191
192    moof.tracks[1].header.track_id = 2;
193    moof.tracks[1].header.has_default_sample_flags = false;
194    moof.tracks[1].decode_time.decode_time = 10;
195    moof.tracks[1].runs.resize(1);
196    moof.tracks[1].runs[0].sample_count = 10;
197    moof.tracks[1].runs[0].data_offset = 200;
198    SetAscending(&moof.tracks[1].runs[0].sample_sizes);
199    SetAscending(&moof.tracks[1].runs[0].sample_durations);
200    SetFlagsOnSamples("US UN UN UN UN UN UN UN UN UN", &moof.tracks[1].runs[0]);
201
202    return moof;
203  }
204
205  // Update the first sample description of a Track to indicate encryption
206  void AddEncryption(Track* track) {
207    SampleDescription* stsd =
208        &track->media.information.sample_table.description;
209    ProtectionSchemeInfo* sinf;
210    if (!stsd->video_entries.empty()) {
211       sinf = &stsd->video_entries[0].sinf;
212    } else {
213       sinf = &stsd->audio_entries[0].sinf;
214    }
215
216    sinf->type.type = FOURCC_CENC;
217    sinf->info.track_encryption.is_encrypted = true;
218    sinf->info.track_encryption.default_iv_size = 8;
219    sinf->info.track_encryption.default_kid.assign(kKeyId,
220                                                   kKeyId + arraysize(kKeyId));
221  }
222
223  // Add SampleGroupDescription Box with two entries (an unencrypted entry and
224  // an encrypted entry). Populate SampleToGroup Box from input array.
225  void AddCencSampleGroup(TrackFragment* frag,
226                          const SampleToGroupEntry* sample_to_group_entries,
227                          size_t num_entries) {
228    frag->sample_group_description.grouping_type = FOURCC_SEIG;
229    frag->sample_group_description.entries.resize(2);
230    frag->sample_group_description.entries[0].is_encrypted = false;
231    frag->sample_group_description.entries[0].iv_size = 0;
232    frag->sample_group_description.entries[1].is_encrypted = true;
233    frag->sample_group_description.entries[1].iv_size = 8;
234    frag->sample_group_description.entries[1].key_id.assign(
235        kCencSampleGroupKeyId,
236        kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
237
238    frag->sample_to_group.grouping_type = FOURCC_SEIG;
239    frag->sample_to_group.entries.assign(sample_to_group_entries,
240                                         sample_to_group_entries + num_entries);
241  }
242
243  // Add aux info covering the first track run to a TrackFragment, and update
244  // the run to ensure it matches length and subsample information.
245  void AddAuxInfoHeaders(int offset, TrackFragment* frag) {
246    frag->auxiliary_offset.offsets.push_back(offset);
247    frag->auxiliary_size.sample_count = 2;
248    frag->auxiliary_size.sample_info_sizes.push_back(8);
249    frag->auxiliary_size.sample_info_sizes.push_back(22);
250    frag->runs[0].sample_count = 2;
251    frag->runs[0].sample_sizes[1] = 10;
252  }
253
254  bool InitMoofWithArbitraryAuxInfo(MovieFragment* moof) {
255    // Add aux info header (equal sized aux info for every sample).
256    for (uint32 i = 0; i < moof->tracks.size(); ++i) {
257      moof->tracks[i].auxiliary_offset.offsets.push_back(50);
258      moof->tracks[i].auxiliary_size.sample_count = 10;
259      moof->tracks[i].auxiliary_size.default_sample_info_size = 8;
260    }
261
262    // We don't care about the actual data in aux.
263    std::vector<uint8> aux_info(1000);
264    return iter_->Init(*moof) &&
265           iter_->CacheAuxInfo(&aux_info[0], aux_info.size());
266  }
267
268  void SetAscending(std::vector<uint32>* vec) {
269    vec->resize(10);
270    for (size_t i = 0; i < vec->size(); i++)
271      (*vec)[i] = i+1;
272  }
273};
274
275TEST_F(TrackRunIteratorTest, NoRunsTest) {
276  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
277  ASSERT_TRUE(iter_->Init(MovieFragment()));
278  EXPECT_FALSE(iter_->IsRunValid());
279  EXPECT_FALSE(iter_->IsSampleValid());
280}
281
282TEST_F(TrackRunIteratorTest, BasicOperationTest) {
283  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
284  MovieFragment moof = CreateFragment();
285
286  // Test that runs are sorted correctly, and that properties of the initial
287  // sample of the first run are correct
288  ASSERT_TRUE(iter_->Init(moof));
289  EXPECT_TRUE(iter_->IsRunValid());
290  EXPECT_FALSE(iter_->is_encrypted());
291  EXPECT_EQ(iter_->track_id(), 1u);
292  EXPECT_EQ(iter_->sample_offset(), 100);
293  EXPECT_EQ(iter_->sample_size(), 1);
294  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kAudioScale));
295  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kAudioScale));
296  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
297  EXPECT_TRUE(iter_->is_keyframe());
298
299  // Advance to the last sample in the current run, and test its properties
300  for (int i = 0; i < 9; i++) iter_->AdvanceSample();
301  EXPECT_EQ(iter_->track_id(), 1u);
302  EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1);
303  EXPECT_EQ(iter_->sample_size(), 10);
304  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 9, kAudioScale));
305  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
306  EXPECT_TRUE(iter_->is_keyframe());
307
308  // Test end-of-run
309  iter_->AdvanceSample();
310  EXPECT_FALSE(iter_->IsSampleValid());
311
312  // Test last sample of next run
313  iter_->AdvanceRun();
314  EXPECT_TRUE(iter_->is_keyframe());
315  for (int i = 0; i < 9; i++) iter_->AdvanceSample();
316  EXPECT_EQ(iter_->track_id(), 2u);
317  EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1);
318  EXPECT_EQ(iter_->sample_size(), 10);
319  int64 base_dts = kSumAscending1 + moof.tracks[1].decode_time.decode_time;
320  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(base_dts, kVideoScale));
321  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(10, kVideoScale));
322  EXPECT_FALSE(iter_->is_keyframe());
323
324  // Test final run
325  iter_->AdvanceRun();
326  EXPECT_EQ(iter_->track_id(), 1u);
327  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 10, kAudioScale));
328  iter_->AdvanceSample();
329  EXPECT_EQ(moof.tracks[0].runs[1].data_offset +
330            moof.tracks[0].header.default_sample_size,
331            iter_->sample_offset());
332  iter_->AdvanceRun();
333  EXPECT_FALSE(iter_->IsRunValid());
334}
335
336TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
337  moov_.extends.tracks[0].default_sample_duration = 50;
338  moov_.extends.tracks[0].default_sample_size = 3;
339  moov_.extends.tracks[0].default_sample_flags = ToSampleFlags("UN");
340  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
341  MovieFragment moof = CreateFragment();
342  moof.tracks[0].header.has_default_sample_flags = false;
343  moof.tracks[0].header.default_sample_size = 0;
344  moof.tracks[0].header.default_sample_duration = 0;
345  moof.tracks[0].runs[0].sample_sizes.clear();
346  ASSERT_TRUE(iter_->Init(moof));
347  iter_->AdvanceSample();
348  EXPECT_FALSE(iter_->is_keyframe());
349  EXPECT_EQ(iter_->sample_size(), 3);
350  EXPECT_EQ(iter_->sample_offset(), moof.tracks[0].runs[0].data_offset + 3);
351  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(50, kAudioScale));
352  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(50, kAudioScale));
353}
354
355TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
356  // Ensure that keyframes are flagged correctly in the face of BMFF boxes which
357  // explicitly specify the flags for the first sample in a run and rely on
358  // defaults for all subsequent samples
359  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
360  MovieFragment moof = CreateFragment();
361  moof.tracks[1].header.has_default_sample_flags = true;
362  moof.tracks[1].header.default_sample_flags = ToSampleFlags("UN");
363  SetFlagsOnSamples("US", &moof.tracks[1].runs[0]);
364
365  ASSERT_TRUE(iter_->Init(moof));
366  EXPECT_EQ("1 KR KR KR KR KR KR KR KR KR KR", KeyframeAndRAPInfo(iter_.get()));
367
368  iter_->AdvanceRun();
369  EXPECT_EQ("2 KR P P P P P P P P P", KeyframeAndRAPInfo(iter_.get()));
370}
371
372TEST_F(TrackRunIteratorTest, ReorderingTest) {
373  // Test frame reordering and edit list support. The frames have the following
374  // decode timestamps:
375  //
376  //   0ms 40ms   120ms     240ms
377  //   | 0 | 1  - | 2  -  - |
378  //
379  // ...and these composition timestamps, after edit list adjustment:
380  //
381  //   0ms 40ms       160ms  240ms
382  //   | 0 | 2  -  -  | 1 - |
383
384  // Create an edit list with one entry, with an initial start time of 80ms
385  // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
386  // infinite according to 14496-12:2012). This will cause the first 80ms of the
387  // media timeline - which will be empty, due to CTS biasing - to be discarded.
388  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
389  EditListEntry entry;
390  entry.segment_duration = 0;
391  entry.media_time = 2;
392  entry.media_rate_integer = 1;
393  entry.media_rate_fraction = 0;
394  moov_.tracks[1].edit.list.edits.push_back(entry);
395
396  // Add CTS offsets. Without bias, the CTS offsets for the first three frames
397  // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
398  // maximum compatibility, these values are biased up to [2, 5, 0], and the
399  // extra 80ms is removed via the edit list.
400  MovieFragment moof = CreateFragment();
401  std::vector<int32>& cts_offsets =
402    moof.tracks[1].runs[0].sample_composition_time_offsets;
403  cts_offsets.resize(10);
404  cts_offsets[0] = 2;
405  cts_offsets[1] = 5;
406  cts_offsets[2] = 0;
407  moof.tracks[1].decode_time.decode_time = 0;
408
409  ASSERT_TRUE(iter_->Init(moof));
410  iter_->AdvanceRun();
411  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kVideoScale));
412  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale));
413  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale));
414  iter_->AdvanceSample();
415  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1, kVideoScale));
416  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(4, kVideoScale));
417  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale));
418  iter_->AdvanceSample();
419  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(3, kVideoScale));
420  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(1, kVideoScale));
421  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(3, kVideoScale));
422}
423
424TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
425  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
426  MovieFragment moof = CreateFragment();
427  moof.tracks[1].auxiliary_offset.offsets.push_back(50);
428  moof.tracks[1].auxiliary_size.default_sample_info_size = 2;
429  moof.tracks[1].auxiliary_size.sample_count = 2;
430  moof.tracks[1].runs[0].sample_count = 2;
431  ASSERT_TRUE(iter_->Init(moof));
432  iter_->AdvanceRun();
433  EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
434}
435
436TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
437  AddEncryption(&moov_.tracks[1]);
438  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
439
440  MovieFragment moof = CreateFragment();
441  AddAuxInfoHeaders(50, &moof.tracks[1]);
442
443  ASSERT_TRUE(iter_->Init(moof));
444
445  // The run for track 2 will be first, since its aux info offset is the first
446  // element in the file.
447  EXPECT_EQ(iter_->track_id(), 2u);
448  EXPECT_TRUE(iter_->is_encrypted());
449  EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
450  EXPECT_EQ(static_cast<uint32>(iter_->aux_info_size()), arraysize(kAuxInfo));
451  EXPECT_EQ(iter_->aux_info_offset(), 50);
452  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
453  EXPECT_FALSE(iter_->CacheAuxInfo(NULL, 0));
454  EXPECT_FALSE(iter_->CacheAuxInfo(kAuxInfo, 3));
455  EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
456  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
457  EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
458  EXPECT_EQ(iter_->sample_offset(), 200);
459  EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
460  scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
461  ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
462  EXPECT_TRUE(!memcmp(kKeyId, config->key_id().data(),
463                      config->key_id().size()));
464  ASSERT_EQ(arraysize(kIv1), config->iv().size());
465  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
466  EXPECT_TRUE(config->subsamples().empty());
467  iter_->AdvanceSample();
468  config = iter_->GetDecryptConfig();
469  EXPECT_EQ(config->subsamples().size(), 2u);
470  EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
471  EXPECT_EQ(config->subsamples()[1].cypher_bytes, 4u);
472}
473
474TEST_F(TrackRunIteratorTest, CencSampleGroupTest) {
475  MovieFragment moof = CreateFragment();
476
477  const SampleToGroupEntry kSampleToGroupTable[] = {
478      // Associated with the second entry in SampleGroupDescription Box.
479      {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
480      // Associated with the first entry in SampleGroupDescription Box.
481      {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1}};
482  AddCencSampleGroup(
483      &moof.tracks[0], kSampleToGroupTable, arraysize(kSampleToGroupTable));
484
485  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
486  ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
487
488  std::string cenc_sample_group_key_id(
489      kCencSampleGroupKeyId,
490      kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
491  // The first sample is encrypted and the second sample is unencrypted.
492  EXPECT_TRUE(iter_->is_encrypted());
493  EXPECT_EQ(cenc_sample_group_key_id, iter_->GetDecryptConfig()->key_id());
494  iter_->AdvanceSample();
495  EXPECT_FALSE(iter_->is_encrypted());
496}
497
498TEST_F(TrackRunIteratorTest, CencSampleGroupWithTrackEncryptionBoxTest) {
499  // Add TrackEncryption Box.
500  AddEncryption(&moov_.tracks[0]);
501
502  MovieFragment moof = CreateFragment();
503
504  const SampleToGroupEntry kSampleToGroupTable[] = {
505      // Associated with the second entry in SampleGroupDescription Box.
506      {2, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
507      // Associated with the default values specified in TrackEncryption Box.
508      {4, 0},
509      // Associated with the first entry in SampleGroupDescription Box.
510      {3, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1}};
511  AddCencSampleGroup(
512      &moof.tracks[0], kSampleToGroupTable, arraysize(kSampleToGroupTable));
513
514  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
515  ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
516
517  std::string track_encryption_key_id(kKeyId, kKeyId + arraysize(kKeyId));
518  std::string cenc_sample_group_key_id(
519      kCencSampleGroupKeyId,
520      kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
521
522  for (size_t i = 0; i < kSampleToGroupTable[0].sample_count; ++i) {
523    EXPECT_TRUE(iter_->is_encrypted());
524    EXPECT_EQ(cenc_sample_group_key_id, iter_->GetDecryptConfig()->key_id());
525    iter_->AdvanceSample();
526  }
527
528  for (size_t i = 0; i < kSampleToGroupTable[1].sample_count; ++i) {
529    EXPECT_TRUE(iter_->is_encrypted());
530    EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
531    iter_->AdvanceSample();
532  }
533
534  for (size_t i = 0; i < kSampleToGroupTable[2].sample_count; ++i) {
535    EXPECT_FALSE(iter_->is_encrypted());
536    iter_->AdvanceSample();
537  }
538
539  // The remaining samples should be associated with the default values
540  // specified in TrackEncryption Box.
541  EXPECT_TRUE(iter_->is_encrypted());
542  EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
543}
544
545// It is legal for aux info blocks to be shared among multiple formats.
546TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
547  AddEncryption(&moov_.tracks[0]);
548  AddEncryption(&moov_.tracks[1]);
549  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
550
551  MovieFragment moof = CreateFragment();
552  moof.tracks[0].runs.resize(1);
553  AddAuxInfoHeaders(50, &moof.tracks[0]);
554  AddAuxInfoHeaders(50, &moof.tracks[1]);
555  moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
556
557  ASSERT_TRUE(iter_->Init(moof));
558  EXPECT_EQ(iter_->track_id(), 1u);
559  EXPECT_EQ(iter_->aux_info_offset(), 50);
560  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
561  scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
562  ASSERT_EQ(arraysize(kIv1), config->iv().size());
563  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
564  iter_->AdvanceSample();
565  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
566  iter_->AdvanceRun();
567  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
568  EXPECT_EQ(iter_->aux_info_offset(), 50);
569  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
570  EXPECT_EQ(iter_->GetMaxClearOffset(), 200);
571  ASSERT_EQ(arraysize(kIv1), config->iv().size());
572  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
573  iter_->AdvanceSample();
574  EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
575}
576
577// Sensible files are expected to place auxiliary information for a run
578// immediately before the main data for that run. Alternative schemes are
579// possible, however, including the somewhat reasonable behavior of placing all
580// aux info at the head of the 'mdat' box together, and the completely
581// unreasonable behavior demonstrated here:
582//  byte 50: track 2, run 1 aux info
583//  byte 100: track 1, run 1 data
584//  byte 200: track 2, run 1 data
585//  byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
586//  byte 10000: track 1, run 2 data
587//  byte 20000: track 1, run 1 aux info
588TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
589  AddEncryption(&moov_.tracks[0]);
590  AddEncryption(&moov_.tracks[1]);
591  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
592
593  MovieFragment moof = CreateFragment();
594  AddAuxInfoHeaders(20000, &moof.tracks[0]);
595  moof.tracks[0].auxiliary_offset.offsets.push_back(201);
596  moof.tracks[0].auxiliary_size.sample_count += 2;
597  moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
598  moof.tracks[0].runs[1].sample_count = 2;
599  AddAuxInfoHeaders(50, &moof.tracks[1]);
600  moof.tracks[1].runs[0].sample_sizes[0] = 5;
601
602  ASSERT_TRUE(iter_->Init(moof));
603  EXPECT_EQ(iter_->track_id(), 2u);
604  EXPECT_EQ(iter_->aux_info_offset(), 50);
605  EXPECT_EQ(iter_->sample_offset(), 200);
606  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
607  EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
608  iter_->AdvanceRun();
609  EXPECT_EQ(iter_->track_id(), 1u);
610  EXPECT_EQ(iter_->aux_info_offset(), 20000);
611  EXPECT_EQ(iter_->sample_offset(), 100);
612  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
613  EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
614  iter_->AdvanceSample();
615  EXPECT_EQ(iter_->GetMaxClearOffset(), 101);
616  iter_->AdvanceRun();
617  EXPECT_EQ(iter_->track_id(), 1u);
618  EXPECT_EQ(iter_->aux_info_offset(), 201);
619  EXPECT_EQ(iter_->sample_offset(), 10000);
620  EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
621  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
622  EXPECT_EQ(iter_->GetMaxClearOffset(), 10000);
623}
624
625TEST_F(TrackRunIteratorTest, MissingAndEmptyStss) {
626  MovieFragment moof = CreateFragment();
627
628  // Setup track 0 to not have an stss box, which means that all samples should
629  // be marked as random access points unless the kSampleIsNonSyncSample flag is
630  // set in the sample flags.
631  moov_.tracks[0].media.information.sample_table.sync_sample.is_present = false;
632  moov_.tracks[0].media.information.sample_table.sync_sample.entries.resize(0);
633  moof.tracks[0].runs.resize(1);
634  moof.tracks[0].runs[0].sample_count = 6;
635  moof.tracks[0].runs[0].data_offset = 100;
636  SetFlagsOnSamples("US UN OS ON NS NN", &moof.tracks[0].runs[0]);
637
638  // Setup track 1 to have an stss box with no entries, which normally means
639  // that none of the samples should be random access points. If the
640  // kSampleIsNonSyncSample flag is NOT set though, the sample should be
641  // considered a random access point.
642  moov_.tracks[1].media.information.sample_table.sync_sample.is_present = true;
643  moov_.tracks[1].media.information.sample_table.sync_sample.entries.resize(0);
644  moof.tracks[1].runs.resize(1);
645  moof.tracks[1].runs[0].sample_count = 6;
646  moof.tracks[1].runs[0].data_offset = 200;
647  SetFlagsOnSamples("US UN OS ON NS NN", &moof.tracks[1].runs[0]);
648
649  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
650
651  ASSERT_TRUE(iter_->Init(moof));
652  EXPECT_TRUE(iter_->IsRunValid());
653
654  // Verify that all samples except for the ones that have the
655  // kSampleIsNonSyncSample flag set are marked as random access points.
656  EXPECT_EQ("1 KR P PR P KR K", KeyframeAndRAPInfo(iter_.get()));
657
658  iter_->AdvanceRun();
659
660  // Verify that nothing is marked as a random access point.
661  EXPECT_EQ("2 KR P PR P KR K", KeyframeAndRAPInfo(iter_.get()));
662}
663
664
665}  // namespace mp4
666}  // namespace media
667