 Chromium Code Reviews
 Chromium Code Reviews Issue 1417533006:
  Unit test for WebMediaPlayerMS  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1417533006:
  Unit test for WebMediaPlayerMS  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: content/renderer/media/webmediaplayer_ms_unittest.cc | 
| diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..830824c978d5f809adecea89e15d9969739e0fbe | 
| --- /dev/null | 
| +++ b/content/renderer/media/webmediaplayer_ms_unittest.cc | 
| @@ -0,0 +1,618 @@ | 
| +// Copyright 2015 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "base/strings/string_number_conversions.h" | 
| +#include "base/strings/string_split.h" | 
| 
mcasas
2015/12/08 22:54:48
Are these two includes used? Check the rest as wel
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| +#include "cc/layers/video_frame_provider.h" | 
| +#include "content/public/renderer/media_stream_renderer_factory.h" | 
| +#include "content/renderer/media/webmediaplayer_ms.h" | 
| +#include "content/renderer/media/webmediaplayer_ms_compositor.h" | 
| +#include "content/renderer/render_frame_impl.h" | 
| +#include "media/base/test_helpers.h" | 
| +#include "media/base/video_frame.h" | 
| +#include "third_party/WebKit/public/platform/WebMediaPlayer.h" | 
| +#include "third_party/WebKit/public/platform/WebMediaPlayerClient.h" | 
| + | 
| +namespace content { | 
| + | 
| +class MockCB { | 
| 
mcasas
2015/12/08 22:54:49
Unused?
 
qiangchen
2015/12/09 00:55:23
Done.
Sorry, unremoved Junk code.
 | 
| + public: | 
| + void VerifyAndClearExpectations() { | 
| + // Note: the raw value of "this" could be different from &mock_cb_. | 
| + // So this is not equivalent with calling | 
| + // VerifyAndClearExpectations(&mock_cb_) outside. | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + } | 
| +}; | 
| + | 
| +enum class FrameType { | 
| + NORMAL_FRAME = 0, | 
| + BROKEN_FRAME = -1, | 
| + TEST_BRAKE = -2 // Signal to pause message loop. | 
| +}; | 
| + | 
| +using TestFrame = std::pair<FrameType, scoped_refptr<media::VideoFrame>>; | 
| + | 
| +class ReusableMessageLoopEvent { | 
| + public: | 
| + ReusableMessageLoopEvent() : event_(new media::WaitableMessageLoopEvent()) {} | 
| + | 
| + base::Closure GetClosure() const { return event_->GetClosure(); } | 
| + | 
| + media::PipelineStatusCB GetPipelineStatusCB() const { | 
| + return event_->GetPipelineStatusCB(); | 
| + } | 
| + | 
| + void RunAndWait() { | 
| + event_->RunAndWait(); | 
| + event_.reset(new media::WaitableMessageLoopEvent()); | 
| + } | 
| + | 
| + void RunAndWaitForStatus(media::PipelineStatus expected) { | 
| + event_->RunAndWaitForStatus(expected); | 
| + event_.reset(new media::WaitableMessageLoopEvent()); | 
| + } | 
| + | 
| + private: | 
| + scoped_ptr<media::WaitableMessageLoopEvent> event_; | 
| +}; | 
| + | 
| +// The class mainly used to inject VideoFrames into WebMediaPlayerMS. | 
| 
mcasas
2015/12/08 22:54:49
// This class is used mainly to...
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| +class MockVideoFrameProvider : public VideoFrameProvider { | 
| + public: | 
| + MockVideoFrameProvider( | 
| + const scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 
| + ReusableMessageLoopEvent* message_loop_controller, | 
| + const base::Closure& error_cb, | 
| + const VideoFrameProvider::RepaintCB& repaint_cb) | 
| + : started_(false), | 
| + task_runner_(task_runner), | 
| + message_loop_controller_(message_loop_controller), | 
| + error_cb_(error_cb), | 
| + repaint_cb_(repaint_cb), | 
| + delay_(base::TimeDelta::FromSecondsD(1.0 / 30.0)) {} | 
| + | 
| + // Implementation of VideoFrameProvider | 
| + void Start() override; | 
| + void Stop() override; | 
| + void Play() override; | 
| + void Pause() override; | 
| + | 
| + // Methods for test use | 
| + void PrepareFrame(FrameType category, | 
| 
mcasas
2015/12/08 22:54:49
When I read this method's name I thought it would
 
qiangchen
2015/12/09 00:55:23
I just changed it to AddFrame, and move it to priv
 | 
| + const scoped_refptr<media::VideoFrame>& frame); | 
| + void QueueFrames(const std::vector<int>& input); | 
| 
mcasas
2015/12/08 22:54:49
s/input/timestamps/, or s/input/categories/?
|inpu
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| + bool Started() { return started_; } | 
| + bool Paused() { return paused_; } | 
| + | 
| + private: | 
| + ~MockVideoFrameProvider() override {} | 
| + | 
| + // Main function that pushes frame into WebMediaPlayerMS | 
| 
mcasas
2015/12/08 22:54:49
.. to push a frame
or
that pushes a frame,
because
 
qiangchen
2015/12/09 00:55:23
Yep, each function call, one frame.
 | 
| + void InjectFrame(); | 
| + | 
| + bool started_; | 
| + bool paused_; | 
| + | 
| + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
| + ReusableMessageLoopEvent* const message_loop_controller_; | 
| + const base::Closure error_cb_; | 
| + const VideoFrameProvider::RepaintCB repaint_cb_; | 
| + | 
| + std::deque<TestFrame> frames_; | 
| + base::TimeDelta delay_; | 
| 
mcasas
2015/12/08 22:54:48
is this the |delay_of_next_generated_frame_| or wh
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| +}; | 
| + | 
| +void MockVideoFrameProvider::Start() { | 
| + started_ = true; | 
| + paused_ = false; | 
| + task_runner_->PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&MockVideoFrameProvider::InjectFrame, base::Unretained(this))); | 
| +} | 
| + | 
| +void MockVideoFrameProvider::Stop() { | 
| + started_ = false; | 
| + frames_.clear(); | 
| +} | 
| + | 
| +void MockVideoFrameProvider::Play() { | 
| + CHECK(started_); | 
| + paused_ = false; | 
| +} | 
| + | 
| +void MockVideoFrameProvider::Pause() { | 
| + CHECK(started_); | 
| + paused_ = true; | 
| +} | 
| + | 
| +void MockVideoFrameProvider::PrepareFrame( | 
| + FrameType category, | 
| + const scoped_refptr<media::VideoFrame>& frame) { | 
| + frames_.push_back(std::make_pair(category, frame)); | 
| +} | 
| + | 
| +void MockVideoFrameProvider::QueueFrames(const std::vector<int>& input) { | 
| + for (const int token : input) { | 
| + if (token < 0) { | 
| + PrepareFrame(static_cast<FrameType>(token), nullptr); | 
| + continue; | 
| + } | 
| + | 
| + if (token >= 0) { | 
| + gfx::Size natural_size = media::TestVideoConfig::NormalCodedSize(); | 
| + auto frame = media::VideoFrame::CreateFrame( | 
| + media::PIXEL_FORMAT_YV12, natural_size, gfx::Rect(natural_size), | 
| + natural_size, base::TimeDelta::FromMilliseconds(token)); | 
| + | 
| + frame->metadata()->SetTimeTicks( | 
| + media::VideoFrameMetadata::Key::REFERENCE_TIME, | 
| + base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(token)); | 
| + | 
| + PrepareFrame(FrameType::NORMAL_FRAME, frame); | 
| + continue; | 
| + } | 
| + | 
| + CHECK(false) << "Unrecognized token: " << token; | 
| 
mcasas
2015/12/08 22:54:48
Unreachable? Bc of l.137 and l.143, which could be
 
qiangchen
2015/12/09 00:55:23
Done.
From old string parse method. Add a MIN_TYP
 | 
| + } | 
| +} | 
| + | 
| +void MockVideoFrameProvider::InjectFrame() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + if (!started_) | 
| + return; | 
| + | 
| + if (frames_.empty()) { | 
| + message_loop_controller_->GetClosure().Run(); | 
| + return; | 
| + } | 
| + | 
| + auto frame = frames_.front(); | 
| + frames_.pop_front(); | 
| + | 
| + if (frame.first == FrameType::BROKEN_FRAME) { | 
| + error_cb_.Run(); | 
| + return; | 
| + } | 
| + | 
| + // For pause case, the provider will still let the stream continue, but | 
| + // not send the frames to the player. As is the same case in reality. | 
| + if (frame.first == FrameType::NORMAL_FRAME) { | 
| + if (!paused_) | 
| + repaint_cb_.Run(frame.second); | 
| + | 
| + for (size_t i = 0; i < frames_.size(); ++i) { | 
| + if (frames_[i].first == FrameType::NORMAL_FRAME) { | 
| + delay_ = (frames_[i].second->timestamp() - frame.second->timestamp()) / | 
| + (i + 1); | 
| + break; | 
| + } | 
| + } | 
| + } | 
| + | 
| + task_runner_->PostDelayedTask( | 
| + FROM_HERE, | 
| + base::Bind(&MockVideoFrameProvider::InjectFrame, base::Unretained(this)), | 
| + delay_); | 
| + | 
| + // This will pause the |message_loop_|, and the purpose is to allow main test | 
| 
mcasas
2015/12/08 22:54:49
to allow _the_ main...
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| + // function to do some operations (e.g. call pause(), switch to background | 
| + // rendering, etc) on WebMediaPlayerMS before resuming the |message_loop_|. | 
| + if (frame.first == FrameType::TEST_BRAKE) | 
| + message_loop_controller_->GetClosure().Run(); | 
| +} | 
| + | 
| +// The class is used to generate a MockVideoProvider in | 
| +// WebMediaPlayerMS::load(). | 
| +class MockRenderFactory : public MediaStreamRendererFactory { | 
| + public: | 
| + MockRenderFactory( | 
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 
| + ReusableMessageLoopEvent* message_loop_controller) | 
| + : task_runner_(task_runner), | 
| + message_loop_controller_(message_loop_controller) {} | 
| + | 
| + scoped_refptr<VideoFrameProvider> GetVideoFrameProvider( | 
| + const GURL& url, | 
| + const base::Closure& error_cb, | 
| + const VideoFrameProvider::RepaintCB& repaint_cb, | 
| + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 
| + const scoped_refptr<base::TaskRunner>& worker_task_runner, | 
| + media::GpuVideoAcceleratorFactories* gpu_factories) override; | 
| + | 
| + MockVideoFrameProvider* provider() { | 
| + return static_cast<MockVideoFrameProvider*>(provider_.get()); | 
| + } | 
| + | 
| + scoped_refptr<MediaStreamAudioRenderer> GetAudioRenderer( | 
| + const GURL& url, | 
| + int render_frame_id, | 
| + const std::string& device_id, | 
| + const url::Origin& security_origin) override { | 
| + return nullptr; | 
| + } | 
| + | 
| + private: | 
| + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
| + scoped_refptr<VideoFrameProvider> provider_; | 
| + ReusableMessageLoopEvent* const message_loop_controller_; | 
| +}; | 
| + | 
| +scoped_refptr<VideoFrameProvider> MockRenderFactory::GetVideoFrameProvider( | 
| + const GURL& url, | 
| + const base::Closure& error_cb, | 
| + const VideoFrameProvider::RepaintCB& repaint_cb, | 
| + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 
| + const scoped_refptr<base::TaskRunner>& worker_task_runner, | 
| + media::GpuVideoAcceleratorFactories* gpu_factories) { | 
| + provider_ = new MockVideoFrameProvider(task_runner_, message_loop_controller_, | 
| + error_cb, repaint_cb); | 
| + | 
| + return provider_; | 
| +} | 
| + | 
| +class WebMediaPlayerMSTest : public testing::Test, | 
| + public blink::WebMediaPlayerClient, | 
| + public cc::VideoFrameProvider::Client { | 
| + public: | 
| + WebMediaPlayerMSTest() | 
| + : render_factory_(new MockRenderFactory(message_loop_.task_runner(), | 
| + &message_loop_controller_)), | 
| + player_(nullptr, | 
| + this, | 
| + base::WeakPtr<media::WebMediaPlayerDelegate>(), | 
| + new media::MediaLog(), | 
| + scoped_ptr<MediaStreamRendererFactory>(render_factory_), | 
| + message_loop_.task_runner(), | 
| + message_loop_.task_runner(), | 
| + message_loop_.task_runner(), | 
| + nullptr, | 
| + blink::WebString(), | 
| + blink::WebSecurityOrigin()), | 
| + rendering_(false) {} | 
| + ~WebMediaPlayerMSTest() override {} | 
| + | 
| + MockVideoFrameProvider* LoadAndGetFrameProvider(bool algorithm_enabled); | 
| + | 
| + // Implementation of WebMediaPlayerClient | 
| + void networkStateChanged() override; | 
| + void readyStateChanged() override; | 
| + void timeChanged() override {} | 
| + void repaint() override {} | 
| + void durationChanged() override {} | 
| + void sizeChanged() override {} | 
| + void playbackStateChanged() override {} | 
| + void setWebLayer(blink::WebLayer* layer) override; | 
| + blink::WebMediaPlayer::TrackId addAudioTrack(const blink::WebString& id, | 
| + AudioTrackKind, | 
| + const blink::WebString& label, | 
| + const blink::WebString& language, | 
| + bool enabled) override { | 
| + return 0; | 
| + } | 
| + void removeAudioTrack(blink::WebMediaPlayer::TrackId) override {} | 
| + blink::WebMediaPlayer::TrackId addVideoTrack(const blink::WebString& id, | 
| + VideoTrackKind, | 
| + const blink::WebString& label, | 
| + const blink::WebString& language, | 
| + bool selected) override { | 
| + return 0; | 
| + } | 
| + void removeVideoTrack(blink::WebMediaPlayer::TrackId) override {} | 
| + void addTextTrack(blink::WebInbandTextTrack*) override {} | 
| + void removeTextTrack(blink::WebInbandTextTrack*) override {} | 
| + void mediaSourceOpened(blink::WebMediaSource*) override {} | 
| + void requestSeek(double) override {} | 
| + void remoteRouteAvailabilityChanged(bool) override {} | 
| + void connectedToRemoteDevice() override {} | 
| + void disconnectedFromRemoteDevice() override {} | 
| + | 
| + // Implementation of cc::VideoFrameProvider::Client | 
| + void StopUsingProvider() override; | 
| + void StartRendering() override; | 
| + void StopRendering() override; | 
| + void DidReceiveFrame() override {} | 
| + void DidUpdateMatrix(const float* matrix) override {} | 
| + | 
| + // For test use | 
| + void SetBackgroundRendering(bool background_rendering) { | 
| + background_rendering_ = background_rendering; | 
| + } | 
| + | 
| + protected: | 
| + MOCK_METHOD0(DoStartRendering, void()); | 
| + MOCK_METHOD0(DoStopRendering, void()); | 
| + | 
| + MOCK_METHOD1(DoSetWebLayer, void(bool)); | 
| + MOCK_METHOD1(DoNetworkStateChanged, | 
| + void(blink::WebMediaPlayer::NetworkState)); | 
| + MOCK_METHOD1(DoReadyStateChanged, void(blink::WebMediaPlayer::ReadyState)); | 
| + | 
| + base::MessageLoop message_loop_; | 
| + MockRenderFactory* render_factory_; | 
| + WebMediaPlayerMS player_; | 
| + WebMediaPlayerMSCompositor* compositor_; | 
| + ReusableMessageLoopEvent message_loop_controller_; | 
| + | 
| + private: | 
| + // Main function trying to ask WebMediaPlayerMS to submit a frame for | 
| + // rendering. | 
| + void RenderFrame(); | 
| + | 
| + bool rendering_; | 
| + bool background_rendering_; | 
| +}; | 
| + | 
| +MockVideoFrameProvider* WebMediaPlayerMSTest::LoadAndGetFrameProvider( | 
| + bool algorithm_enabled) { | 
| + MockVideoFrameProvider* provider = render_factory_->provider(); | 
| + EXPECT_EQ(nullptr, provider); | 
| 
mcasas
2015/12/08 22:54:48
Hmm no need to hold on to |provider| here, I'd say
 
qiangchen
2015/12/09 00:55:24
Done.
 | 
| + | 
| + EXPECT_CALL( | 
| + *this, DoNetworkStateChanged(blink::WebMediaPlayer::NetworkStateLoading)); | 
| + EXPECT_CALL( | 
| + *this, DoReadyStateChanged(blink::WebMediaPlayer::ReadyStateHaveNothing)); | 
| + player_.load(blink::WebMediaPlayer::LoadTypeURL, blink::WebURL(), | 
| + blink::WebMediaPlayer::CORSModeUnspecified); | 
| + compositor_ = player_.compositor_.get(); | 
| + EXPECT_NE(nullptr, compositor_); | 
| + compositor_->SetAlgorithmEnabledForTesting(algorithm_enabled); | 
| + | 
| + provider = render_factory_->provider(); | 
| + EXPECT_NE(nullptr, provider); | 
| + EXPECT_TRUE(provider->Started()); | 
| + | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + return provider; | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::networkStateChanged() { | 
| + blink::WebMediaPlayer::NetworkState state = player_.networkState(); | 
| + DoNetworkStateChanged(state); | 
| + if (state == blink::WebMediaPlayer::NetworkState::NetworkStateFormatError || | 
| + state == blink::WebMediaPlayer::NetworkState::NetworkStateDecodeError || | 
| + state == blink::WebMediaPlayer::NetworkState::NetworkStateNetworkError) { | 
| + message_loop_controller_.GetPipelineStatusCB().Run( | 
| + media::PipelineStatus::PIPELINE_ERROR_NETWORK); | 
| + } | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::readyStateChanged() { | 
| + blink::WebMediaPlayer::ReadyState state = player_.readyState(); | 
| + DoReadyStateChanged(state); | 
| + if (state == blink::WebMediaPlayer::ReadyState::ReadyStateHaveEnoughData) | 
| + player_.play(); | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::setWebLayer(blink::WebLayer* layer) { | 
| + if (layer) | 
| + compositor_->SetVideoFrameProviderClient(this); | 
| + DoSetWebLayer(!!layer); | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::StopUsingProvider() { | 
| + if (rendering_) | 
| + StopRendering(); | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::StartRendering() { | 
| + if (!rendering_) { | 
| + rendering_ = true; | 
| + message_loop_.PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&WebMediaPlayerMSTest::RenderFrame, base::Unretained(this))); | 
| + } | 
| + DoStartRendering(); | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::StopRendering() { | 
| + rendering_ = false; | 
| + DoStopRendering(); | 
| +} | 
| + | 
| +void WebMediaPlayerMSTest::RenderFrame() { | 
| + if (!rendering_ || !compositor_) | 
| + return; | 
| + | 
| + base::TimeTicks now = base::TimeTicks::Now(); | 
| + base::TimeTicks deadline_min = | 
| + now + base::TimeDelta::FromSecondsD(1.0 / 60.0); | 
| + base::TimeTicks deadline_max = | 
| + deadline_min + base::TimeDelta::FromSecondsD(1.0 / 60.0); | 
| + | 
| + // Background rendering is different from stop rendering. The rendering loop | 
| + // is still running but we do not ask frames from compositor_. And | 
| + // background rendering is not initiated from compositor_. | 
| 
mcasas
2015/12/08 22:54:48
|compositor_| here and in the previous line.
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| + if (!background_rendering_) { | 
| + compositor_->UpdateCurrentFrame(deadline_min, deadline_max); | 
| + auto frame = compositor_->GetCurrentFrame(); | 
| + compositor_->PutCurrentFrame(); | 
| + } | 
| + message_loop_.PostDelayedTask( | 
| + FROM_HERE, | 
| + base::Bind(&WebMediaPlayerMSTest::RenderFrame, base::Unretained(this)), | 
| + base::TimeDelta::FromSecondsD(1.0 / 60.0)); | 
| +} | 
| + | 
| +TEST_F(WebMediaPlayerMSTest, Playing_Normal) { | 
| + // Workflow: | 
| + // 1. WMPMS::Load will generate and start content::VideoFrameProvider. | 
| + // 2. content::VideoFrameProvider will start pushing frames into WMPMS | 
| + // repeatedly. | 
| + // 3. On WMPMS receiving the first frame, a WebLayer will be created. | 
| + // 4. The WebLayer will call WMPMSCompositor::SetVideoFrameProviderClient, | 
| + // which in turn will trigger cc::VideoFrameProviderClient::StartRendering. | 
| + // 5. Then cc::VideoFrameProviderClient will start calling | 
| + // WMPMSCompositor::UpdateCurrentFrame, GetCurrentFrame for rendering | 
| + // repeatedly. | 
| + // 6. When WMPSMS::pause gets called, it should trigger | 
| + // content::VideoFrameProvider::Pause, and then the provider will stop | 
| + // pushing frames into WMPMS, but instead digesting them; simultanously, it | 
| + // should call cc::VideoFrameProviderClient::StopRendering, so the client | 
| + // will stop asking frames from WMPMSCompositor. | 
| + // 7. When WMPMS::play gets called, evething paused in step 6 should be | 
| + // resumed. | 
| 
mcasas
2015/12/08 22:54:49
I'd say this is a nice description that applies, w
 
qiangchen
2015/12/09 00:55:24
Done.
 | 
| + MockVideoFrameProvider* provider = LoadAndGetFrameProvider(true); | 
| + | 
| + int tokens[] = {0, 33, 66, 100, 133, 166, 200, 233, 266, 300, | 
| + 333, 366, 400, 433, 466, 500, 533, 566, 600}; | 
| + std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int)); | 
| + provider->QueueFrames(timestamps); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(true)); | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveMetadata)); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveEnoughData)); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(false)); | 
| +} | 
| + | 
| +TEST_F(WebMediaPlayerMSTest, Playing_ErrorFrame) { | 
| + MockVideoFrameProvider* provider = LoadAndGetFrameProvider(false); | 
| + | 
| + const int kBrokenFrame = static_cast<int>(FrameType::BROKEN_FRAME); | 
| + int tokens[] = {0, 33, 66, 100, 133, 166, 200, 233, 266, 300, | 
| + 333, 366, 400, 433, 466, 500, 533, 566, 600, kBrokenFrame}; | 
| + std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int)); | 
| 
mcasas
2015/12/08 22:54:48
If you make |tokens| a static array, we have the a
 
