Index: media/test/pipeline_integration_test.cc |
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc |
index 53e727037c3f9438a2366f5b7ec0233841ec9735..e1d7f55d7206fb6e10fd1f7c0ba0f237bc55b406 100644 |
--- a/media/test/pipeline_integration_test.cc |
+++ b/media/test/pipeline_integration_test.cc |
@@ -19,9 +19,7 @@ |
#include "base/threading/thread_task_runner_handle.h" |
#include "build/build_config.h" |
#include "media/base/cdm_callback_promise.h" |
-#include "media/base/cdm_context.h" |
#include "media/base/cdm_key_information.h" |
-#include "media/base/content_decryption_module.h" |
#include "media/base/decoder_buffer.h" |
#include "media/base/media.h" |
#include "media/base/media_switches.h" |
@@ -30,9 +28,10 @@ |
#include "media/base/timestamp_constants.h" |
#include "media/cdm/aes_decryptor.h" |
#include "media/cdm/json_web_key.h" |
-#include "media/filters/chunk_demuxer.h" |
#include "media/media_features.h" |
#include "media/renderers/renderer_impl.h" |
+#include "media/test/fake_encrypted_media.h" |
+#include "media/test/mock_media_source.h" |
#include "media/test/pipeline_integration_test_base.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "url/gurl.h" |
@@ -94,8 +93,6 @@ using testing::SaveArg; |
namespace media { |
-const char kSourceId[] = "SourceId"; |
- |
const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; |
const char kWebMVP9[] = "video/webm; codecs=\"vp9\""; |
const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; |
@@ -114,8 +111,6 @@ const char kMP3[] = "audio/mpeg"; |
const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\""; |
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) |
-const size_t kAppendWholeFile = std::numeric_limits<size_t>::max(); |
- |
// Constants for the Media Source config change tests. |
const int kAppendTimeSec = 1; |
const int kAppendTimeMs = kAppendTimeSec * 1000; |
@@ -187,86 +182,6 @@ static base::Time kLiveTimelineOffset() { |
return timeline_offset; |
} |
-// Note: Tests using this class only exercise the DecryptingDemuxerStream path. |
-// They do not exercise the Decrypting{Audio|Video}Decoder path. |
-class FakeEncryptedMedia { |
- public: |
- // Defines the behavior of the "app" that responds to EME events. |
- class AppBase { |
- public: |
- virtual ~AppBase() {} |
- |
- virtual void OnSessionMessage( |
- const std::string& session_id, |
- ContentDecryptionModule::MessageType message_type, |
- const std::vector<uint8_t>& message, |
- AesDecryptor* decryptor) = 0; |
- |
- virtual void OnSessionClosed(const std::string& session_id) = 0; |
- |
- virtual void OnSessionKeysChange(const std::string& session_id, |
- bool has_additional_usable_key, |
- CdmKeysInfo keys_info) = 0; |
- |
- virtual void OnEncryptedMediaInitData(EmeInitDataType init_data_type, |
- const std::vector<uint8_t>& init_data, |
- AesDecryptor* decryptor) = 0; |
- }; |
- |
- FakeEncryptedMedia(AppBase* app) |
- : decryptor_(new AesDecryptor( |
- GURL::EmptyGURL(), |
- base::Bind(&FakeEncryptedMedia::OnSessionMessage, |
- base::Unretained(this)), |
- base::Bind(&FakeEncryptedMedia::OnSessionClosed, |
- base::Unretained(this)), |
- base::Bind(&FakeEncryptedMedia::OnSessionKeysChange, |
- base::Unretained(this)))), |
- cdm_context_(decryptor_.get()), |
- app_(app) {} |
- |
- CdmContext* GetCdmContext() { return &cdm_context_; } |
- |
- // Callbacks for firing session events. Delegate to |app_|. |
- void OnSessionMessage(const std::string& session_id, |
- ContentDecryptionModule::MessageType message_type, |
- const std::vector<uint8_t>& message) { |
- app_->OnSessionMessage(session_id, message_type, message, decryptor_.get()); |
- } |
- |
- void OnSessionClosed(const std::string& session_id) { |
- app_->OnSessionClosed(session_id); |
- } |
- |
- void OnSessionKeysChange(const std::string& session_id, |
- bool has_additional_usable_key, |
- CdmKeysInfo keys_info) { |
- app_->OnSessionKeysChange(session_id, has_additional_usable_key, |
- std::move(keys_info)); |
- } |
- |
- void OnEncryptedMediaInitData(EmeInitDataType init_data_type, |
- const std::vector<uint8_t>& init_data) { |
- app_->OnEncryptedMediaInitData(init_data_type, init_data, decryptor_.get()); |
- } |
- |
- private: |
- class TestCdmContext : public CdmContext { |
- public: |
- TestCdmContext(Decryptor* decryptor) : decryptor_(decryptor) {} |
- |
- Decryptor* GetDecryptor() final { return decryptor_; } |
- int GetCdmId() const final { return kInvalidCdmId; } |
- |
- private: |
- Decryptor* decryptor_; |
- }; |
- |
- scoped_refptr<AesDecryptor> decryptor_; |
- TestCdmContext cdm_context_; |
- std::unique_ptr<AppBase> app_; |
-}; |
- |
enum PromiseResult { RESOLVED, REJECTED }; |
// Provides the test key in response to the encrypted event. |
@@ -442,210 +357,6 @@ class NoResponseApp : public FakeEncryptedMedia::AppBase { |
AesDecryptor* decryptor) override {} |
}; |
-// Helper class that emulates calls made on the ChunkDemuxer by the |
-// Media Source API. |
-class MockMediaSource { |
- public: |
- MockMediaSource(const std::string& filename, |
- const std::string& mimetype, |
- size_t initial_append_size) |
- : current_position_(0), |
- initial_append_size_(initial_append_size), |
- mimetype_(mimetype), |
- chunk_demuxer_(new ChunkDemuxer( |
- base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)), |
- base::Bind(&MockMediaSource::OnEncryptedMediaInitData, |
- base::Unretained(this)), |
- &media_log_)), |
- owned_chunk_demuxer_(chunk_demuxer_) { |
- file_data_ = ReadTestDataFile(filename); |
- |
- if (initial_append_size_ == kAppendWholeFile) |
- initial_append_size_ = file_data_->data_size(); |
- |
- DCHECK_GT(initial_append_size_, 0u); |
- DCHECK_LE(initial_append_size_, file_data_->data_size()); |
- } |
- |
- virtual ~MockMediaSource() {} |
- |
- std::unique_ptr<Demuxer> GetDemuxer() { |
- return std::move(owned_chunk_demuxer_); |
- } |
- |
- void set_encrypted_media_init_data_cb( |
- const Demuxer::EncryptedMediaInitDataCB& encrypted_media_init_data_cb) { |
- encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; |
- } |
- |
- void set_demuxer_failure_cb(const PipelineStatusCB& demuxer_failure_cb) { |
- demuxer_failure_cb_ = demuxer_failure_cb; |
- } |
- |
- void Seek(base::TimeDelta seek_time, |
- size_t new_position, |
- size_t seek_append_size) { |
- chunk_demuxer_->StartWaitingForSeek(seek_time); |
- |
- chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(), |
- kInfiniteDuration, |
- &last_timestamp_offset_); |
- |
- DCHECK_LT(new_position, file_data_->data_size()); |
- current_position_ = new_position; |
- |
- AppendData(seek_append_size); |
- } |
- |
- void Seek(base::TimeDelta seek_time) { |
- chunk_demuxer_->StartWaitingForSeek(seek_time); |
- } |
- |
- void AppendData(size_t size) { |
- DCHECK(chunk_demuxer_); |
- DCHECK_LT(current_position_, file_data_->data_size()); |
- DCHECK_LE(current_position_ + size, file_data_->data_size()); |
- |
- ASSERT_TRUE(chunk_demuxer_->AppendData( |
- kSourceId, file_data_->data() + current_position_, size, |
- base::TimeDelta(), kInfiniteDuration, &last_timestamp_offset_)); |
- current_position_ += size; |
- } |
- |
- bool AppendAtTime(base::TimeDelta timestamp_offset, |
- const uint8_t* pData, |
- int size) { |
- CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId)); |
- bool success = |
- chunk_demuxer_->AppendData(kSourceId, pData, size, base::TimeDelta(), |
- kInfiniteDuration, ×tamp_offset); |
- last_timestamp_offset_ = timestamp_offset; |
- return success; |
- } |
- |
- void AppendAtTimeWithWindow(base::TimeDelta timestamp_offset, |
- base::TimeDelta append_window_start, |
- base::TimeDelta append_window_end, |
- const uint8_t* pData, |
- int size) { |
- CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId)); |
- ASSERT_TRUE( |
- chunk_demuxer_->AppendData(kSourceId, pData, size, append_window_start, |
- append_window_end, ×tamp_offset)); |
- last_timestamp_offset_ = timestamp_offset; |
- } |
- |
- void SetMemoryLimits(size_t limit_bytes) { |
- chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::AUDIO, limit_bytes); |
- chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::VIDEO, limit_bytes); |
- } |
- |
- bool EvictCodedFrames(base::TimeDelta currentMediaTime, size_t newDataSize) { |
- return chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime, |
- newDataSize); |
- } |
- |
- void RemoveRange(base::TimeDelta start, base::TimeDelta end) { |
- chunk_demuxer_->Remove(kSourceId, start, end); |
- } |
- |
- void EndOfStream() { chunk_demuxer_->MarkEndOfStream(PIPELINE_OK); } |
- |
- void Shutdown() { |
- if (!chunk_demuxer_) |
- return; |
- chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(), |
- kInfiniteDuration, |
- &last_timestamp_offset_); |
- chunk_demuxer_->Shutdown(); |
- chunk_demuxer_ = NULL; |
- } |
- |
- void DemuxerOpened() { |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, base::Bind(&MockMediaSource::DemuxerOpenedTask, |
- base::Unretained(this))); |
- } |
- |
- void DemuxerOpenedTask() { |
- ChunkDemuxer::Status status = AddId(); |
- if (status != ChunkDemuxer::kOk) { |
- CHECK(!demuxer_failure_cb_.is_null()); |
- demuxer_failure_cb_.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
- return; |
- } |
- chunk_demuxer_->SetTracksWatcher( |
- kSourceId, base::Bind(&MockMediaSource::InitSegmentReceived, |
- base::Unretained(this))); |
- |
- AppendData(initial_append_size_); |
- } |
- |
- ChunkDemuxer::Status AddId() { |
- // This code assumes that |mimetype_| is one of the following forms. |
- // 1. audio/mpeg |
- // 2. video/webm;codec="vorbis,vp8". |
- size_t semicolon = mimetype_.find(";"); |
- std::string type = mimetype_; |
- std::string codecs_param = ""; |
- if (semicolon != std::string::npos) { |
- type = mimetype_.substr(0, semicolon); |
- size_t codecs_param_start = mimetype_.find("codecs=\"", semicolon); |
- |
- CHECK_NE(codecs_param_start, std::string::npos); |
- |
- codecs_param_start += 8; // Skip over the codecs=". |
- |
- size_t codecs_param_end = mimetype_.find("\"", codecs_param_start); |
- |
- CHECK_NE(codecs_param_end, std::string::npos); |
- |
- codecs_param = mimetype_.substr(codecs_param_start, |
- codecs_param_end - codecs_param_start); |
- } |
- |
- return chunk_demuxer_->AddId(kSourceId, type, codecs_param); |
- } |
- |
- void OnEncryptedMediaInitData(EmeInitDataType init_data_type, |
- const std::vector<uint8_t>& init_data) { |
- DCHECK(!init_data.empty()); |
- CHECK(!encrypted_media_init_data_cb_.is_null()); |
- encrypted_media_init_data_cb_.Run(init_data_type, init_data); |
- } |
- |
- base::TimeDelta last_timestamp_offset() const { |
- return last_timestamp_offset_; |
- } |
- |
- void InitSegmentReceived(std::unique_ptr<MediaTracks> tracks) { |
- CHECK(tracks.get()); |
- EXPECT_GT(tracks->tracks().size(), 0u); |
- CHECK(chunk_demuxer_); |
- // Verify that track ids are unique. |
- std::set<MediaTrack::Id> track_ids; |
- for (const auto& track : tracks->tracks()) { |
- EXPECT_EQ(track_ids.end(), track_ids.find(track->id())); |
- track_ids.insert(track->id()); |
- } |
- InitSegmentReceivedMock(tracks); |
- } |
- |
- MOCK_METHOD1(InitSegmentReceivedMock, void(std::unique_ptr<MediaTracks>&)); |
- |
- private: |
- MediaLog media_log_; |
- scoped_refptr<DecoderBuffer> file_data_; |
- size_t current_position_; |
- size_t initial_append_size_; |
- std::string mimetype_; |
- ChunkDemuxer* chunk_demuxer_; |
- std::unique_ptr<Demuxer> owned_chunk_demuxer_; |
- PipelineStatusCB demuxer_failure_cb_; |
- Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_; |
- base::TimeDelta last_timestamp_offset_; |
-}; |
- |
// A rough simulation of GpuVideoDecoder that fails every Decode() request. This |
// is used to test post-Initialize() fallback paths. |
class FailingVideoDecoder : public VideoDecoder { |
@@ -673,10 +384,10 @@ class FailingVideoDecoder : public VideoDecoder { |
// preferably by eliminating multiple inheritance here which is |
// banned by Google C++ style. |
#if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST) |
-class PipelineIntegrationTestHost : public service_manager::test::ServiceTest, |
- public PipelineIntegrationTestBase { |
+class PipelineIntegrationTest : public service_manager::test::ServiceTest, |
+ public PipelineIntegrationTestBase { |
public: |
- PipelineIntegrationTestHost() |
+ PipelineIntegrationTest() |
: service_manager::test::ServiceTest( |
"media_pipeline_integration_shelltests") {} |
@@ -703,74 +414,9 @@ class PipelineIntegrationTestHost : public service_manager::test::ServiceTest, |
mojom::InterfaceFactoryPtr media_interface_factory_; |
}; |
#else |
-class PipelineIntegrationTestHost : public testing::Test, |
- public PipelineIntegrationTestBase {}; |
-#endif // defined(MOJO_RENDERER) |
- |
-class PipelineIntegrationTest : public PipelineIntegrationTestHost { |
+class PipelineIntegrationTest : public testing::Test, |
+ public PipelineIntegrationTestBase { |
public: |
- PipelineStatus StartPipelineWithMediaSource(MockMediaSource* source) { |
- return StartPipelineWithMediaSource(source, kNormal, nullptr); |
- } |
- |
- PipelineStatus StartPipelineWithEncryptedMedia( |
- MockMediaSource* source, |
- FakeEncryptedMedia* encrypted_media) { |
- return StartPipelineWithMediaSource(source, kNormal, encrypted_media); |
- } |
- |
- PipelineStatus StartPipelineWithMediaSource( |
- MockMediaSource* source, |
- uint8_t test_type, |
- FakeEncryptedMedia* encrypted_media) { |
- hashing_enabled_ = test_type & kHashed; |
- clockless_playback_ = test_type & kClockless; |
- |
- if (!(test_type & kExpectDemuxerFailure)) |
- EXPECT_CALL(*source, InitSegmentReceivedMock(_)).Times(AtLeast(1)); |
- |
- EXPECT_CALL(*this, OnMetadata(_)) |
- .Times(AtMost(1)) |
- .WillRepeatedly(SaveArg<0>(&metadata_)); |
- EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)) |
- .Times(AnyNumber()); |
- EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)) |
- .Times(AnyNumber()); |
- EXPECT_CALL(*this, OnDurationChange()).Times(AnyNumber()); |
- EXPECT_CALL(*this, OnVideoNaturalSizeChange(_)).Times(AtMost(1)); |
- EXPECT_CALL(*this, OnVideoOpacityChange(_)).Times(AtMost(1)); |
- |
- source->set_demuxer_failure_cb(base::Bind( |
- &PipelineIntegrationTest::OnStatusCallback, base::Unretained(this))); |
- demuxer_ = source->GetDemuxer(); |
- |
- if (encrypted_media) { |
- EXPECT_CALL(*this, DecryptorAttached(true)); |
- |
- // Encrypted content used but keys provided in advance, so this is |
- // never called. |
- EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); |
- pipeline_->SetCdm(encrypted_media->GetCdmContext(), |
- base::Bind(&PipelineIntegrationTest::DecryptorAttached, |
- base::Unretained(this))); |
- } else { |
- // Encrypted content not used, so this is never called. |
- EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); |
- } |
- |
- pipeline_->Start(demuxer_.get(), CreateRenderer(), this, |
- base::Bind(&PipelineIntegrationTest::OnStatusCallback, |
- base::Unretained(this))); |
- |
- if (encrypted_media) { |
- source->set_encrypted_media_init_data_cb( |
- base::Bind(&FakeEncryptedMedia::OnEncryptedMediaInitData, |
- base::Unretained(encrypted_media))); |
- } |
- base::RunLoop().Run(); |
- return pipeline_status_; |
- } |
- |
// Verifies that seeking works properly for ChunkDemuxer when the |
// seek happens while there is a pending read on the ChunkDemuxer |
// and no data is available. |
@@ -801,6 +447,7 @@ class PipelineIntegrationTest : public PipelineIntegrationTestHost { |
return true; |
} |
}; |
+#endif // defined(MOJO_RENDERER) |
struct PlaybackTestData { |
const std::string filename; |