Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(451)

Unified Diff: media/test/pipeline_integration_test_base.cc

Issue 2808583002: RELAND: Media Remoting end to end integration tests. (Closed)
Patch Set: Rebased. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/test/pipeline_integration_test_base.cc
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index b2fd6f608808f6a898df374bf93c36ccbcb7152d..60b4eec159f62181498f6270f3590552fbaaaff0 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -12,11 +12,13 @@
#include "base/memory/scoped_vector.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
-#include "media/base/cdm_context.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/base/cdm_key_information.h"
#include "media/base/media_log.h"
#include "media/base/media_tracks.h"
#include "media/base/test_data_util.h"
-#include "media/filters/chunk_demuxer.h"
+#include "media/base/timestamp_constants.h"
+#include "media/cdm/aes_decryptor.h"
#if !defined(MEDIA_DISABLE_FFMPEG)
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
@@ -82,6 +84,275 @@ static ScopedVector<AudioDecoder> CreateAudioDecodersForTest(
const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
+const size_t kAppendWholeFile = std::numeric_limits<size_t>::max();
+
+namespace {
DaleCurtis 2017/04/20 18:24:51 Again drop.
xjz 2017/04/20 21:26:10 Done.
+
+constexpr char kSourceId[] = "SourceId";
+
+class RendererFactoryImpl final : public PipelineTestRendererFactory {
+ public:
+ explicit RendererFactoryImpl(PipelineIntegrationTestBase* integration_test)
+ : integration_test_(integration_test) {}
+ ~RendererFactoryImpl() override {}
+
+ // PipelineTestRendererFactory implementation.
+ std::unique_ptr<Renderer> CreateRenderer(
+ CreateVideoDecodersCB prepend_video_decoders_cb = CreateVideoDecodersCB(),
+ CreateAudioDecodersCB prepend_audio_decoders_cb =
+ CreateAudioDecodersCB()) override {
+ return integration_test_->CreateRenderer(prepend_video_decoders_cb,
+ prepend_audio_decoders_cb);
+ }
+
+ private:
+ PipelineIntegrationTestBase* integration_test_;
+
+ DISALLOW_COPY_AND_ASSIGN(RendererFactoryImpl);
+};
+
+} // namespace
+
+MockMediaSource::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());
+}
+
+MockMediaSource::~MockMediaSource() {}
+
+std::unique_ptr<Demuxer> MockMediaSource::GetDemuxer() {
+ return std::move(owned_chunk_demuxer_);
+}
+
+void MockMediaSource::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 MockMediaSource::set_demuxer_failure_cb(
+ const PipelineStatusCB& demuxer_failure_cb) {
+ demuxer_failure_cb_ = demuxer_failure_cb;
+}
+
+void MockMediaSource::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 MockMediaSource::Seek(base::TimeDelta seek_time) {
+ chunk_demuxer_->StartWaitingForSeek(seek_time);
+}
+
+void MockMediaSource::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 MockMediaSource::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, &timestamp_offset);
+ last_timestamp_offset_ = timestamp_offset;
+ return success;
+}
+
+void MockMediaSource::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,
+ &timestamp_offset));
+ last_timestamp_offset_ = timestamp_offset;
+}
+
+void MockMediaSource::SetMemoryLimits(size_t limit_bytes) {
+ chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::AUDIO, limit_bytes);
+ chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::VIDEO, limit_bytes);
+}
+
+void MockMediaSource::EvictCodedFrames(base::TimeDelta currentMediaTime,
+ size_t newDataSize) {
+ chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime, newDataSize);
+}
+
+void MockMediaSource::RemoveRange(base::TimeDelta start, base::TimeDelta end) {
+ chunk_demuxer_->Remove(kSourceId, start, end);
+}
+
+void MockMediaSource::EndOfStream() {
+ chunk_demuxer_->MarkEndOfStream(PIPELINE_OK);
+}
+
+void MockMediaSource::Shutdown() {
+ if (!chunk_demuxer_)
+ return;
+ chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(),
+ kInfiniteDuration, &last_timestamp_offset_);
+ chunk_demuxer_->Shutdown();
+ chunk_demuxer_ = NULL;
+}
+
+void MockMediaSource::DemuxerOpened() {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockMediaSource::DemuxerOpenedTask, base::Unretained(this)));
+}
+
+void MockMediaSource::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 MockMediaSource::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 MockMediaSource::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 MockMediaSource::last_timestamp_offset() const {
+ return last_timestamp_offset_;
+}
+
+void MockMediaSource::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);
+}
+
+FakeEncryptedMedia::TestCdmContext::TestCdmContext(Decryptor* decryptor)
+ : decryptor_(decryptor) {}
+
+Decryptor* FakeEncryptedMedia::TestCdmContext::GetDecryptor() {
+ return decryptor_;
+}
+int FakeEncryptedMedia::TestCdmContext::GetCdmId() const {
+ return kInvalidCdmId;
+}
+
+FakeEncryptedMedia::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) {}
+
+FakeEncryptedMedia::~FakeEncryptedMedia() {}
+
+CdmContext* FakeEncryptedMedia::GetCdmContext() {
+ return &cdm_context_;
+}
+
+// Callbacks for firing session events. Delegate to |app_|.
+void FakeEncryptedMedia::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 FakeEncryptedMedia::OnSessionClosed(const std::string& session_id) {
+ app_->OnSessionClosed(session_id);
+}
+
+void FakeEncryptedMedia::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 FakeEncryptedMedia::OnEncryptedMediaInitData(
+ EmeInitDataType init_data_type,
+ const std::vector<uint8_t>& init_data) {
+ app_->OnEncryptedMediaInitData(init_data_type, init_data, decryptor_.get());
+}
PipelineIntegrationTestBase::PipelineIntegrationTestBase()
: hashing_enabled_(false),
@@ -91,7 +362,8 @@ PipelineIntegrationTestBase::PipelineIntegrationTestBase()
pipeline_status_(PIPELINE_OK),
last_video_frame_format_(PIXEL_FORMAT_UNKNOWN),
last_video_frame_color_space_(COLOR_SPACE_UNSPECIFIED),
- current_duration_(kInfiniteDuration) {
+ current_duration_(kInfiniteDuration),
+ renderer_factory_(new RendererFactoryImpl(this)) {
ResetVideoHash();
EXPECT_CALL(*this, OnVideoAverageKeyframeDistanceUpdate()).Times(AnyNumber());
}
@@ -217,12 +489,12 @@ PipelineStatus PipelineIntegrationTestBase::StartInternal(
// media files are provided in advance.
EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
- pipeline_->Start(
- demuxer_.get(),
- CreateRenderer(prepend_video_decoders_cb, prepend_audio_decoders_cb),
- this,
- base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
- base::Unretained(this)));
+ pipeline_->Start(demuxer_.get(),
+ renderer_factory_->CreateRenderer(prepend_video_decoders_cb,
+ prepend_audio_decoders_cb),
+ this,
+ base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
+ base::Unretained(this)));
base::RunLoop().Run();
return pipeline_status_;
}
@@ -298,7 +570,7 @@ bool PipelineIntegrationTestBase::Resume(base::TimeDelta seek_time) {
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH))
.WillOnce(InvokeWithoutArgs(&message_loop_, &base::MessageLoop::QuitNow));
- pipeline_->Resume(CreateRenderer(), seek_time,
+ pipeline_->Resume(renderer_factory_->CreateRenderer(), seek_time,
base::Bind(&PipelineIntegrationTestBase::OnSeeked,
base::Unretained(this), seek_time));
base::RunLoop().Run();
@@ -472,6 +744,101 @@ base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
return clockless_audio_sink_->render_time();
}
+PipelineStatus PipelineIntegrationTestBase::StartPipelineWithMediaSource(
+ MockMediaSource* source) {
+ return StartPipelineWithMediaSource(source, kNormal, nullptr);
+}
+
+PipelineStatus PipelineIntegrationTestBase::StartPipelineWithEncryptedMedia(
+ MockMediaSource* source,
+ FakeEncryptedMedia* encrypted_media) {
+ return StartPipelineWithMediaSource(source, kNormal, encrypted_media);
+}
+
+PipelineStatus PipelineIntegrationTestBase::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(
+ &PipelineIntegrationTestBase::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(&PipelineIntegrationTestBase::DecryptorAttached,
+ base::Unretained(this)));
+ } else {
+ // Encrypted content not used, so this is never called.
+ EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
+ }
+
+ pipeline_->Start(demuxer_.get(), renderer_factory_->CreateRenderer(), this,
+ base::Bind(&PipelineIntegrationTestBase::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.
+bool PipelineIntegrationTestBase::TestSeekDuringRead(
+ const std::string& filename,
+ const std::string& mimetype,
+ int initial_append_size,
+ base::TimeDelta start_seek_time,
+ base::TimeDelta seek_time,
+ int seek_file_position,
+ int seek_append_size) {
+ MockMediaSource source(filename, mimetype, initial_append_size);
+
+ if (StartPipelineWithMediaSource(&source) != PIPELINE_OK)
+ return false;
+
+ Play();
+ if (!WaitUntilCurrentTimeIsAfter(start_seek_time))
+ return false;
+
+ source.Seek(seek_time, seek_file_position, seek_append_size);
+ if (!Seek(seek_time))
+ return false;
+
+ source.EndOfStream();
+
+ source.Shutdown();
+ Stop();
+ return true;
+}
+
base::TimeTicks DummyTickClock::NowTicks() {
now_ += base::TimeDelta::FromSeconds(60);
return now_;
« media/test/pipeline_integration_test_base.h ('K') | « media/test/pipeline_integration_test_base.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698