| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/filters/pipeline_integration_test_base.h" | 5 #include "media/filters/pipeline_integration_test_base.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/memory/scoped_ptr.h" |
| 8 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 9 #include "build/build_config.h" | 10 #include "build/build_config.h" |
| 10 #include "media/base/decoder_buffer.h" | 11 #include "media/base/decoder_buffer.h" |
| 11 #include "media/base/test_data_util.h" | 12 #include "media/base/test_data_util.h" |
| 12 #include "media/crypto/aes_decryptor.h" | 13 #include "media/crypto/aes_decryptor.h" |
| 13 | 14 |
| 14 using testing::AtMost; | 15 using testing::AtMost; |
| 15 | 16 |
| 16 namespace media { | 17 namespace media { |
| 17 | 18 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 42 static const int kAppendTimeSec = 1; | 43 static const int kAppendTimeSec = 1; |
| 43 static const int kAppendTimeMs = kAppendTimeSec * 1000; | 44 static const int kAppendTimeMs = kAppendTimeSec * 1000; |
| 44 static const int k320WebMFileDurationMs = 2737; | 45 static const int k320WebMFileDurationMs = 2737; |
| 45 static const int k640WebMFileDurationMs = 2763; | 46 static const int k640WebMFileDurationMs = 2763; |
| 46 static const int k1280IsoFileDurationMs = 2736; | 47 static const int k1280IsoFileDurationMs = 2736; |
| 47 | 48 |
| 48 // Note: Tests using this class only exercise the DecryptingDemuxerStream path. | 49 // Note: Tests using this class only exercise the DecryptingDemuxerStream path. |
| 49 // They do not exercise the Decrypting{Audio|Video}Decoder path. | 50 // They do not exercise the Decrypting{Audio|Video}Decoder path. |
| 50 class FakeEncryptedMedia { | 51 class FakeEncryptedMedia { |
| 51 public: | 52 public: |
| 52 FakeEncryptedMedia() | 53 // Defines the behavior of the "app" that responds to EME events. |
| 54 class AppBase { |
| 55 public: |
| 56 virtual ~AppBase() {} |
| 57 |
| 58 virtual void KeyAdded(const std::string& key_system, |
| 59 const std::string& session_id) = 0; |
| 60 |
| 61 // Errors are not expected unless overridden. |
| 62 virtual void KeyError(const std::string& key_system, |
| 63 const std::string& session_id, |
| 64 AesDecryptor::KeyError error_code, |
| 65 int system_code) { |
| 66 FAIL() << "Unexpected Key Error"; |
| 67 } |
| 68 |
| 69 virtual void KeyMessage(const std::string& key_system, |
| 70 const std::string& session_id, |
| 71 const std::string& message, |
| 72 const std::string& default_url) = 0; |
| 73 |
| 74 virtual void NeedKey(const std::string& key_system, |
| 75 const std::string& session_id, |
| 76 const std::string& type, |
| 77 scoped_array<uint8> init_data, int init_data_length, |
| 78 AesDecryptor* decryptor) = 0; |
| 79 }; |
| 80 |
| 81 FakeEncryptedMedia(AppBase* app) |
| 53 : decryptor_(base::Bind(&FakeEncryptedMedia::KeyAdded, | 82 : decryptor_(base::Bind(&FakeEncryptedMedia::KeyAdded, |
| 54 base::Unretained(this)), | 83 base::Unretained(this)), |
| 55 base::Bind(&FakeEncryptedMedia::KeyError, | 84 base::Bind(&FakeEncryptedMedia::KeyError, |
| 56 base::Unretained(this)), | 85 base::Unretained(this)), |
| 57 base::Bind(&FakeEncryptedMedia::KeyMessage, | 86 base::Bind(&FakeEncryptedMedia::KeyMessage, |
| 58 base::Unretained(this)), | 87 base::Unretained(this)), |
| 59 base::Bind(&FakeEncryptedMedia::NeedKey, | 88 base::Bind(&FakeEncryptedMedia::NeedKey, |
| 60 base::Unretained(this))) { | 89 base::Unretained(this))), |
| 90 app_(app) { |
| 61 } | 91 } |
| 62 | 92 |
| 63 AesDecryptor* decryptor() { | 93 AesDecryptor* decryptor() { |
| 64 return &decryptor_; | 94 return &decryptor_; |
| 65 } | 95 } |
| 66 | 96 |
| 67 // Callbacks for firing key events. | 97 // Callbacks for firing key events. Delegate to |app_|. |
| 68 void KeyAdded(const std::string& key_system, const std::string& session_id) { | 98 void KeyAdded(const std::string& key_system, const std::string& session_id) { |
| 69 EXPECT_EQ(kClearKeySystem, key_system); | 99 app_->KeyAdded(key_system, session_id); |
| 70 EXPECT_FALSE(session_id.empty()); | |
| 71 } | 100 } |
| 72 | 101 |
| 73 void KeyError(const std::string& key_system, | 102 void KeyError(const std::string& key_system, |
| 74 const std::string& session_id, | 103 const std::string& session_id, |
| 75 AesDecryptor::KeyError error_code, | 104 AesDecryptor::KeyError error_code, |
| 76 int system_code) { | 105 int system_code) { |
| 77 FAIL() << "Unexpected Key Error"; | 106 app_->KeyError(key_system, session_id, error_code, system_code); |
| 78 } | 107 } |
| 79 | 108 |
| 80 void KeyMessage(const std::string& key_system, | 109 void KeyMessage(const std::string& key_system, |
| 81 const std::string& session_id, | 110 const std::string& session_id, |
| 82 const std::string& message, | 111 const std::string& message, |
| 83 const std::string& default_url) { | 112 const std::string& default_url) { |
| 113 app_->KeyMessage(key_system, session_id, message, default_url); |
| 114 } |
| 115 |
| 116 void NeedKey(const std::string& key_system, |
| 117 const std::string& session_id, |
| 118 const std::string& type, |
| 119 scoped_array<uint8> init_data, int init_data_length) { |
| 120 app_->NeedKey(key_system, session_id, type, |
| 121 init_data.Pass(), init_data_length, &decryptor_); |
| 122 } |
| 123 |
| 124 private: |
| 125 AesDecryptor decryptor_; |
| 126 scoped_ptr<AppBase> app_; |
| 127 }; |
| 128 |
| 129 // Provides |kSecretKey| in response to needkey. |
| 130 class KeyProvidingApp : public FakeEncryptedMedia::AppBase { |
| 131 public: |
| 132 virtual void KeyAdded(const std::string& key_system, |
| 133 const std::string& session_id) OVERRIDE { |
| 134 EXPECT_EQ(kClearKeySystem, key_system); |
| 135 EXPECT_FALSE(session_id.empty()); |
| 136 } |
| 137 |
| 138 virtual void KeyMessage(const std::string& key_system, |
| 139 const std::string& session_id, |
| 140 const std::string& message, |
| 141 const std::string& default_url) OVERRIDE { |
| 84 EXPECT_EQ(kClearKeySystem, key_system); | 142 EXPECT_EQ(kClearKeySystem, key_system); |
| 85 EXPECT_FALSE(session_id.empty()); | 143 EXPECT_FALSE(session_id.empty()); |
| 86 EXPECT_FALSE(message.empty()); | 144 EXPECT_FALSE(message.empty()); |
| 87 | 145 |
| 88 current_key_system_ = key_system; | 146 current_key_system_ = key_system; |
| 89 current_session_id_ = session_id; | 147 current_session_id_ = session_id; |
| 90 } | 148 } |
| 91 | 149 |
| 92 void NeedKey(const std::string& key_system, | 150 virtual void NeedKey(const std::string& key_system, |
| 93 const std::string& session_id, | 151 const std::string& session_id, |
| 94 const std::string& type, | 152 const std::string& type, |
| 95 scoped_array<uint8> init_data, int init_data_length) { | 153 scoped_array<uint8> init_data, int init_data_length, |
| 154 AesDecryptor* decryptor) OVERRIDE { |
| 96 current_key_system_ = key_system; | 155 current_key_system_ = key_system; |
| 97 current_session_id_ = session_id; | 156 current_session_id_ = session_id; |
| 98 | 157 |
| 99 // When NeedKey is called from the demuxer, the |key_system| will be empty. | 158 // When NeedKey is called from the demuxer, the |key_system| will be empty. |
| 100 // In this case, we need to call GenerateKeyRequest() to initialize a | 159 // In this case, we need to call GenerateKeyRequest() to initialize a |
| 101 // session (which will call KeyMessage). | 160 // session (which will call KeyMessage). |
| 102 if (current_key_system_.empty()) { | 161 if (current_key_system_.empty()) { |
| 103 EXPECT_TRUE(current_session_id_.empty()); | 162 EXPECT_TRUE(current_session_id_.empty()); |
| 104 EXPECT_TRUE(decryptor_.GenerateKeyRequest( | 163 EXPECT_TRUE(decryptor->GenerateKeyRequest( |
| 105 kClearKeySystem, type, kInitData, arraysize(kInitData))); | 164 kClearKeySystem, type, kInitData, arraysize(kInitData))); |
| 106 } | 165 } |
| 107 | 166 |
| 108 EXPECT_FALSE(current_key_system_.empty()); | 167 EXPECT_FALSE(current_key_system_.empty()); |
| 109 EXPECT_FALSE(current_session_id_.empty()); | 168 EXPECT_FALSE(current_session_id_.empty()); |
| 110 decryptor_.AddKey(current_key_system_, kSecretKey, arraysize(kSecretKey), | 169 decryptor->AddKey(current_key_system_, kSecretKey, arraysize(kSecretKey), |
| 111 init_data.get(), init_data_length, current_session_id_); | 170 init_data.get(), init_data_length, current_session_id_); |
| 112 } | 171 } |
| 113 | 172 |
| 114 private: | |
| 115 AesDecryptor decryptor_; | |
| 116 std::string current_key_system_; | 173 std::string current_key_system_; |
| 117 std::string current_session_id_; | 174 std::string current_session_id_; |
| 118 }; | 175 }; |
| 119 | 176 |
| 177 // Ignores needkey and does not perform a license request |
| 178 class NoResponseApp : public FakeEncryptedMedia::AppBase { |
| 179 public: |
| 180 virtual void KeyAdded(const std::string& key_system, |
| 181 const std::string& session_id) OVERRIDE { |
| 182 EXPECT_EQ(kClearKeySystem, key_system); |
| 183 EXPECT_FALSE(session_id.empty()); |
| 184 FAIL() << "Unexpected KeyAdded"; |
| 185 } |
| 186 |
| 187 virtual void KeyMessage(const std::string& key_system, |
| 188 const std::string& session_id, |
| 189 const std::string& message, |
| 190 const std::string& default_url) OVERRIDE { |
| 191 EXPECT_EQ(kClearKeySystem, key_system); |
| 192 EXPECT_FALSE(session_id.empty()); |
| 193 EXPECT_FALSE(message.empty()); |
| 194 FAIL() << "Unexpected KeyMessage"; |
| 195 } |
| 196 |
| 197 virtual void NeedKey(const std::string& key_system, |
| 198 const std::string& session_id, |
| 199 const std::string& type, |
| 200 scoped_array<uint8> init_data, int init_data_length, |
| 201 AesDecryptor* decryptor) OVERRIDE { |
| 202 } |
| 203 }; |
| 204 |
| 120 // Helper class that emulates calls made on the ChunkDemuxer by the | 205 // Helper class that emulates calls made on the ChunkDemuxer by the |
| 121 // Media Source API. | 206 // Media Source API. |
| 122 class MockMediaSource { | 207 class MockMediaSource { |
| 123 public: | 208 public: |
| 124 MockMediaSource(const std::string& filename, const std::string& mimetype, | 209 MockMediaSource(const std::string& filename, const std::string& mimetype, |
| 125 int initial_append_size) | 210 int initial_append_size) |
| 126 : file_path_(GetTestDataFilePath(filename)), | 211 : file_path_(GetTestDataFilePath(filename)), |
| 127 current_position_(0), | 212 current_position_(0), |
| 128 initial_append_size_(initial_append_size), | 213 initial_append_size_(initial_append_size), |
| 129 mimetype_(mimetype) { | 214 mimetype_(mimetype) { |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 358 Play(); | 443 Play(); |
| 359 | 444 |
| 360 EXPECT_TRUE(WaitUntilOnEnded()); | 445 EXPECT_TRUE(WaitUntilOnEnded()); |
| 361 source.Abort(); | 446 source.Abort(); |
| 362 Stop(); | 447 Stop(); |
| 363 } | 448 } |
| 364 | 449 |
| 365 TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_Encrypted_WebM) { | 450 TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_Encrypted_WebM) { |
| 366 MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM, | 451 MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM, |
| 367 kAppendWholeFile); | 452 kAppendWholeFile); |
| 368 FakeEncryptedMedia encrypted_media; | 453 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
| 369 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | 454 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 370 | 455 |
| 371 scoped_refptr<DecoderBuffer> second_file = | 456 scoped_refptr<DecoderBuffer> second_file = |
| 372 ReadTestDataFile("bear-640x360-av_enc-av.webm"); | 457 ReadTestDataFile("bear-640x360-av_enc-av.webm"); |
| 373 | 458 |
| 374 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), | 459 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), |
| 375 second_file->GetData(), second_file->GetDataSize()); | 460 second_file->GetData(), second_file->GetDataSize()); |
| 376 | 461 |
| 377 source.EndOfStream(); | 462 source.EndOfStream(); |
| 378 | 463 |
| 379 EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); | 464 EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); |
| 380 EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds()); | 465 EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds()); |
| 381 EXPECT_EQ(kAppendTimeMs + k640WebMFileDurationMs, | 466 EXPECT_EQ(kAppendTimeMs + k640WebMFileDurationMs, |
| 382 pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds()); | 467 pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds()); |
| 383 | 468 |
| 384 Play(); | 469 Play(); |
| 385 | 470 |
| 386 EXPECT_TRUE(WaitUntilOnEnded()); | 471 EXPECT_TRUE(WaitUntilOnEnded()); |
| 387 source.Abort(); | 472 source.Abort(); |
| 388 Stop(); | 473 Stop(); |
| 389 } | 474 } |
| 390 | 475 |
| 391 // Config changes from encrypted to clear are not currently supported. | 476 // Config changes from encrypted to clear are not currently supported. |
| 392 TEST_F(PipelineIntegrationTest, | 477 TEST_F(PipelineIntegrationTest, |
| 393 MediaSource_ConfigChange_ClearThenEncrypted_WebM) { | 478 MediaSource_ConfigChange_ClearThenEncrypted_WebM) { |
| 394 MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM, | 479 MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM, |
| 395 kAppendWholeFile); | 480 kAppendWholeFile); |
| 396 FakeEncryptedMedia encrypted_media; | 481 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
| 397 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | 482 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 398 | 483 |
| 399 scoped_refptr<DecoderBuffer> second_file = | 484 scoped_refptr<DecoderBuffer> second_file = |
| 400 ReadTestDataFile("bear-640x360-av_enc-av.webm"); | 485 ReadTestDataFile("bear-640x360-av_enc-av.webm"); |
| 401 | 486 |
| 402 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), | 487 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), |
| 403 second_file->GetData(), second_file->GetDataSize()); | 488 second_file->GetData(), second_file->GetDataSize()); |
| 404 | 489 |
| 405 source.EndOfStream(); | 490 source.EndOfStream(); |
| 406 | 491 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 417 | 502 |
| 418 EXPECT_EQ(PIPELINE_ERROR_DECODE, WaitUntilEndedOrError()); | 503 EXPECT_EQ(PIPELINE_ERROR_DECODE, WaitUntilEndedOrError()); |
| 419 source.Abort(); | 504 source.Abort(); |
| 420 } | 505 } |
| 421 | 506 |
| 422 // Config changes from clear to encrypted are not currently supported. | 507 // Config changes from clear to encrypted are not currently supported. |
| 423 TEST_F(PipelineIntegrationTest, | 508 TEST_F(PipelineIntegrationTest, |
| 424 MediaSource_ConfigChange_EncryptedThenClear_WebM) { | 509 MediaSource_ConfigChange_EncryptedThenClear_WebM) { |
| 425 MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM, | 510 MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM, |
| 426 kAppendWholeFile); | 511 kAppendWholeFile); |
| 427 FakeEncryptedMedia encrypted_media; | 512 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
| 428 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | 513 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 429 | 514 |
| 430 scoped_refptr<DecoderBuffer> second_file = | 515 scoped_refptr<DecoderBuffer> second_file = |
| 431 ReadTestDataFile("bear-640x360.webm"); | 516 ReadTestDataFile("bear-640x360.webm"); |
| 432 | 517 |
| 433 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), | 518 source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), |
| 434 second_file->GetData(), second_file->GetDataSize()); | 519 second_file->GetData(), second_file->GetDataSize()); |
| 435 | 520 |
| 436 source.EndOfStream(); | 521 source.EndOfStream(); |
| 437 | 522 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 } | 558 } |
| 474 #endif | 559 #endif |
| 475 | 560 |
| 476 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { | 561 TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) { |
| 477 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"), | 562 ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-16x9-aspect.webm"), |
| 478 PIPELINE_OK)); | 563 PIPELINE_OK)); |
| 479 Play(); | 564 Play(); |
| 480 ASSERT_TRUE(WaitUntilOnEnded()); | 565 ASSERT_TRUE(WaitUntilOnEnded()); |
| 481 } | 566 } |
| 482 | 567 |
| 483 TEST_F(PipelineIntegrationTest, EncryptedPlayback) { | 568 TEST_F(PipelineIntegrationTest, EncryptedPlayback_WebM) { |
| 484 MockMediaSource source("bear-320x240-av_enc-av.webm", kWebM, 219816); | 569 MockMediaSource source("bear-320x240-av_enc-av.webm", kWebM, 219816); |
| 485 FakeEncryptedMedia encrypted_media; | 570 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
| 486 StartPipelineWithEncryptedMedia(&source, &encrypted_media); | 571 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 487 | 572 |
| 488 source.EndOfStream(); | 573 source.EndOfStream(); |
| 574 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 575 |
| 576 Play(); |
| 577 |
| 578 ASSERT_TRUE(WaitUntilOnEnded()); |
| 579 source.Abort(); |
| 580 Stop(); |
| 581 } |
| 582 |
| 583 TEST_F(PipelineIntegrationTest, EncryptedPlayback_ClearStart_WebM) { |
| 584 MockMediaSource source("bear-320x240-av_enc-av_clear-1s.webm", |
| 585 kWebM, kAppendWholeFile); |
| 586 FakeEncryptedMedia encrypted_media(new KeyProvidingApp()); |
| 587 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 588 |
| 589 source.EndOfStream(); |
| 590 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 591 |
| 592 Play(); |
| 593 |
| 594 ASSERT_TRUE(WaitUntilOnEnded()); |
| 595 source.Abort(); |
| 596 Stop(); |
| 597 } |
| 598 |
| 599 TEST_F(PipelineIntegrationTest, EncryptedPlayback_NoEncryptedFrames_WebM) { |
| 600 MockMediaSource source("bear-320x240-av_enc-av_clear-all.webm", |
| 601 kWebM, kAppendWholeFile); |
| 602 FakeEncryptedMedia encrypted_media(new NoResponseApp()); |
| 603 StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
| 604 |
| 605 source.EndOfStream(); |
| 489 ASSERT_EQ(PIPELINE_OK, pipeline_status_); | 606 ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
| 490 | 607 |
| 491 Play(); | 608 Play(); |
| 492 | 609 |
| 493 ASSERT_TRUE(WaitUntilOnEnded()); | 610 ASSERT_TRUE(WaitUntilOnEnded()); |
| 494 source.Abort(); | 611 source.Abort(); |
| 495 Stop(); | 612 Stop(); |
| 496 } | 613 } |
| 497 | 614 |
| 498 // TODO(acolwell): Fix flakiness http://crbug.com/117921 | 615 // TODO(acolwell): Fix flakiness http://crbug.com/117921 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 551 // Verify video decoder & renderer can handle aborted demuxer reads. | 668 // Verify video decoder & renderer can handle aborted demuxer reads. |
| 552 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 669 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
| 553 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM, | 670 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM, |
| 554 32768, | 671 32768, |
| 555 base::TimeDelta::FromMilliseconds(200), | 672 base::TimeDelta::FromMilliseconds(200), |
| 556 base::TimeDelta::FromMilliseconds(1668), | 673 base::TimeDelta::FromMilliseconds(1668), |
| 557 0x1C896, 65536)); | 674 0x1C896, 65536)); |
| 558 } | 675 } |
| 559 | 676 |
| 560 } // namespace media | 677 } // namespace media |
| OLD | NEW |