qiangchen
2015/12/09 00:55:23
Ah, that's a good suggestion, but we will have to
 | 
| + provider->QueueFrames(timestamps); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(true)); | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveMetadata)); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveEnoughData)); | 
| + EXPECT_CALL(*this, DoNetworkStateChanged( | 
| + blink::WebMediaPlayer::NetworkStateFormatError)); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_ERROR_NETWORK); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(false)); | 
| +} | 
| + | 
| +TEST_F(WebMediaPlayerMSTest, PlayThenPause) { | 
| + MockVideoFrameProvider* provider = LoadAndGetFrameProvider(false); | 
| + | 
| + const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE); | 
| + int tokens[] = {0, 33, 66, 100, 133, kTestBrake, 166, 200, 233, 266, | 
| + 300, 333, 366, 400, 433, 466, 500, 533, 566, 600}; | 
| + std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int)); | 
| + provider->QueueFrames(timestamps); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(true)); | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveMetadata)); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveEnoughData)); | 
| + // Rendering will be "paused" at the TestBrake. | 
| 
mcasas
2015/12/08 22:54:49
|kTestBrake|.
 
qiangchen
2015/12/09 00:55:23
Let me remove this confusing comment. Because this
 | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + // Here No more rendering. 1. Render should not be called, 2. Frame freeze. | 
