Fix FullScreen crash in Webapp
[platform/framework/web/chromium-efl.git] / media / remoting / renderer_controller_unittest.cc
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/remoting/renderer_controller.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/functional/callback.h"
11 #include "base/run_loop.h"
12 #include "base/test/simple_test_tick_clock.h"
13 #include "base/test/task_environment.h"
14 #include "build/build_config.h"
15 #include "media/base/audio_decoder_config.h"
16 #include "media/base/limits.h"
17 #include "media/base/media_util.h"
18 #include "media/base/test_helpers.h"
19 #include "media/base/video_decoder_config.h"
20 #include "media/remoting/fake_remoter.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace media {
24 namespace remoting {
25
26 namespace {
27
28 PipelineMetadata DefaultMetadata(VideoCodec codec) {
29   PipelineMetadata data;
30   data.has_audio = true;
31   data.has_video = true;
32   data.video_decoder_config = TestVideoConfig::Normal(codec);
33   data.audio_decoder_config = TestAudioConfig::Normal();
34   data.natural_size = gfx::Size(1920, 1080);
35   return data;
36 }
37
38 const char kDefaultReceiver[] = "TestingChromeCast";
39
40 mojom::RemotingSinkMetadataPtr GetDefaultSinkMetadata(bool enable) {
41   mojom::RemotingSinkMetadataPtr metadata = mojom::RemotingSinkMetadata::New();
42   if (enable) {
43     metadata->features.push_back(mojom::RemotingSinkFeature::RENDERING);
44   } else {
45     metadata->features.clear();
46   }
47   metadata->video_capabilities.push_back(
48       mojom::RemotingSinkVideoCapability::CODEC_VP8);
49   metadata->audio_capabilities.push_back(
50       mojom::RemotingSinkAudioCapability::CODEC_BASELINE_SET);
51   metadata->friendly_name = kDefaultReceiver;
52   return metadata;
53 }
54
55 constexpr base::TimeDelta kDelayedStartDuration = base::Seconds(5);
56 constexpr double frame_rate = 30;
57 constexpr double high_pixel_rate = 3840 * 2160 * 30;
58 }  // namespace
59
60 class RendererControllerTest : public ::testing::Test,
61                                public MediaObserverClient {
62  public:
63   RendererControllerTest()
64       : controller_(FakeRemoterFactory::CreateController(false)) {}
65
66   RendererControllerTest(const RendererControllerTest&) = delete;
67   RendererControllerTest& operator=(const RendererControllerTest&) = delete;
68
69   ~RendererControllerTest() override = default;
70
71   void TearDown() final { RunUntilIdle(); }
72
73   static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
74
75   // MediaObserverClient implementation.
76   void SwitchToRemoteRenderer(
77       const std::string& remote_device_friendly_name) override {
78     is_rendering_remotely_ = true;
79     disable_pipeline_suspend_ = true;
80     sink_name_ = remote_device_friendly_name;
81   }
82
83   void SwitchToLocalRenderer(ReasonToSwitchToLocal reason) override {
84     is_rendering_remotely_ = false;
85     disable_pipeline_suspend_ = false;
86     sink_name_.clear();
87   }
88
89   double Duration() const override { return duration_in_sec_; }
90
91   unsigned DecodedFrameCount() const override { return decoded_frames_; }
92
93   void UpdateRemotePlaybackCompatibility(bool is_compatible) override {
94     is_remote_playback_compatible_ = is_compatible;
95   }
96
97   void set_pixels_per_second_(double pixels) {
98     controller_->pixels_per_second_ = pixels;
99   }
100
101   void InitializeControllerWithSink(
102       const PipelineMetadata& pipeline_metadata,
103       mojom::RemotingSinkMetadataPtr sink_metadata) {
104     EXPECT_FALSE(is_rendering_remotely_);
105     EXPECT_TRUE(sink_name_.empty());
106     controller_->clock_ = &clock_;
107     clock_.Advance(base::Seconds(1));
108     controller_->SetClient(this);
109     controller_->OnSinkAvailable(std::move(sink_metadata));
110     controller_->OnRemotePlaybackDisabled(false);
111     controller_->OnMetadataChanged(pipeline_metadata);
112     controller_->OnPlaying();
113     RunUntilIdle();
114     PixelRateTimerEnds();
115     RunUntilIdle();
116     EXPECT_FALSE(is_rendering_remotely_);
117     EXPECT_FALSE(disable_pipeline_suspend_);
118   }
119
120   void InitializeControllerAndBecomeDominant(
121       const PipelineMetadata& pipeline_metadata,
122       mojom::RemotingSinkMetadataPtr sink_metadata) {
123     InitializeControllerWithSink(pipeline_metadata, std::move(sink_metadata));
124     controller_->OnBecameDominantVisibleContent(true);
125     RunUntilIdle();
126   }
127
128   void PixelRateTimerEnds() {
129     EXPECT_TRUE(controller_->pixel_rate_timer_.IsRunning());
130     decoded_frames_ = frame_rate * kDelayedStartDuration.InSeconds();
131     clock_.Advance(kDelayedStartDuration);
132     controller_->pixel_rate_timer_.FireNow();
133   }
134
135   void ExpectInRemoting() const {
136     EXPECT_TRUE(is_rendering_remotely_);
137     EXPECT_TRUE(disable_pipeline_suspend_);
138     EXPECT_EQ(kDefaultReceiver, sink_name_);
139   }
140
141   void ExpectInLocalRendering() const {
142     EXPECT_FALSE(is_rendering_remotely_);
143     EXPECT_FALSE(disable_pipeline_suspend_);
144     EXPECT_TRUE(sink_name_.empty());
145   }
146
147   bool ShouldBeRemoting() const { return controller_->ShouldBeRemoting(); }
148
149   base::test::SingleThreadTaskEnvironment task_environment_;
150
151  protected:
152   bool is_rendering_remotely_ = false;
153   bool disable_pipeline_suspend_ = false;
154   bool is_remote_playback_compatible_ = false;
155   size_t decoded_bytes_ = 0;
156   unsigned decoded_frames_ = 0;
157   base::SimpleTestTickClock clock_;
158   std::string sink_name_;
159   std::unique_ptr<RendererController> controller_;
160   double duration_in_sec_ = 120;  // 2m duration.
161 };
162
163 TEST_F(RendererControllerTest, ShouldBeRemotingForDominantVisibleContent) {
164   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
165                                         GetDefaultSinkMetadata(true));
166   EXPECT_TRUE(ShouldBeRemoting());
167
168   controller_->OnBecameDominantVisibleContent(false);
169   RunUntilIdle();
170   EXPECT_FALSE(ShouldBeRemoting());
171 }
172
173 TEST_F(RendererControllerTest, ShouldBeRemotingForRequestFromBrowser) {
174   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
175                                         GetDefaultSinkMetadata(true));
176   controller_->OnMediaRemotingRequested();
177   RunUntilIdle();
178   EXPECT_TRUE(ShouldBeRemoting());
179
180   controller_->OnSinkGone();
181   RunUntilIdle();
182   EXPECT_FALSE(ShouldBeRemoting());
183   RunUntilIdle();
184 }
185
186 TEST_F(RendererControllerTest, ToggleRendererOnDominantChange) {
187   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
188                                         GetDefaultSinkMetadata(true));
189   ExpectInRemoting();  // All requirements now satisfied.
190
191   // Leaving fullscreen should shut down remoting.
192   controller_->OnBecameDominantVisibleContent(false);
193   RunUntilIdle();
194   ExpectInLocalRendering();
195 }
196
197 TEST_F(RendererControllerTest, ToggleRendererOnDisableChange) {
198   EXPECT_FALSE(is_rendering_remotely_);
199   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
200                                         GetDefaultSinkMetadata(true));
201   RunUntilIdle();
202   ExpectInRemoting();  // All requirements now satisfied.
203
204   // If the page disables remote playback (e.g., by setting the
205   // disableRemotePlayback attribute), this should shut down remoting.
206   controller_->OnRemotePlaybackDisabled(true);
207   RunUntilIdle();
208   ExpectInLocalRendering();
209 }
210
211 TEST_F(RendererControllerTest, NotStartForShortContent) {
212   duration_in_sec_ = 20;
213   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
214                                         GetDefaultSinkMetadata(true));
215   ExpectInLocalRendering();
216 }
217
218 TEST_F(RendererControllerTest, ToggleRendererOnSinkCapabilities) {
219   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
220                                         GetDefaultSinkMetadata(false));
221   // An available sink that does not support remote rendering should not cause
222   // the controller to toggle remote rendering on.
223   ExpectInLocalRendering();
224   controller_->OnSinkGone();  // Bye-bye useless sink!
225   RunUntilIdle();
226   ExpectInLocalRendering();
227
228   // A sink that *does* support remote rendering *does* cause the controller to
229   // toggle remote rendering on.
230   controller_->OnSinkAvailable(GetDefaultSinkMetadata(true));
231   RunUntilIdle();
232   RunUntilIdle();
233   ExpectInRemoting();  // All requirements now satisfied.
234 }
235
236 TEST_F(RendererControllerTest, ToggleRendererOnMediaRemotingRequest) {
237   InitializeControllerWithSink(DefaultMetadata(VideoCodec::kVP8), nullptr);
238   ExpectInLocalRendering();
239
240   // Should not start media remoting when there is no sink.
241   controller_->OnMediaRemotingRequested();
242   RunUntilIdle();
243   ExpectInLocalRendering();
244
245   // Start media remoting when there are available sinks.
246   controller_->OnSinkAvailable(GetDefaultSinkMetadata(false));
247   RunUntilIdle();
248   RunUntilIdle();
249   ExpectInRemoting();
250 }
251
252 TEST_F(RendererControllerTest, WithVP9VideoCodec) {
253   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP9),
254                                         GetDefaultSinkMetadata(true));
255   // An available sink that does not support VP9 video codec should not cause
256   // the controller to toggle remote rendering on.
257   ExpectInLocalRendering();
258
259   controller_->OnSinkGone();  // Bye-bye useless sink!
260   mojom::RemotingSinkMetadataPtr sink_metadata = GetDefaultSinkMetadata(true);
261   sink_metadata->video_capabilities.push_back(
262       mojom::RemotingSinkVideoCapability::CODEC_VP9);
263   // A sink that *does* support VP9 video codec *does* cause the controller to
264   // toggle remote rendering on.
265   controller_->OnSinkAvailable(std::move(sink_metadata));
266   RunUntilIdle();
267   RunUntilIdle();
268   ExpectInRemoting();  // All requirements now satisfied.
269 }
270
271 TEST_F(RendererControllerTest, WithHEVCVideoCodec) {
272   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kHEVC),
273                                         GetDefaultSinkMetadata(true));
274   // An available sink that does not support HEVC video codec should not cause
275   // the controller to toggle remote rendering on.
276   ExpectInLocalRendering();
277
278   controller_->OnSinkGone();  // Bye-bye useless sink!
279   RunUntilIdle();
280   ExpectInLocalRendering();
281   mojom::RemotingSinkMetadataPtr sink_metadata = GetDefaultSinkMetadata(true);
282   sink_metadata->video_capabilities.push_back(
283       mojom::RemotingSinkVideoCapability::CODEC_HEVC);
284   // A sink that *does* support HEVC video codec *does* cause the controller to
285   // toggle remote rendering on.
286   controller_->OnSinkAvailable(std::move(sink_metadata));
287   RunUntilIdle();
288   RunUntilIdle();
289   ExpectInRemoting();  // All requirements now satisfied.
290 }
291
292 TEST_F(RendererControllerTest, WithAACAudioCodec) {
293   const AudioDecoderConfig audio_config = AudioDecoderConfig(
294       AudioCodec::kAAC, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100,
295       EmptyExtraData(), EncryptionScheme::kUnencrypted);
296   PipelineMetadata pipeline_metadata = DefaultMetadata(VideoCodec::kVP8);
297   pipeline_metadata.audio_decoder_config = audio_config;
298   InitializeControllerAndBecomeDominant(pipeline_metadata,
299                                         GetDefaultSinkMetadata(true));
300   // An available sink that does not support AAC audio codec should not cause
301   // the controller to toggle remote rendering on.
302   ExpectInLocalRendering();
303
304   controller_->OnSinkGone();  // Bye-bye useless sink!
305   RunUntilIdle();
306   ExpectInLocalRendering();
307   mojom::RemotingSinkMetadataPtr sink_metadata = GetDefaultSinkMetadata(true);
308   sink_metadata->audio_capabilities.push_back(
309       mojom::RemotingSinkAudioCapability::CODEC_AAC);
310   // A sink that *does* support AAC audio codec *does* cause the controller to
311   // toggle remote rendering on.
312   controller_->OnSinkAvailable(std::move(sink_metadata));
313   RunUntilIdle();
314   RunUntilIdle();
315   ExpectInRemoting();  // All requirements now satisfied.
316 }
317
318 TEST_F(RendererControllerTest, WithOpusAudioCodec) {
319   const AudioDecoderConfig audio_config = AudioDecoderConfig(
320       AudioCodec::kOpus, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100,
321       EmptyExtraData(), EncryptionScheme::kUnencrypted);
322   PipelineMetadata pipeline_metadata = DefaultMetadata(VideoCodec::kVP8);
323   pipeline_metadata.audio_decoder_config = audio_config;
324   InitializeControllerAndBecomeDominant(pipeline_metadata,
325                                         GetDefaultSinkMetadata(true));
326   // An available sink that does not support Opus audio codec should not cause
327   // the controller to toggle remote rendering on.
328   ExpectInLocalRendering();
329
330   controller_->OnSinkGone();  // Bye-bye useless sink!
331   RunUntilIdle();
332   mojom::RemotingSinkMetadataPtr sink_metadata = GetDefaultSinkMetadata(true);
333   sink_metadata->audio_capabilities.push_back(
334       mojom::RemotingSinkAudioCapability::CODEC_OPUS);
335   // A sink that *does* support Opus audio codec *does* cause the controller to
336   // toggle remote rendering on.
337   controller_->OnSinkAvailable(std::move(sink_metadata));
338   RunUntilIdle();
339   RunUntilIdle();
340   ExpectInRemoting();  // All requirements now satisfied.
341 }
342
343 TEST_F(RendererControllerTest, StartFailedWithHighPixelRate) {
344   InitializeControllerWithSink(DefaultMetadata(VideoCodec::kVP8),
345                                GetDefaultSinkMetadata(true));
346   set_pixels_per_second_(high_pixel_rate);
347
348   controller_->OnBecameDominantVisibleContent(true);
349   RunUntilIdle();
350   ExpectInLocalRendering();
351 }
352
353 TEST_F(RendererControllerTest, StartSuccessWithHighPixelRate) {
354   mojom::RemotingSinkMetadataPtr sink_metadata = GetDefaultSinkMetadata(true);
355   sink_metadata->video_capabilities.push_back(
356       mojom::RemotingSinkVideoCapability::SUPPORT_4K);
357   InitializeControllerWithSink(DefaultMetadata(VideoCodec::kVP8),
358                                std::move(sink_metadata));
359   set_pixels_per_second_(high_pixel_rate);
360
361   controller_->OnBecameDominantVisibleContent(true);
362   RunUntilIdle();
363   ExpectInRemoting();
364 }
365
366 TEST_F(RendererControllerTest, PacingTooSlowly) {
367   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
368                                         GetDefaultSinkMetadata(true));
369   RunUntilIdle();
370   ExpectInRemoting();  // All requirements now satisfied.
371   controller_->OnRendererFatalError(StopTrigger::PACING_TOO_SLOWLY);
372   RunUntilIdle();
373   ExpectInLocalRendering();
374   controller_->OnSinkAvailable(GetDefaultSinkMetadata(true));
375   RunUntilIdle();
376   controller_->OnBecameDominantVisibleContent(false);
377   RunUntilIdle();
378   ExpectInLocalRendering();
379   controller_->OnBecameDominantVisibleContent(true);
380   RunUntilIdle();
381   ExpectInRemoting();  // All requirements now satisfied.
382 }
383
384 TEST_F(RendererControllerTest, StartFailed) {
385   controller_ = FakeRemoterFactory::CreateController(true);
386   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
387                                         GetDefaultSinkMetadata(true));
388   RunUntilIdle();
389   ExpectInLocalRendering();
390 }
391
392 TEST_F(RendererControllerTest, SetClientNullptr) {
393   controller_ = FakeRemoterFactory::CreateController(true);
394   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
395                                         GetDefaultSinkMetadata(true));
396   controller_->SetClient(nullptr);
397   RunUntilIdle();
398   ExpectInLocalRendering();
399 }
400
401 TEST_F(RendererControllerTest, OnFrozen) {
402   InitializeControllerAndBecomeDominant(DefaultMetadata(VideoCodec::kVP8),
403                                         GetDefaultSinkMetadata(true));
404
405   RunUntilIdle();
406   ExpectInRemoting();
407
408   // Pausing needs to occur before freezing can be enabled.
409   controller_->OnPaused();
410   ExpectInRemoting();
411
412   // Freezing should kick rendering back to local.
413   controller_->OnFrozen();
414   RunUntilIdle();
415   ExpectInLocalRendering();
416 }
417
418 #if BUILDFLAG(IS_ANDROID)
419 TEST_F(RendererControllerTest, RemotePlaybackHlsCompatibility) {
420   controller_ = FakeRemoterFactory::CreateController(true);
421   controller_->SetClient(this);
422
423   controller_->OnDataSourceInitialized(GURL("http://example.com/foo.m3u8"));
424
425   PipelineMetadata incompatible_metadata;
426   incompatible_metadata.has_video = false;
427   incompatible_metadata.has_audio = false;
428   controller_->OnMetadataChanged(incompatible_metadata);
429   EXPECT_FALSE(is_remote_playback_compatible_);
430
431   // HLS is compatible with RemotePlayback regardless of the metadata we have.
432   controller_->OnHlsManifestDetected();
433   EXPECT_TRUE(is_remote_playback_compatible_);
434 }
435 #endif
436
437 }  // namespace remoting
438 }  // namespace media