OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 "media/remoting/remoting_renderer_controller.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/threading/thread_checker.h" |
| 10 #include "media/remoting/remoting_cdm_context.h" |
| 11 |
| 12 namespace media { |
| 13 |
| 14 RemotingRendererController::RemotingRendererController( |
| 15 scoped_refptr<RemotingSourceImpl> remoting_source) |
| 16 : remoting_source_(remoting_source), weak_factory_(this) { |
| 17 remoting_source_->AddClient(this); |
| 18 } |
| 19 |
| 20 RemotingRendererController::~RemotingRendererController() { |
| 21 DCHECK(thread_checker_.CalledOnValidThread()); |
| 22 remoting_source_->RemoveClient(this); |
| 23 } |
| 24 |
| 25 void RemotingRendererController::OnStarted(bool success) { |
| 26 DCHECK(thread_checker_.CalledOnValidThread()); |
| 27 |
| 28 if (success) { |
| 29 VLOG(1) << "Remoting started successively."; |
| 30 if (remote_rendering_started_) { |
| 31 DCHECK(!switch_renderer_cb_.is_null()); |
| 32 switch_renderer_cb_.Run(); |
| 33 } else { |
| 34 remoting_source_->StopRemoting(this); |
| 35 } |
| 36 } else { |
| 37 VLOG(1) << "Failed to start remoting."; |
| 38 remote_rendering_started_ = false; |
| 39 } |
| 40 } |
| 41 |
| 42 void RemotingRendererController::OnSessionStateChanged() { |
| 43 DCHECK(thread_checker_.CalledOnValidThread()); |
| 44 |
| 45 VLOG(1) << "OnSessionStateChanged: " << remoting_source_->state(); |
| 46 UpdateAndMaybeSwitch(); |
| 47 } |
| 48 |
| 49 void RemotingRendererController::OnEnteredFullscreen() { |
| 50 DCHECK(thread_checker_.CalledOnValidThread()); |
| 51 |
| 52 is_fullscreen_ = true; |
| 53 UpdateAndMaybeSwitch(); |
| 54 } |
| 55 |
| 56 void RemotingRendererController::OnExitedFullscreen() { |
| 57 DCHECK(thread_checker_.CalledOnValidThread()); |
| 58 |
| 59 is_fullscreen_ = false; |
| 60 UpdateAndMaybeSwitch(); |
| 61 } |
| 62 |
| 63 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) { |
| 64 DCHECK(thread_checker_.CalledOnValidThread()); |
| 65 |
| 66 auto* remoting_cdm_context = RemotingCdmContext::From(cdm_context); |
| 67 if (!remoting_cdm_context) |
| 68 return; |
| 69 |
| 70 remoting_source_->RemoveClient(this); |
| 71 remoting_source_ = remoting_cdm_context->GetRemotingSource(); |
| 72 remoting_source_->AddClient(this); // Calls OnSessionStateChanged(). |
| 73 UpdateAndMaybeSwitch(); |
| 74 } |
| 75 |
| 76 void RemotingRendererController::SetSwitchRendererCallback( |
| 77 const base::Closure& cb) { |
| 78 DCHECK(thread_checker_.CalledOnValidThread()); |
| 79 DCHECK(!cb.is_null()); |
| 80 |
| 81 switch_renderer_cb_ = cb; |
| 82 UpdateAndMaybeSwitch(); |
| 83 } |
| 84 |
| 85 base::WeakPtr<remoting::RpcBroker> RemotingRendererController::GetRpcBroker() |
| 86 const { |
| 87 DCHECK(thread_checker_.CalledOnValidThread()); |
| 88 |
| 89 return remoting_source_->GetRpcBroker()->GetWeakPtr(); |
| 90 } |
| 91 |
| 92 void RemotingRendererController::StartDataPipe( |
| 93 std::unique_ptr<mojo::DataPipe> audio_data_pipe, |
| 94 std::unique_ptr<mojo::DataPipe> video_data_pipe, |
| 95 const RemotingSourceImpl::DataPipeStartCallback& done_callback) { |
| 96 DCHECK(thread_checker_.CalledOnValidThread()); |
| 97 |
| 98 remoting_source_->StartDataPipe(std::move(audio_data_pipe), |
| 99 std::move(video_data_pipe), done_callback); |
| 100 } |
| 101 |
| 102 void RemotingRendererController::OnMetadataChanged( |
| 103 const PipelineMetadata& metadata) { |
| 104 DCHECK(thread_checker_.CalledOnValidThread()); |
| 105 |
| 106 has_video_ = |
| 107 metadata.has_video && metadata.video_decoder_config.IsValidConfig(); |
| 108 has_audio_ = |
| 109 metadata.has_audio && metadata.audio_decoder_config.IsValidConfig(); |
| 110 |
| 111 is_encrypted_ = false; |
| 112 if (has_video_) { |
| 113 video_decoder_config_ = metadata.video_decoder_config; |
| 114 is_encrypted_ |= video_decoder_config_.is_encrypted(); |
| 115 } |
| 116 if (has_audio_) { |
| 117 audio_decoder_config_ = metadata.audio_decoder_config; |
| 118 is_encrypted_ |= audio_decoder_config_.is_encrypted(); |
| 119 } |
| 120 UpdateAndMaybeSwitch(); |
| 121 } |
| 122 |
| 123 bool RemotingRendererController::IsVideoCodecSupported() { |
| 124 DCHECK(thread_checker_.CalledOnValidThread()); |
| 125 DCHECK(has_video_); |
| 126 |
| 127 switch (video_decoder_config_.codec()) { |
| 128 case VideoCodec::kCodecH264: |
| 129 case VideoCodec::kCodecVP8: |
| 130 return true; |
| 131 default: |
| 132 VLOG(2) << "Remoting does not support video codec: " |
| 133 << video_decoder_config_.codec(); |
| 134 return false; |
| 135 } |
| 136 } |
| 137 |
| 138 bool RemotingRendererController::IsAudioCodecSupported() { |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); |
| 140 DCHECK(has_audio_); |
| 141 |
| 142 switch (audio_decoder_config_.codec()) { |
| 143 case AudioCodec::kCodecAAC: |
| 144 case AudioCodec::kCodecMP3: |
| 145 case AudioCodec::kCodecPCM: |
| 146 case AudioCodec::kCodecVorbis: |
| 147 case AudioCodec::kCodecFLAC: |
| 148 case AudioCodec::kCodecAMR_NB: |
| 149 case AudioCodec::kCodecAMR_WB: |
| 150 case AudioCodec::kCodecPCM_MULAW: |
| 151 case AudioCodec::kCodecGSM_MS: |
| 152 case AudioCodec::kCodecPCM_S16BE: |
| 153 case AudioCodec::kCodecPCM_S24BE: |
| 154 case AudioCodec::kCodecOpus: |
| 155 case AudioCodec::kCodecEAC3: |
| 156 case AudioCodec::kCodecPCM_ALAW: |
| 157 case AudioCodec::kCodecALAC: |
| 158 case AudioCodec::kCodecAC3: |
| 159 return true; |
| 160 default: |
| 161 VLOG(2) << "Remoting does not support audio codec: " |
| 162 << audio_decoder_config_.codec(); |
| 163 return false; |
| 164 } |
| 165 } |
| 166 |
| 167 bool RemotingRendererController::ShouldBeRemoting() { |
| 168 DCHECK(thread_checker_.CalledOnValidThread()); |
| 169 |
| 170 if (switch_renderer_cb_.is_null()) |
| 171 return false; // No way to switch to a RemotingRenderImpl. |
| 172 |
| 173 const RemotingSessionState state = remoting_source_->state(); |
| 174 if (is_encrypted_) { |
| 175 // Due to technical limitations when playing encrypted content, once a |
| 176 // remoting session has been started, always return true here to indicate |
| 177 // that the RemotingRendererImpl should be used. In the stopped states, |
| 178 // RemotingRendererImpl will display an interstitial to notify the user that |
| 179 // local rendering cannot be resumed. |
| 180 return state == RemotingSessionState::SESSION_STARTED || |
| 181 state == RemotingSessionState::SESSION_STOPPING || |
| 182 state == RemotingSessionState::SESSION_PERMANENTLY_STOPPED; |
| 183 } |
| 184 |
| 185 switch (state) { |
| 186 case SESSION_UNAVAILABLE: |
| 187 return false; // Cannot remote media without a remote sink. |
| 188 case SESSION_CAN_START: |
| 189 case SESSION_STARTING: |
| 190 case SESSION_STARTED: |
| 191 break; // Media remoting is possible, assuming other requirments are met. |
| 192 case SESSION_STOPPING: |
| 193 case SESSION_PERMANENTLY_STOPPED: |
| 194 return false; // Use local rendering after stopping remoting. |
| 195 } |
| 196 if ((!has_audio_ && !has_video_) || |
| 197 (has_video_ && !IsVideoCodecSupported()) || |
| 198 (has_audio_ && !IsAudioCodecSupported())) |
| 199 return false; |
| 200 |
| 201 // Normally, entering fullscreen is the signal that starts remote rendering. |
| 202 // However, current technical limitations require encrypted content be remoted |
| 203 // without waiting for a user signal. |
| 204 return is_fullscreen_; |
| 205 } |
| 206 |
| 207 void RemotingRendererController::UpdateAndMaybeSwitch() { |
| 208 DCHECK(thread_checker_.CalledOnValidThread()); |
| 209 |
| 210 bool should_be_remoting = ShouldBeRemoting(); |
| 211 |
| 212 if (remote_rendering_started_ == should_be_remoting) |
| 213 return; |
| 214 |
| 215 // Switch between local renderer and remoting renderer. |
| 216 remote_rendering_started_ = should_be_remoting; |
| 217 |
| 218 if (remote_rendering_started_) { |
| 219 DCHECK(!switch_renderer_cb_.is_null()); |
| 220 if (remoting_source_->state() == |
| 221 RemotingSessionState::SESSION_PERMANENTLY_STOPPED) { |
| 222 switch_renderer_cb_.Run(); |
| 223 return; |
| 224 } |
| 225 // |switch_renderer_cb_.Run()| will be called after remoting is started |
| 226 // successfully. |
| 227 remoting_source_->StartRemoting(this); |
| 228 } else { |
| 229 // For encrypted content, it's only valid to switch to remoting renderer, |
| 230 // and never back to the local renderer. The RemotingCdmController will |
| 231 // force-stop the session when remoting has ended; so no need to call |
| 232 // StopRemoting() from here. |
| 233 DCHECK(!is_encrypted_); |
| 234 switch_renderer_cb_.Run(); |
| 235 remoting_source_->StopRemoting(this); |
| 236 } |
| 237 } |
| 238 |
| 239 } // namespace media |
OLD | NEW |