blob: 456a34e6e338eaec3fed3b13c28a2c5946324fc8 [file] [log] [blame]
Thomas Guilbert130188c172018-04-18 20:10:181// Copyright 2018 The Chromium Authors. All rights reserved.
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 "content/browser/media/flinging_renderer.h"
6
Thomas Guilbert69c2be862019-04-26 20:09:387#include <utility>
8
Thomas Guilbert130188c172018-04-18 20:10:189#include "base/memory/ptr_util.h"
danakj30f5b7dd2020-09-16 15:38:4510#include "content/browser/renderer_host/render_frame_host_delegate.h"
11#include "content/browser/renderer_host/render_frame_host_impl.h"
Thomas Guilbert130188c172018-04-18 20:10:1812#include "content/public/browser/content_browser_client.h"
13#include "content/public/browser/presentation_service_delegate.h"
14#include "content/public/browser/render_frame_host.h"
15#include "content/public/common/content_client.h"
16
17namespace content {
18
Thomas Guilbert2784a2c62018-07-12 20:02:4019FlingingRenderer::FlingingRenderer(
Thomas Guilbert69c2be862019-04-26 20:09:3820 std::unique_ptr<media::FlingingController> controller,
Gyuyoung Kim4da45e42019-10-28 03:40:4221 mojo::PendingRemote<ClientExtension> client_extension)
Thomas Guilbert69c2be862019-04-26 20:09:3822 : client_extension_(std::move(client_extension)),
23 controller_(std::move(controller)) {
Thomas Guilbertb2fd9092018-08-15 01:00:3924 controller_->AddMediaStatusObserver(this);
25}
Thomas Guilbert130188c172018-04-18 20:10:1826
Thomas Guilbertb2fd9092018-08-15 01:00:3927FlingingRenderer::~FlingingRenderer() {
28 controller_->RemoveMediaStatusObserver(this);
Nico Weber40cd26fd2019-02-09 17:37:0229}
Thomas Guilbert130188c172018-04-18 20:10:1830
31// static
32std::unique_ptr<FlingingRenderer> FlingingRenderer::Create(
33 RenderFrameHost* render_frame_host,
Thomas Guilbert69c2be862019-04-26 20:09:3834 const std::string& presentation_id,
Gyuyoung Kim4da45e42019-10-28 03:40:4235 mojo::PendingRemote<ClientExtension> client_extension) {
Thomas Guilbert130188c172018-04-18 20:10:1836 DVLOG(1) << __func__;
37
38 ContentClient* content_client = GetContentClient();
39 if (!content_client)
40 return nullptr;
41
42 ContentBrowserClient* browser_client = content_client->browser();
43 if (!browser_client)
44 return nullptr;
45
46 ControllerPresentationServiceDelegate* presentation_delegate =
47 browser_client->GetControllerPresentationServiceDelegate(
48 static_cast<RenderFrameHostImpl*>(render_frame_host)
49 ->delegate()
50 ->GetAsWebContents());
51
52 if (!presentation_delegate)
53 return nullptr;
54
Thomas Guilbertfc4431b2018-07-20 19:05:4755 auto flinging_controller = presentation_delegate->GetFlingingController(
Thomas Guilbert130188c172018-04-18 20:10:1856 render_frame_host->GetProcess()->GetID(),
57 render_frame_host->GetRoutingID(), presentation_id);
58
Thomas Guilbertfc4431b2018-07-20 19:05:4759 if (!flinging_controller)
Thomas Guilbert130188c172018-04-18 20:10:1860 return nullptr;
61
Thomas Guilbert69c2be862019-04-26 20:09:3862 return base::WrapUnique<FlingingRenderer>(new FlingingRenderer(
63 std::move(flinging_controller), std::move(client_extension)));
Thomas Guilbert130188c172018-04-18 20:10:1864}
65
66// media::Renderer implementation
67void FlingingRenderer::Initialize(media::MediaResource* media_resource,
68 media::RendererClient* client,
Chris Mumford84f095f2019-10-25 23:19:4469 media::PipelineStatusCallback init_cb) {
Thomas Guilbert130188c172018-04-18 20:10:1870 DVLOG(2) << __func__;
Thomas Guilbertd9fd6f562018-08-25 00:57:3171 client_ = client;
Chris Mumford84f095f2019-10-25 23:19:4472 std::move(init_cb).Run(media::PIPELINE_OK);
Thomas Guilbert130188c172018-04-18 20:10:1873}
74
Chris Cunninghame8c626332019-11-20 23:34:2775void FlingingRenderer::SetLatencyHint(
76 base::Optional<base::TimeDelta> latency_hint) {}
77
Chris Mumford1ccf4512019-10-25 19:34:0578void FlingingRenderer::Flush(base::OnceClosure flush_cb) {
Thomas Guilbert130188c172018-04-18 20:10:1879 DVLOG(2) << __func__;
80 // There is nothing to reset, we can no-op the call.
Chris Mumford1ccf4512019-10-25 19:34:0581 std::move(flush_cb).Run();
Thomas Guilbert130188c172018-04-18 20:10:1882}
83
84void FlingingRenderer::StartPlayingFrom(base::TimeDelta time) {
85 DVLOG(2) << __func__;
Thomas Guilbertfc4431b2018-07-20 19:05:4786 controller_->GetMediaController()->Seek(time);
Thomas Guilbertd9fd6f562018-08-25 00:57:3187
88 // After a seek when using the FlingingRenderer, WMPI will never get back to
89 // BUFFERING_HAVE_ENOUGH. This prevents Blink from getting the appropriate
90 // seek completion signals, and time updates are never re-scheduled.
91 //
92 // The FlingingRenderer doesn't need to buffer, since playback happens on a
93 // different device. This means it's ok to always send BUFFERING_HAVE_ENOUGH
94 // when sending buffering state changes. That being said, sending state
95 // changes here might be surprising, but the same signals are sent from
96 // MediaPlayerRenderer::StartPlayingFrom(), and it has been working mostly
97 // smoothly for all HLS playback.
Chris Cunninghamfc0d67e2019-07-22 20:29:1698 client_->OnBufferingStateChange(media::BUFFERING_HAVE_ENOUGH,
99 media::BUFFERING_CHANGE_REASON_UNKNOWN);
Thomas Guilbert130188c172018-04-18 20:10:18100}
101
102void FlingingRenderer::SetPlaybackRate(double playback_rate) {
103 DVLOG(2) << __func__;
Thomas Guilbert9f575d22019-01-25 20:51:15104 if (playback_rate == 0) {
Thomas Guilbert1ed6ada2019-09-14 00:23:35105 SetExpectedPlayState(PlayState::PAUSED);
Thomas Guilbertfc4431b2018-07-20 19:05:47106 controller_->GetMediaController()->Pause();
Thomas Guilbert9f575d22019-01-25 20:51:15107 } else {
Thomas Guilbert1ed6ada2019-09-14 00:23:35108 SetExpectedPlayState(PlayState::PLAYING);
Thomas Guilbertfc4431b2018-07-20 19:05:47109 controller_->GetMediaController()->Play();
Thomas Guilbert9f575d22019-01-25 20:51:15110 }
Thomas Guilbert130188c172018-04-18 20:10:18111}
112
113void FlingingRenderer::SetVolume(float volume) {
114 DVLOG(2) << __func__;
Thomas Guilbertfc4431b2018-07-20 19:05:47115 controller_->GetMediaController()->SetVolume(volume);
Thomas Guilbert130188c172018-04-18 20:10:18116}
117
118base::TimeDelta FlingingRenderer::GetMediaTime() {
Thomas Guilbert8a6d66b92018-08-23 23:33:29119 return controller_->GetApproximateCurrentTime();
Thomas Guilbert130188c172018-04-18 20:10:18120}
121
Thomas Guilbert1ed6ada2019-09-14 00:23:35122void FlingingRenderer::SetExpectedPlayState(PlayState state) {
Thomas Guilbert9f575d22019-01-25 20:51:15123 DVLOG(3) << __func__ << " : state " << static_cast<int>(state);
124 DCHECK(state == PlayState::PLAYING || state == PlayState::PAUSED);
Thomas Guilbert1ed6ada2019-09-14 00:23:35125
126 expected_play_state_ = state;
127 play_state_is_stable_ = (expected_play_state_ == last_play_state_received_);
Thomas Guilbert9f575d22019-01-25 20:51:15128}
129
Thomas Guilbertb2fd9092018-08-15 01:00:39130void FlingingRenderer::OnMediaStatusUpdated(const media::MediaStatus& status) {
Thomas Guilbert9f575d22019-01-25 20:51:15131 const auto& current_state = status.state;
132
Thomas Guilbert1ed6ada2019-09-14 00:23:35133 if (current_state == expected_play_state_)
134 play_state_is_stable_ = true;
Thomas Guilbert9f575d22019-01-25 20:51:15135
136 // Because we can get a MediaStatus update at any time from the device, only
137 // handle state updates after we have reached the target state.
138 // If we do not, we can encounter the following scenario:
139 // - A user pauses the video.
140 // - While the PAUSE command is in flight, an unrelated MediaStatus with a
141 // PLAYING state is sent from the cast device.
142 // - We call OnRemotePlaybackStateChange(PLAYING).
143 // - As the PAUSE command completes and we receive a PlayState::PAUSE, we
144 // queue a new PLAYING.
145 // - The local device enters a tick/tock feedback loop of constantly
146 // requesting the wrong state of PLAYING/PAUSED.
Thomas Guilbert1ed6ada2019-09-14 00:23:35147 if (!play_state_is_stable_)
Thomas Guilbert9f575d22019-01-25 20:51:15148 return;
149
150 // Ignore all non PLAYING/PAUSED states.
151 // UNKNOWN and BUFFERING states are uninteresting and can be safely ignored.
152 // STOPPED normally causes the session to teardown, and |this| is destroyed
153 // shortly after.
154 if (current_state != PlayState::PLAYING &&
155 current_state != PlayState::PAUSED) {
156 DVLOG(3) << __func__ << " : external state ignored: "
157 << static_cast<int>(current_state);
158 return;
159 }
160
Thomas Guilbert1ed6ada2019-09-14 00:23:35161 // Save whether the remote device is currently playing or paused.
162 last_play_state_received_ = current_state;
163
164 // If the remote device's play state has toggled and we didn't initiate it,
165 // notify WMPI to update it's own play/pause state.
166 if (last_play_state_received_ != expected_play_state_)
Thomas Guilbert69c2be862019-04-26 20:09:38167 client_extension_->OnRemotePlayStateChange(current_state);
Thomas Guilbertb2fd9092018-08-15 01:00:39168}
169
Thomas Guilbert130188c172018-04-18 20:10:18170} // namespace content