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