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