Index: media/filters/pipeline_integration_test.cc |
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc |
index 2d4c4d9ebe74830fad4912d6b1c58e53b1dd3c8f..dba6272865135cab7e708a23693d70360d5c56c6 100644 |
--- a/media/filters/pipeline_integration_test.cc |
+++ b/media/filters/pipeline_integration_test.cc |
@@ -248,6 +248,76 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase { |
uint32 current_session_id_; |
}; |
+class RotatingKeyProvidingApp : public KeyProvidingApp { |
+ public: |
+ RotatingKeyProvidingApp() : num_distint_need_key_calls_(0) {} |
+ virtual ~RotatingKeyProvidingApp() { |
+ // Expect that NeedKey is fired multiple times with different |init_data|. |
+ EXPECT_GT(num_distint_need_key_calls_, 1u); |
+ } |
+ |
+ virtual void NeedKey(const std::string& type, |
+ const std::vector<uint8>& init_data, |
+ AesDecryptor* decryptor) OVERRIDE { |
+ // Skip the request if the |init_data| has been seen. |
+ if (init_data == prev_init_data_) |
+ return; |
+ prev_init_data_ = init_data; |
+ ++num_distint_need_key_calls_; |
+ |
+ EXPECT_TRUE(decryptor->CreateSession(current_session_id_ + 1, |
+ type, |
+ vector_as_array(&init_data), |
+ init_data.size())); |
+ |
+ std::vector<uint8> key_id; |
+ std::vector<uint8> key; |
+ EXPECT_TRUE(GetKeyAndKeyId(init_data, &key, &key_id)); |
+ |
+ // Convert key into a JSON structure and then add it. |
+ std::string jwk = GenerateJWKSet(vector_as_array(&key), |
+ key.size(), |
+ vector_as_array(&key_id), |
+ key_id.size()); |
+ decryptor->UpdateSession(current_session_id_, |
+ reinterpret_cast<const uint8*>(jwk.data()), |
+ jwk.size()); |
+ } |
+ |
+ private: |
+ bool GetKeyAndKeyId(std::vector<uint8> init_data, |
+ std::vector<uint8>* key, |
+ std::vector<uint8>* key_id) { |
+ // For WebM, init_data is key_id; for ISO CENC, init_data should contain |
+ // the key_id. We assume key_id is in the end of init_data here (that is |
+ // only a reasonable assumption for WebM and clear key ISO CENC). |
+ DCHECK_GE(init_data.size(), arraysize(kKeyId)); |
+ std::vector<uint8> key_id_from_init_data( |
+ init_data.end() - arraysize(kKeyId), init_data.end()); |
+ |
+ key->assign(kSecretKey, kSecretKey + arraysize(kSecretKey)); |
+ key_id->assign(kKeyId, kKeyId + arraysize(kKeyId)); |
+ |
+ // The Key and KeyId for this testing key provider are created by left |
+ // rotating kSecretKey and kKeyId. Note that this implementation is only |
+ // intended for testing purpose. The actual key rotation algorithm can be |
+ // much more complicated. |
+ // Find out the rotating position from |key_id_from_init_data| and apply on |
+ // |key|. |
+ for (size_t pos = 0; pos < arraysize(kKeyId); ++pos) { |
+ std::rotate(key_id->begin(), key_id->begin() + pos, key_id->end()); |
+ if (*key_id == key_id_from_init_data) { |
+ std::rotate(key->begin(), key->begin() + pos, key->end()); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ std::vector<uint8> prev_init_data_; |
+ uint32 num_distint_need_key_calls_; |
+}; |
+ |
// Ignores needkey and does not perform a license request |
class NoResponseApp : public FakeEncryptedMedia::AppBase { |
public: |
@@ -993,6 +1063,33 @@ TEST_P(PipelineIntegrationTest, |
Stop(); |
} |
+TEST_P(PipelineIntegrationTest, |
+ MediaSource_ConfigChange_Encrypted_MP4_CENC_KeyRotation_VideoOnly) { |
+ MockMediaSource source("bear-640x360-v_frag-cenc-key_rotation.mp4", |
+ kMP4Video, kAppendWholeFile, GetParam()); |
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp()); |
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
+ |
+ scoped_refptr<DecoderBuffer> second_file = |
+ ReadTestDataFile("bear-1280x720-v_frag-cenc-key_rotation.mp4"); |
+ |
+ source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), |
+ second_file->data(), second_file->data_size()); |
+ |
+ source.EndOfStream(); |
+ |
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); |
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds()); |
+ EXPECT_EQ(kAppendTimeMs + k1280IsoFileDurationMs, |
+ pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds()); |
+ |
+ Play(); |
+ |
+ EXPECT_TRUE(WaitUntilOnEnded()); |
+ source.Abort(); |
+ Stop(); |
+} |
+ |
// Config changes from clear to encrypted are not currently supported. |
// TODO(ddorwin): Figure out why this CHECKs in AppendAtTime(). |
TEST_P(PipelineIntegrationTest, |
@@ -1203,6 +1300,37 @@ TEST_P(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_AVC3) { |
Stop(); |
} |
+TEST_P(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_KeyRotation_Video) { |
+ MockMediaSource source("bear-1280x720-v_frag-cenc-key_rotation.mp4", |
+ kMP4Video, kAppendWholeFile, GetParam()); |
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp()); |
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
+ |
+ source.EndOfStream(); |
+ ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
+ |
+ Play(); |
+ |
+ ASSERT_TRUE(WaitUntilOnEnded()); |
+ source.Abort(); |
+ Stop(); |
+} |
+ |
+TEST_P(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_KeyRotation_Audio) { |
+ MockMediaSource source("bear-1280x720-a_frag-cenc-key_rotation.mp4", |
+ kMP4Audio, kAppendWholeFile, GetParam()); |
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp()); |
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media); |
+ |
+ source.EndOfStream(); |
+ ASSERT_EQ(PIPELINE_OK, pipeline_status_); |
+ |
+ Play(); |
+ |
+ ASSERT_TRUE(WaitUntilOnEnded()); |
+ source.Abort(); |
+ Stop(); |
+} |
#endif |
// TODO(acolwell): Fix flakiness http://crbug.com/117921 |