| 
mcasas
2015/12/08 22:54:48
Correct syntax plz.
 
qiangchen
2015/12/09 00:55:23
Done.
 | 
| + EXPECT_CALL(*this, DoStopRendering()); | 
| + player_.pause(); | 
| + auto prev_frame = compositor_->GetCurrentFrame(); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + auto after_frame = compositor_->GetCurrentFrame(); | 
| + EXPECT_EQ(prev_frame->timestamp(), after_frame->timestamp()); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(false)); | 
| +} | 
| + | 
| +TEST_F(WebMediaPlayerMSTest, PlayThenPauseThenPlay) { | 
| + MockVideoFrameProvider* provider = LoadAndGetFrameProvider(false); | 
| + | 
| + const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE); | 
| + int tokens[] = {0, 33, 66, 100, 133, kTestBrake, 166, | 
| + 200, 233, 266, 300, 333, 366, 400, | 
| + 433, kTestBrake, 466, 500, 533, 566, 600}; | 
| + std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int)); | 
| + provider->QueueFrames(timestamps); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(true)); | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveMetadata)); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveEnoughData)); | 
| + // Rendering will be "paused" at the TestBrake. | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + // Here No more rendering. 1. Render should not be called, 2. Frame freeze. | 
| + EXPECT_CALL(*this, DoStopRendering()); | 
| + player_.pause(); | 
| + auto prev_frame = compositor_->GetCurrentFrame(); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + auto after_frame = compositor_->GetCurrentFrame(); | 
| + EXPECT_EQ(prev_frame->timestamp(), after_frame->timestamp()); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + // We resume the player | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + player_.play(); | 
| + prev_frame = compositor_->GetCurrentFrame(); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + after_frame = compositor_->GetCurrentFrame(); | 
| + EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp()); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(false)); | 
| +} | 
| + | 
| +TEST_F(WebMediaPlayerMSTest, BackgroudRendering) { | 
| + MockVideoFrameProvider* provider = LoadAndGetFrameProvider(true); | 
| + | 
| + const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE); | 
| + int tokens[] = {0, 33, 66, 100, 133, kTestBrake, 166, | 
| + 200, 233, 266, 300, 333, 366, 400, | 
| + 433, kTestBrake, 466, 500, 533, 566, 600}; | 
| + std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int)); | 
| + provider->QueueFrames(timestamps); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(true)); | 
| + EXPECT_CALL(*this, DoStartRendering()); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveMetadata)); | 
| + EXPECT_CALL(*this, DoReadyStateChanged( | 
| + blink::WebMediaPlayer::ReadyStateHaveEnoughData)); | 
| + // Rendering will be "paused" at the TestBrake. | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + // Switch to background rendering, expect rendering to continue | 
| + SetBackgroundRendering(true); | 
| + auto prev_frame = compositor_->GetCurrentFrame(); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + auto after_frame = compositor_->GetCurrentFrame(); | 
| + EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp()); | 
| + | 
| + // Switch to foreground rendering | 
| + SetBackgroundRendering(false); | 
| + prev_frame = compositor_->GetCurrentFrame(); | 
| + message_loop_controller_.RunAndWaitForStatus( | 
| + media::PipelineStatus::PIPELINE_OK); | 
| + after_frame = compositor_->GetCurrentFrame(); | 
| + EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp()); | 
| + testing::Mock::VerifyAndClearExpectations(this); | 
| + | 
| + EXPECT_CALL(*this, DoSetWebLayer(false)); | 
| +} | 
| + | 
| +} // namespace content |