1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/flinging_renderer.h"
#include <utility>
#include "base/memory/ptr_util.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/presentation_service_delegate.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
namespace content {
FlingingRenderer::FlingingRenderer(
std::unique_ptr<media::FlingingController> controller,
mojo::PendingRemote<ClientExtension> client_extension)
: client_extension_(std::move(client_extension)),
controller_(std::move(controller)) {
controller_->AddMediaStatusObserver(this);
}
FlingingRenderer::~FlingingRenderer() {
controller_->RemoveMediaStatusObserver(this);
}
// static
std::unique_ptr<FlingingRenderer> FlingingRenderer::Create(
RenderFrameHost* render_frame_host,
const std::string& presentation_id,
mojo::PendingRemote<ClientExtension> client_extension) {
DVLOG(1) << __func__;
ContentClient* content_client = GetContentClient();
if (!content_client)
return nullptr;
ContentBrowserClient* browser_client = content_client->browser();
if (!browser_client)
return nullptr;
ControllerPresentationServiceDelegate* presentation_delegate =
browser_client->GetControllerPresentationServiceDelegate(
WebContents::FromRenderFrameHost(render_frame_host));
if (!presentation_delegate)
return nullptr;
auto flinging_controller = presentation_delegate->GetFlingingController(
render_frame_host->GetProcess()->GetDeprecatedID(),
render_frame_host->GetRoutingID(), presentation_id);
if (!flinging_controller)
return nullptr;
return base::WrapUnique<FlingingRenderer>(new FlingingRenderer(
std::move(flinging_controller), std::move(client_extension)));
}
// media::Renderer implementation
void FlingingRenderer::Initialize(media::MediaResource* media_resource,
media::RendererClient* client,
media::PipelineStatusCallback init_cb) {
DVLOG(2) << __func__;
client_ = client;
std::move(init_cb).Run(media::PIPELINE_OK);
}
void FlingingRenderer::SetLatencyHint(
std::optional<base::TimeDelta> latency_hint) {}
void FlingingRenderer::Flush(base::OnceClosure flush_cb) {
DVLOG(2) << __func__;
// There is nothing to reset, we can no-op the call.
std::move(flush_cb).Run();
}
void FlingingRenderer::StartPlayingFrom(base::TimeDelta time) {
DVLOG(2) << __func__;
controller_->GetMediaController()->Seek(time);
// After a seek when using the FlingingRenderer, WMPI will never get back to
// BUFFERING_HAVE_ENOUGH. This prevents Blink from getting the appropriate
// seek completion signals, and time updates are never re-scheduled.
//
// The FlingingRenderer doesn't need to buffer, since playback happens on a
// different device. This means it's ok to always send BUFFERING_HAVE_ENOUGH
// when sending buffering state changes.
client_->OnBufferingStateChange(media::BUFFERING_HAVE_ENOUGH,
media::BUFFERING_CHANGE_REASON_UNKNOWN);
}
void FlingingRenderer::SetPlaybackRate(double playback_rate) {
DVLOG(2) << __func__;
if (playback_rate == 0) {
SetExpectedPlayState(PlayState::kPaused);
controller_->GetMediaController()->Pause();
} else {
SetExpectedPlayState(PlayState::kPlaying);
controller_->GetMediaController()->Play();
}
}
void FlingingRenderer::SetVolume(float volume) {
DVLOG(2) << __func__;
controller_->GetMediaController()->SetVolume(volume);
}
base::TimeDelta FlingingRenderer::GetMediaTime() {
return controller_->GetApproximateCurrentTime();
}
media::RendererType FlingingRenderer::GetRendererType() {
return media::RendererType::kFlinging;
}
void FlingingRenderer::SetExpectedPlayState(PlayState state) {
DVLOG(3) << __func__ << " : state " << static_cast<int>(state);
DCHECK(state == PlayState::kPlaying || state == PlayState::kPaused);
expected_play_state_ = state;
play_state_is_stable_ = (expected_play_state_ == last_play_state_received_);
}
void FlingingRenderer::OnMediaStatusUpdated(const media::MediaStatus& status) {
const auto& current_state = status.state;
if (current_state == expected_play_state_)
play_state_is_stable_ = true;
// Because we can get a MediaStatus update at any time from the device, only
// handle state updates after we have reached the target state.
// If we do not, we can encounter the following scenario:
// - A user pauses the video.
// - While the PAUSE command is in flight, an unrelated MediaStatus with a
// PLAYING state is sent from the cast device.
// - We call OnRemotePlaybackStateChange(PLAYING).
// - As the PAUSE command completes and we receive a PlayState::PAUSE, we
// queue a new PLAYING.
// - The local device enters a tick/tock feedback loop of constantly
// requesting the wrong state of PLAYING/PAUSED.
if (!play_state_is_stable_)
return;
// Ignore all non PLAYING/PAUSED states.
// UNKNOWN and BUFFERING states are uninteresting and can be safely ignored.
// STOPPED normally causes the session to teardown, and |this| is destroyed
// shortly after.
if (current_state != PlayState::kPlaying &&
current_state != PlayState::kPaused) {
DVLOG(3) << __func__ << " : external state ignored: "
<< static_cast<int>(current_state);
return;
}
// Save whether the remote device is currently playing or paused.
last_play_state_received_ = current_state;
// If the remote device's play state has toggled and we didn't initiate it,
// notify WMPI to update it's own play/pause state.
if (last_play_state_received_ != expected_play_state_)
client_extension_->OnRemotePlayStateChange(current_state);
}
} // namespace content
|