| OLD | NEW | 
|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "media/remoting/remoting_renderer_controller.h" | 5 #include "media/remoting/renderer_controller.h" | 
| 6 | 6 | 
| 7 #include "base/bind.h" | 7 #include "base/bind.h" | 
| 8 #include "base/logging.h" | 8 #include "base/logging.h" | 
| 9 #include "base/threading/thread_checker.h" | 9 #include "base/threading/thread_checker.h" | 
| 10 #include "base/time/time.h" | 10 #include "base/time/time.h" | 
|  | 11 #include "media/remoting/remoting_cdm.h" | 
| 11 #include "media/remoting/remoting_cdm_context.h" | 12 #include "media/remoting/remoting_cdm_context.h" | 
| 12 | 13 | 
| 13 namespace media { | 14 namespace media { | 
|  | 15 namespace remoting { | 
| 14 | 16 | 
| 15 RemotingRendererController::RemotingRendererController( | 17 RendererController::RendererController(scoped_refptr<SharedSession> session) | 
| 16     scoped_refptr<RemotingSourceImpl> remoting_source) | 18     : session_(std::move(session)), weak_factory_(this) { | 
| 17     : remoting_source_(remoting_source), weak_factory_(this) { | 19   session_->AddClient(this); | 
| 18   remoting_source_->AddClient(this); |  | 
| 19 } | 20 } | 
| 20 | 21 | 
| 21 RemotingRendererController::~RemotingRendererController() { | 22 RendererController::~RendererController() { | 
| 22   DCHECK(thread_checker_.CalledOnValidThread()); | 23   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 23   metrics_recorder_.WillStopSession(remoting::MEDIA_ELEMENT_DESTROYED); | 24   metrics_recorder_.WillStopSession(MEDIA_ELEMENT_DESTROYED); | 
| 24   remoting_source_->RemoveClient(this); | 25   session_->RemoveClient(this); | 
| 25 } | 26 } | 
| 26 | 27 | 
| 27 void RemotingRendererController::OnStarted(bool success) { | 28 void RendererController::OnStarted(bool success) { | 
| 28   DCHECK(thread_checker_.CalledOnValidThread()); | 29   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 29 | 30 | 
| 30   if (success) { | 31   if (success) { | 
| 31     VLOG(1) << "Remoting started successively."; | 32     VLOG(1) << "Remoting started successively."; | 
| 32     if (remote_rendering_started_) { | 33     if (remote_rendering_started_) { | 
| 33       metrics_recorder_.DidStartSession(); | 34       metrics_recorder_.DidStartSession(); | 
| 34       DCHECK(!switch_renderer_cb_.is_null()); | 35       DCHECK(!switch_renderer_cb_.is_null()); | 
| 35       switch_renderer_cb_.Run(); | 36       switch_renderer_cb_.Run(); | 
| 36     } else { | 37     } else { | 
| 37       remoting_source_->StopRemoting(this); | 38       session_->StopRemoting(this); | 
| 38     } | 39     } | 
| 39   } else { | 40   } else { | 
| 40     VLOG(1) << "Failed to start remoting."; | 41     VLOG(1) << "Failed to start remoting."; | 
| 41     remote_rendering_started_ = false; | 42     remote_rendering_started_ = false; | 
| 42     metrics_recorder_.WillStopSession(remoting::START_RACE); | 43     metrics_recorder_.WillStopSession(START_RACE); | 
| 43   } | 44   } | 
| 44 } | 45 } | 
| 45 | 46 | 
| 46 void RemotingRendererController::OnSessionStateChanged() { | 47 void RendererController::OnSessionStateChanged() { | 
| 47   DCHECK(thread_checker_.CalledOnValidThread()); | 48   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 48   UpdateFromSessionState(remoting::SINK_AVAILABLE, remoting::ROUTE_TERMINATED); | 49   UpdateFromSessionState(SINK_AVAILABLE, ROUTE_TERMINATED); | 
| 49 } | 50 } | 
| 50 | 51 | 
| 51 void RemotingRendererController::UpdateFromSessionState( | 52 void RendererController::UpdateFromSessionState(StartTrigger start_trigger, | 
| 52     remoting::StartTrigger start_trigger, | 53                                                 StopTrigger stop_trigger) { | 
| 53     remoting::StopTrigger stop_trigger) { | 54   VLOG(1) << "UpdateFromSessionState: " << session_->state(); | 
| 54   VLOG(1) << "UpdateFromSessionState: " << remoting_source_->state(); |  | 
| 55   if (!sink_available_changed_cb_.is_null()) | 55   if (!sink_available_changed_cb_.is_null()) | 
| 56     sink_available_changed_cb_.Run(IsRemoteSinkAvailable()); | 56     sink_available_changed_cb_.Run(IsRemoteSinkAvailable()); | 
| 57 | 57 | 
| 58   UpdateInterstitial(base::nullopt); | 58   UpdateInterstitial(base::nullopt); | 
| 59   UpdateAndMaybeSwitch(start_trigger, stop_trigger); | 59   UpdateAndMaybeSwitch(start_trigger, stop_trigger); | 
| 60 } | 60 } | 
| 61 | 61 | 
| 62 bool RemotingRendererController::IsRemoteSinkAvailable() { | 62 bool RendererController::IsRemoteSinkAvailable() { | 
| 63   DCHECK(thread_checker_.CalledOnValidThread()); | 63   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 64 | 64 | 
| 65   switch (remoting_source_->state()) { | 65   switch (session_->state()) { | 
| 66     case SESSION_CAN_START: | 66     case SharedSession::SESSION_CAN_START: | 
| 67     case SESSION_STARTING: | 67     case SharedSession::SESSION_STARTING: | 
| 68     case SESSION_STARTED: | 68     case SharedSession::SESSION_STARTED: | 
| 69       return true; | 69       return true; | 
| 70     case SESSION_UNAVAILABLE: | 70     case SharedSession::SESSION_UNAVAILABLE: | 
| 71     case SESSION_STOPPING: | 71     case SharedSession::SESSION_STOPPING: | 
| 72     case SESSION_PERMANENTLY_STOPPED: | 72     case SharedSession::SESSION_PERMANENTLY_STOPPED: | 
| 73       return false; | 73       return false; | 
| 74   } | 74   } | 
| 75 | 75 | 
| 76   return false;  // To suppress compile warning. | 76   NOTREACHED(); | 
|  | 77   return false;  // To suppress compiler warning on Windows. | 
| 77 } | 78 } | 
| 78 | 79 | 
| 79 void RemotingRendererController::OnEnteredFullscreen() { | 80 void RendererController::OnEnteredFullscreen() { | 
| 80   DCHECK(thread_checker_.CalledOnValidThread()); | 81   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 81 | 82 | 
| 82   is_fullscreen_ = true; | 83   is_fullscreen_ = true; | 
| 83   // See notes in OnBecameDominantVisibleContent() for why this is forced: | 84   // See notes in OnBecameDominantVisibleContent() for why this is forced: | 
| 84   is_dominant_content_ = true; | 85   is_dominant_content_ = true; | 
| 85   UpdateAndMaybeSwitch(remoting::ENTERED_FULLSCREEN, | 86   UpdateAndMaybeSwitch(ENTERED_FULLSCREEN, UNKNOWN_STOP_TRIGGER); | 
| 86                        remoting::UNKNOWN_STOP_TRIGGER); |  | 
| 87 } | 87 } | 
| 88 | 88 | 
| 89 void RemotingRendererController::OnExitedFullscreen() { | 89 void RendererController::OnExitedFullscreen() { | 
| 90   DCHECK(thread_checker_.CalledOnValidThread()); | 90   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 91 | 91 | 
| 92   is_fullscreen_ = false; | 92   is_fullscreen_ = false; | 
| 93   // See notes in OnBecameDominantVisibleContent() for why this is forced: | 93   // See notes in OnBecameDominantVisibleContent() for why this is forced: | 
| 94   is_dominant_content_ = false; | 94   is_dominant_content_ = false; | 
| 95   UpdateAndMaybeSwitch(remoting::UNKNOWN_START_TRIGGER, | 95   UpdateAndMaybeSwitch(UNKNOWN_START_TRIGGER, EXITED_FULLSCREEN); | 
| 96                        remoting::EXITED_FULLSCREEN); |  | 
| 97 } | 96 } | 
| 98 | 97 | 
| 99 void RemotingRendererController::OnBecameDominantVisibleContent( | 98 void RendererController::OnBecameDominantVisibleContent(bool is_dominant) { | 
| 100     bool is_dominant) { |  | 
| 101   DCHECK(thread_checker_.CalledOnValidThread()); | 99   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 102 | 100 | 
| 103   // Two scenarios where "dominance" status mixes with fullscreen transitions: | 101   // Two scenarios where "dominance" status mixes with fullscreen transitions: | 
| 104   // | 102   // | 
| 105   //   1. Just before/after entering fullscreen, the element will, of course, | 103   //   1. Just before/after entering fullscreen, the element will, of course, | 
| 106   //      become the dominant on-screen content via automatic page layout. | 104   //      become the dominant on-screen content via automatic page layout. | 
| 107   //   2. Just before/after exiting fullscreen, the element may or may not | 105   //   2. Just before/after exiting fullscreen, the element may or may not | 
| 108   //      shrink in size enough to become non-dominant. However, exiting | 106   //      shrink in size enough to become non-dominant. However, exiting | 
| 109   //      fullscreen was caused by a user action that explicitly indicates a | 107   //      fullscreen was caused by a user action that explicitly indicates a | 
| 110   //      desire to exit remoting, so even if the element is still dominant, | 108   //      desire to exit remoting, so even if the element is still dominant, | 
| 111   //      remoting should be shut down. | 109   //      remoting should be shut down. | 
| 112   // | 110   // | 
| 113   // Thus, to achieve the desired behaviors, |is_dominant_content_| is force-set | 111   // Thus, to achieve the desired behaviors, |is_dominant_content_| is force-set | 
| 114   // in OnEnteredFullscreen() and OnExitedFullscreen(), and changes to it here | 112   // in OnEnteredFullscreen() and OnExitedFullscreen(), and changes to it here | 
| 115   // are ignored while in fullscreen. | 113   // are ignored while in fullscreen. | 
| 116   if (is_fullscreen_) | 114   if (is_fullscreen_) | 
| 117     return; | 115     return; | 
| 118 | 116 | 
| 119   is_dominant_content_ = is_dominant; | 117   is_dominant_content_ = is_dominant; | 
| 120   UpdateAndMaybeSwitch(remoting::BECAME_DOMINANT_CONTENT, | 118   UpdateAndMaybeSwitch(BECAME_DOMINANT_CONTENT, BECAME_AUXILIARY_CONTENT); | 
| 121                        remoting::BECAME_AUXILIARY_CONTENT); |  | 
| 122 } | 119 } | 
| 123 | 120 | 
| 124 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) { | 121 void RendererController::OnSetCdm(CdmContext* cdm_context) { | 
| 125   DCHECK(thread_checker_.CalledOnValidThread()); | 122   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 126 | 123 | 
| 127   auto* remoting_cdm_context = RemotingCdmContext::From(cdm_context); | 124   auto* remoting_cdm_context = RemotingCdmContext::From(cdm_context); | 
| 128   if (!remoting_cdm_context) | 125   if (!remoting_cdm_context) | 
| 129     return; | 126     return; | 
| 130 | 127 | 
| 131   remoting_source_->RemoveClient(this); | 128   session_->RemoveClient(this); | 
| 132   remoting_source_ = remoting_cdm_context->GetRemotingSource(); | 129   session_ = remoting_cdm_context->GetSharedSession(); | 
| 133   remoting_source_->AddClient(this); | 130   session_->AddClient(this); | 
| 134   UpdateFromSessionState(remoting::CDM_READY, remoting::DECRYPTION_ERROR); | 131   UpdateFromSessionState(CDM_READY, DECRYPTION_ERROR); | 
| 135 } | 132 } | 
| 136 | 133 | 
| 137 void RemotingRendererController::OnRemotePlaybackDisabled(bool disabled) { | 134 void RendererController::OnRemotePlaybackDisabled(bool disabled) { | 
| 138   DCHECK(thread_checker_.CalledOnValidThread()); | 135   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 139 | 136 | 
| 140   is_remote_playback_disabled_ = disabled; | 137   is_remote_playback_disabled_ = disabled; | 
| 141   metrics_recorder_.OnRemotePlaybackDisabled(disabled); | 138   metrics_recorder_.OnRemotePlaybackDisabled(disabled); | 
| 142   UpdateAndMaybeSwitch(remoting::ENABLED_BY_PAGE, remoting::DISABLED_BY_PAGE); | 139   UpdateAndMaybeSwitch(ENABLED_BY_PAGE, DISABLED_BY_PAGE); | 
| 143 } | 140 } | 
| 144 | 141 | 
| 145 void RemotingRendererController::OnSetPoster(const GURL& poster_url) { | 142 void RendererController::OnSetPoster(const GURL& poster_url) { | 
| 146   DCHECK(thread_checker_.CalledOnValidThread()); | 143   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 147 | 144 | 
| 148   if (poster_url != poster_url_) { | 145   if (poster_url != poster_url_) { | 
| 149     poster_url_ = poster_url; | 146     poster_url_ = poster_url; | 
| 150     if (poster_url_.is_empty()) | 147     if (poster_url_.is_empty()) | 
| 151       UpdateInterstitial(SkBitmap()); | 148       UpdateInterstitial(SkBitmap()); | 
| 152     else | 149     else | 
| 153       DownloadPosterImage(); | 150       DownloadPosterImage(); | 
| 154   } | 151   } | 
| 155 } | 152 } | 
| 156 | 153 | 
| 157 void RemotingRendererController::SetSwitchRendererCallback( | 154 void RendererController::SetSwitchRendererCallback(const base::Closure& cb) { | 
| 158     const base::Closure& cb) { |  | 
| 159   DCHECK(thread_checker_.CalledOnValidThread()); | 155   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 160   DCHECK(!cb.is_null()); | 156   DCHECK(!cb.is_null()); | 
| 161 | 157 | 
| 162   switch_renderer_cb_ = cb; | 158   switch_renderer_cb_ = cb; | 
| 163   // Note: Not calling UpdateAndMaybeSwitch() here since this method should be | 159   // Note: Not calling UpdateAndMaybeSwitch() here since this method should be | 
| 164   // called during the initialization phase of this RemotingRendererController; | 160   // called during the initialization phase of this RendererController; | 
| 165   // and definitely before a whole lot of other things that are needed to | 161   // and definitely before a whole lot of other things that are needed to | 
| 166   // trigger a switch. | 162   // trigger a switch. | 
| 167 } | 163 } | 
| 168 | 164 | 
| 169 void RemotingRendererController::SetRemoteSinkAvailableChangedCallback( | 165 void RendererController::SetRemoteSinkAvailableChangedCallback( | 
| 170     const base::Callback<void(bool)>& cb) { | 166     const base::Callback<void(bool)>& cb) { | 
| 171   DCHECK(thread_checker_.CalledOnValidThread()); | 167   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 172 | 168 | 
| 173   sink_available_changed_cb_ = cb; | 169   sink_available_changed_cb_ = cb; | 
| 174   if (!sink_available_changed_cb_.is_null()) | 170   if (!sink_available_changed_cb_.is_null()) | 
| 175     sink_available_changed_cb_.Run(IsRemoteSinkAvailable()); | 171     sink_available_changed_cb_.Run(IsRemoteSinkAvailable()); | 
| 176 } | 172 } | 
| 177 | 173 | 
| 178 base::WeakPtr<remoting::RpcBroker> RemotingRendererController::GetRpcBroker() | 174 base::WeakPtr<RpcBroker> RendererController::GetRpcBroker() const { | 
| 179     const { |  | 
| 180   DCHECK(thread_checker_.CalledOnValidThread()); | 175   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 181 | 176 | 
| 182   return remoting_source_->GetRpcBroker()->GetWeakPtr(); | 177   return session_->rpc_broker()->GetWeakPtr(); | 
| 183 } | 178 } | 
| 184 | 179 | 
| 185 void RemotingRendererController::StartDataPipe( | 180 void RendererController::StartDataPipe( | 
| 186     std::unique_ptr<mojo::DataPipe> audio_data_pipe, | 181     std::unique_ptr<mojo::DataPipe> audio_data_pipe, | 
| 187     std::unique_ptr<mojo::DataPipe> video_data_pipe, | 182     std::unique_ptr<mojo::DataPipe> video_data_pipe, | 
| 188     const RemotingSourceImpl::DataPipeStartCallback& done_callback) { | 183     const SharedSession::DataPipeStartCallback& done_callback) { | 
| 189   DCHECK(thread_checker_.CalledOnValidThread()); | 184   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 190 | 185 | 
| 191   remoting_source_->StartDataPipe(std::move(audio_data_pipe), | 186   session_->StartDataPipe(std::move(audio_data_pipe), | 
| 192                                   std::move(video_data_pipe), done_callback); | 187                           std::move(video_data_pipe), done_callback); | 
| 193 } | 188 } | 
| 194 | 189 | 
| 195 void RemotingRendererController::OnMetadataChanged( | 190 void RendererController::OnMetadataChanged(const PipelineMetadata& metadata) { | 
| 196     const PipelineMetadata& metadata) { |  | 
| 197   DCHECK(thread_checker_.CalledOnValidThread()); | 191   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 198 | 192 | 
| 199   const gfx::Size old_size = pipeline_metadata_.natural_size; | 193   const gfx::Size old_size = pipeline_metadata_.natural_size; | 
| 200   const bool was_audio_codec_supported = has_audio() && IsAudioCodecSupported(); | 194   const bool was_audio_codec_supported = has_audio() && IsAudioCodecSupported(); | 
| 201   const bool was_video_codec_supported = has_video() && IsVideoCodecSupported(); | 195   const bool was_video_codec_supported = has_video() && IsVideoCodecSupported(); | 
| 202   pipeline_metadata_ = metadata; | 196   pipeline_metadata_ = metadata; | 
| 203   const bool is_audio_codec_supported = has_audio() && IsAudioCodecSupported(); | 197   const bool is_audio_codec_supported = has_audio() && IsAudioCodecSupported(); | 
| 204   const bool is_video_codec_supported = has_video() && IsVideoCodecSupported(); | 198   const bool is_video_codec_supported = has_video() && IsVideoCodecSupported(); | 
| 205   metrics_recorder_.OnPipelineMetadataChanged(metadata); | 199   metrics_recorder_.OnPipelineMetadataChanged(metadata); | 
| 206 | 200 | 
| 207   is_encrypted_ = false; | 201   is_encrypted_ = false; | 
| 208   if (has_video()) | 202   if (has_video()) | 
| 209     is_encrypted_ |= metadata.video_decoder_config.is_encrypted(); | 203     is_encrypted_ |= metadata.video_decoder_config.is_encrypted(); | 
| 210   if (has_audio()) | 204   if (has_audio()) | 
| 211     is_encrypted_ |= metadata.audio_decoder_config.is_encrypted(); | 205     is_encrypted_ |= metadata.audio_decoder_config.is_encrypted(); | 
| 212 | 206 | 
| 213   if (pipeline_metadata_.natural_size != old_size) | 207   if (pipeline_metadata_.natural_size != old_size) | 
| 214     UpdateInterstitial(base::nullopt); | 208     UpdateInterstitial(base::nullopt); | 
| 215 | 209 | 
| 216   remoting::StartTrigger start_trigger = remoting::UNKNOWN_START_TRIGGER; | 210   StartTrigger start_trigger = UNKNOWN_START_TRIGGER; | 
| 217   if (!was_audio_codec_supported && is_audio_codec_supported) | 211   if (!was_audio_codec_supported && is_audio_codec_supported) | 
| 218     start_trigger = remoting::SUPPORTED_AUDIO_CODEC; | 212     start_trigger = SUPPORTED_AUDIO_CODEC; | 
| 219   if (!was_video_codec_supported && is_video_codec_supported) { | 213   if (!was_video_codec_supported && is_video_codec_supported) { | 
| 220     start_trigger = start_trigger == remoting::SUPPORTED_AUDIO_CODEC | 214     start_trigger = start_trigger == SUPPORTED_AUDIO_CODEC | 
| 221                         ? remoting::SUPPORTED_AUDIO_AND_VIDEO_CODECS | 215                         ? SUPPORTED_AUDIO_AND_VIDEO_CODECS | 
| 222                         : remoting::SUPPORTED_VIDEO_CODEC; | 216                         : SUPPORTED_VIDEO_CODEC; | 
| 223   } | 217   } | 
| 224   remoting::StopTrigger stop_trigger = remoting::UNKNOWN_STOP_TRIGGER; | 218   StopTrigger stop_trigger = UNKNOWN_STOP_TRIGGER; | 
| 225   if (was_audio_codec_supported && !is_audio_codec_supported) | 219   if (was_audio_codec_supported && !is_audio_codec_supported) | 
| 226     stop_trigger = remoting::UNSUPPORTED_AUDIO_CODEC; | 220     stop_trigger = UNSUPPORTED_AUDIO_CODEC; | 
| 227   if (was_video_codec_supported && !is_video_codec_supported) { | 221   if (was_video_codec_supported && !is_video_codec_supported) { | 
| 228     stop_trigger = stop_trigger == remoting::UNSUPPORTED_AUDIO_CODEC | 222     stop_trigger = stop_trigger == UNSUPPORTED_AUDIO_CODEC | 
| 229                        ? remoting::UNSUPPORTED_AUDIO_AND_VIDEO_CODECS | 223                        ? UNSUPPORTED_AUDIO_AND_VIDEO_CODECS | 
| 230                        : remoting::UNSUPPORTED_VIDEO_CODEC; | 224                        : UNSUPPORTED_VIDEO_CODEC; | 
| 231   } | 225   } | 
| 232   UpdateAndMaybeSwitch(start_trigger, stop_trigger); | 226   UpdateAndMaybeSwitch(start_trigger, stop_trigger); | 
| 233 } | 227 } | 
| 234 | 228 | 
| 235 bool RemotingRendererController::IsVideoCodecSupported() { | 229 bool RendererController::IsVideoCodecSupported() { | 
| 236   DCHECK(thread_checker_.CalledOnValidThread()); | 230   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 237   DCHECK(has_video()); | 231   DCHECK(has_video()); | 
| 238 | 232 | 
| 239   switch (pipeline_metadata_.video_decoder_config.codec()) { | 233   switch (pipeline_metadata_.video_decoder_config.codec()) { | 
| 240     case VideoCodec::kCodecH264: | 234     case VideoCodec::kCodecH264: | 
| 241     case VideoCodec::kCodecVP8: | 235     case VideoCodec::kCodecVP8: | 
| 242       return true; | 236       return true; | 
| 243     default: | 237     default: | 
| 244       VLOG(2) << "Remoting does not support video codec: " | 238       VLOG(2) << "Remoting does not support video codec: " | 
| 245               << pipeline_metadata_.video_decoder_config.codec(); | 239               << pipeline_metadata_.video_decoder_config.codec(); | 
| 246       return false; | 240       return false; | 
| 247   } | 241   } | 
| 248 } | 242 } | 
| 249 | 243 | 
| 250 bool RemotingRendererController::IsAudioCodecSupported() { | 244 bool RendererController::IsAudioCodecSupported() { | 
| 251   DCHECK(thread_checker_.CalledOnValidThread()); | 245   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 252   DCHECK(has_audio()); | 246   DCHECK(has_audio()); | 
| 253 | 247 | 
| 254   switch (pipeline_metadata_.audio_decoder_config.codec()) { | 248   switch (pipeline_metadata_.audio_decoder_config.codec()) { | 
| 255     case AudioCodec::kCodecAAC: | 249     case AudioCodec::kCodecAAC: | 
| 256     case AudioCodec::kCodecMP3: | 250     case AudioCodec::kCodecMP3: | 
| 257     case AudioCodec::kCodecPCM: | 251     case AudioCodec::kCodecPCM: | 
| 258     case AudioCodec::kCodecVorbis: | 252     case AudioCodec::kCodecVorbis: | 
| 259     case AudioCodec::kCodecFLAC: | 253     case AudioCodec::kCodecFLAC: | 
| 260     case AudioCodec::kCodecAMR_NB: | 254     case AudioCodec::kCodecAMR_NB: | 
| 261     case AudioCodec::kCodecAMR_WB: | 255     case AudioCodec::kCodecAMR_WB: | 
| 262     case AudioCodec::kCodecPCM_MULAW: | 256     case AudioCodec::kCodecPCM_MULAW: | 
| 263     case AudioCodec::kCodecGSM_MS: | 257     case AudioCodec::kCodecGSM_MS: | 
| 264     case AudioCodec::kCodecPCM_S16BE: | 258     case AudioCodec::kCodecPCM_S16BE: | 
| 265     case AudioCodec::kCodecPCM_S24BE: | 259     case AudioCodec::kCodecPCM_S24BE: | 
| 266     case AudioCodec::kCodecOpus: | 260     case AudioCodec::kCodecOpus: | 
| 267     case AudioCodec::kCodecEAC3: | 261     case AudioCodec::kCodecEAC3: | 
| 268     case AudioCodec::kCodecPCM_ALAW: | 262     case AudioCodec::kCodecPCM_ALAW: | 
| 269     case AudioCodec::kCodecALAC: | 263     case AudioCodec::kCodecALAC: | 
| 270     case AudioCodec::kCodecAC3: | 264     case AudioCodec::kCodecAC3: | 
| 271       return true; | 265       return true; | 
| 272     default: | 266     default: | 
| 273       VLOG(2) << "Remoting does not support audio codec: " | 267       VLOG(2) << "Remoting does not support audio codec: " | 
| 274               << pipeline_metadata_.audio_decoder_config.codec(); | 268               << pipeline_metadata_.audio_decoder_config.codec(); | 
| 275       return false; | 269       return false; | 
| 276   } | 270   } | 
| 277 } | 271 } | 
| 278 | 272 | 
| 279 void RemotingRendererController::OnPlaying() { | 273 void RendererController::OnPlaying() { | 
| 280   DCHECK(thread_checker_.CalledOnValidThread()); | 274   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 281 | 275 | 
| 282   is_paused_ = false; | 276   is_paused_ = false; | 
| 283   UpdateAndMaybeSwitch(remoting::PLAY_COMMAND, remoting::UNKNOWN_STOP_TRIGGER); | 277   UpdateAndMaybeSwitch(PLAY_COMMAND, UNKNOWN_STOP_TRIGGER); | 
| 284 } | 278 } | 
| 285 | 279 | 
| 286 void RemotingRendererController::OnPaused() { | 280 void RendererController::OnPaused() { | 
| 287   DCHECK(thread_checker_.CalledOnValidThread()); | 281   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 288 | 282 | 
| 289   is_paused_ = true; | 283   is_paused_ = true; | 
| 290 } | 284 } | 
| 291 | 285 | 
| 292 bool RemotingRendererController::ShouldBeRemoting() { | 286 bool RendererController::ShouldBeRemoting() { | 
| 293   DCHECK(thread_checker_.CalledOnValidThread()); | 287   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 294 | 288 | 
| 295   if (switch_renderer_cb_.is_null()) { | 289   if (switch_renderer_cb_.is_null()) { | 
| 296     DCHECK(!remote_rendering_started_); | 290     DCHECK(!remote_rendering_started_); | 
| 297     return false;  // No way to switch to a RemotingRenderImpl. | 291     return false;  // No way to switch to the remoting renderer. | 
| 298   } | 292   } | 
| 299 | 293 | 
| 300   const RemotingSessionState state = remoting_source_->state(); | 294   const SharedSession::SessionState state = session_->state(); | 
| 301   if (is_encrypted_) { | 295   if (is_encrypted_) { | 
| 302     // Due to technical limitations when playing encrypted content, once a | 296     // Due to technical limitations when playing encrypted content, once a | 
| 303     // remoting session has been started, always return true here to indicate | 297     // remoting session has been started, always return true here to indicate | 
| 304     // that the RemotingRendererImpl should be used. In the stopped states, | 298     // that the CourierRenderer should continue to be used. In the stopped | 
| 305     // RemotingRendererImpl will display an interstitial to notify the user that | 299     // states, CourierRenderer will display an interstitial to notify the user | 
| 306     // local rendering cannot be resumed. | 300     // that local rendering cannot be resumed. | 
| 307     // | 301     // | 
| 308     // TODO(miu): Revisit this once more of the encrypted-remoting impl is | 302     // TODO(miu): Revisit this once more of the encrypted-remoting impl is | 
| 309     // in-place. For example, this will prevent metrics from recording session | 303     // in-place. For example, this will prevent metrics from recording session | 
| 310     // stop reasons. | 304     // stop reasons. | 
| 311     return state == RemotingSessionState::SESSION_STARTED || | 305     return state == SharedSession::SESSION_STARTED || | 
| 312            state == RemotingSessionState::SESSION_STOPPING || | 306            state == SharedSession::SESSION_STOPPING || | 
| 313            state == RemotingSessionState::SESSION_PERMANENTLY_STOPPED; | 307            state == SharedSession::SESSION_PERMANENTLY_STOPPED; | 
| 314   } | 308   } | 
| 315 | 309 | 
| 316   if (encountered_renderer_fatal_error_) | 310   if (encountered_renderer_fatal_error_) | 
| 317     return false; | 311     return false; | 
| 318 | 312 | 
| 319   switch (state) { | 313   switch (state) { | 
| 320     case SESSION_UNAVAILABLE: | 314     case SharedSession::SESSION_UNAVAILABLE: | 
| 321       return false;  // Cannot remote media without a remote sink. | 315       return false;  // Cannot remote media without a remote sink. | 
| 322     case SESSION_CAN_START: | 316     case SharedSession::SESSION_CAN_START: | 
| 323     case SESSION_STARTING: | 317     case SharedSession::SESSION_STARTING: | 
| 324     case SESSION_STARTED: | 318     case SharedSession::SESSION_STARTED: | 
| 325       break;  // Media remoting is possible, assuming other requirments are met. | 319       break;  // Media remoting is possible, assuming other requirments are met. | 
| 326     case SESSION_STOPPING: | 320     case SharedSession::SESSION_STOPPING: | 
| 327     case SESSION_PERMANENTLY_STOPPED: | 321     case SharedSession::SESSION_PERMANENTLY_STOPPED: | 
| 328       return false;  // Use local rendering after stopping remoting. | 322       return false;  // Use local rendering after stopping remoting. | 
| 329   } | 323   } | 
| 330 | 324 | 
| 331   switch (remoting_source_->sink_capabilities()) { | 325   switch (session_->sink_capabilities()) { | 
| 332     case mojom::RemotingSinkCapabilities::NONE: | 326     case mojom::RemotingSinkCapabilities::NONE: | 
| 333       return false; | 327       return false; | 
| 334     case mojom::RemotingSinkCapabilities::RENDERING_ONLY: | 328     case mojom::RemotingSinkCapabilities::RENDERING_ONLY: | 
| 335     case mojom::RemotingSinkCapabilities::CONTENT_DECRYPTION_AND_RENDERING: | 329     case mojom::RemotingSinkCapabilities::CONTENT_DECRYPTION_AND_RENDERING: | 
| 336       break;  // The sink is capable of remote rendering. | 330       break;  // The sink is capable of remote rendering. | 
| 337   } | 331   } | 
| 338 | 332 | 
| 339   if ((!has_audio() && !has_video()) || | 333   if ((!has_audio() && !has_video()) || | 
| 340       (has_video() && !IsVideoCodecSupported()) || | 334       (has_video() && !IsVideoCodecSupported()) || | 
| 341       (has_audio() && !IsAudioCodecSupported())) { | 335       (has_audio() && !IsAudioCodecSupported())) { | 
| 342     return false; | 336     return false; | 
| 343   } | 337   } | 
| 344 | 338 | 
| 345   if (is_remote_playback_disabled_) | 339   if (is_remote_playback_disabled_) | 
| 346     return false; | 340     return false; | 
| 347 | 341 | 
| 348   // Normally, entering fullscreen or being the dominant visible content is the | 342   // Normally, entering fullscreen or being the dominant visible content is the | 
| 349   // signal that starts remote rendering. However, current technical limitations | 343   // signal that starts remote rendering. However, current technical limitations | 
| 350   // require encrypted content be remoted without waiting for a user signal. | 344   // require encrypted content be remoted without waiting for a user signal. | 
| 351   return is_fullscreen_ || is_dominant_content_; | 345   return is_fullscreen_ || is_dominant_content_; | 
| 352 } | 346 } | 
| 353 | 347 | 
| 354 void RemotingRendererController::UpdateAndMaybeSwitch( | 348 void RendererController::UpdateAndMaybeSwitch(StartTrigger start_trigger, | 
| 355     remoting::StartTrigger start_trigger, | 349                                               StopTrigger stop_trigger) { | 
| 356     remoting::StopTrigger stop_trigger) { |  | 
| 357   DCHECK(thread_checker_.CalledOnValidThread()); | 350   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 358 | 351 | 
| 359   bool should_be_remoting = ShouldBeRemoting(); | 352   bool should_be_remoting = ShouldBeRemoting(); | 
| 360 | 353 | 
| 361   if (remote_rendering_started_ == should_be_remoting) | 354   if (remote_rendering_started_ == should_be_remoting) | 
| 362     return; | 355     return; | 
| 363 | 356 | 
| 364   // Only switch to remoting when media is playing. Since the renderer is | 357   // Only switch to remoting when media is playing. Since the renderer is | 
| 365   // created when video starts loading/playing, receiver will display a black | 358   // created when video starts loading/playing, receiver will display a black | 
| 366   // screen before video starts playing if switching to remoting when paused. | 359   // screen before video starts playing if switching to remoting when paused. | 
| 367   // Thus, the user experience is improved by not starting remoting until | 360   // Thus, the user experience is improved by not starting remoting until | 
| 368   // playback resumes. | 361   // playback resumes. | 
| 369   if (should_be_remoting && is_paused_) | 362   if (should_be_remoting && is_paused_) | 
| 370     return; | 363     return; | 
| 371 | 364 | 
| 372   // Switch between local renderer and remoting renderer. | 365   // Switch between local renderer and remoting renderer. | 
| 373   remote_rendering_started_ = should_be_remoting; | 366   remote_rendering_started_ = should_be_remoting; | 
| 374 | 367 | 
| 375   if (remote_rendering_started_) { | 368   if (remote_rendering_started_) { | 
| 376     DCHECK(!switch_renderer_cb_.is_null()); | 369     DCHECK(!switch_renderer_cb_.is_null()); | 
| 377     if (remoting_source_->state() == | 370     if (session_->state() == SharedSession::SESSION_PERMANENTLY_STOPPED) { | 
| 378         RemotingSessionState::SESSION_PERMANENTLY_STOPPED) { |  | 
| 379       switch_renderer_cb_.Run(); | 371       switch_renderer_cb_.Run(); | 
| 380       return; | 372       return; | 
| 381     } | 373     } | 
| 382     DCHECK_NE(start_trigger, remoting::UNKNOWN_START_TRIGGER); | 374     DCHECK_NE(start_trigger, UNKNOWN_START_TRIGGER); | 
| 383     metrics_recorder_.WillStartSession(start_trigger); | 375     metrics_recorder_.WillStartSession(start_trigger); | 
| 384     // |switch_renderer_cb_.Run()| will be called after remoting is started | 376     // |switch_renderer_cb_.Run()| will be called after remoting is started | 
| 385     // successfully. | 377     // successfully. | 
| 386     remoting_source_->StartRemoting(this); | 378     session_->StartRemoting(this); | 
| 387   } else { | 379   } else { | 
| 388     // For encrypted content, it's only valid to switch to remoting renderer, | 380     // For encrypted content, it's only valid to switch to remoting renderer, | 
| 389     // and never back to the local renderer. The RemotingCdmController will | 381     // and never back to the local renderer. The RemotingCdmController will | 
| 390     // force-stop the session when remoting has ended; so no need to call | 382     // force-stop the session when remoting has ended; so no need to call | 
| 391     // StopRemoting() from here. | 383     // StopRemoting() from here. | 
| 392     DCHECK(!is_encrypted_); | 384     DCHECK(!is_encrypted_); | 
| 393     DCHECK_NE(stop_trigger, remoting::UNKNOWN_STOP_TRIGGER); | 385     DCHECK_NE(stop_trigger, UNKNOWN_STOP_TRIGGER); | 
| 394     metrics_recorder_.WillStopSession(stop_trigger); | 386     metrics_recorder_.WillStopSession(stop_trigger); | 
| 395     switch_renderer_cb_.Run(); | 387     switch_renderer_cb_.Run(); | 
| 396     remoting_source_->StopRemoting(this); | 388     session_->StopRemoting(this); | 
| 397   } | 389   } | 
| 398 } | 390 } | 
| 399 | 391 | 
| 400 void RemotingRendererController::SetShowInterstitialCallback( | 392 void RendererController::SetShowInterstitialCallback( | 
| 401     const ShowInterstitialCallback& cb) { | 393     const ShowInterstitialCallback& cb) { | 
| 402   DCHECK(thread_checker_.CalledOnValidThread()); | 394   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 403   show_interstitial_cb_ = cb; | 395   show_interstitial_cb_ = cb; | 
| 404   UpdateInterstitial(SkBitmap()); | 396   UpdateInterstitial(SkBitmap()); | 
| 405   if (!poster_url_.is_empty()) | 397   if (!poster_url_.is_empty()) | 
| 406     DownloadPosterImage(); | 398     DownloadPosterImage(); | 
| 407 } | 399 } | 
| 408 | 400 | 
| 409 void RemotingRendererController::SetDownloadPosterCallback( | 401 void RendererController::SetDownloadPosterCallback( | 
| 410     const DownloadPosterCallback& cb) { | 402     const DownloadPosterCallback& cb) { | 
| 411   DCHECK(thread_checker_.CalledOnValidThread()); | 403   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 412   DCHECK(download_poster_cb_.is_null()); | 404   DCHECK(download_poster_cb_.is_null()); | 
| 413   download_poster_cb_ = cb; | 405   download_poster_cb_ = cb; | 
| 414   if (!poster_url_.is_empty()) | 406   if (!poster_url_.is_empty()) | 
| 415     DownloadPosterImage(); | 407     DownloadPosterImage(); | 
| 416 } | 408 } | 
| 417 | 409 | 
| 418 void RemotingRendererController::UpdateInterstitial( | 410 void RendererController::UpdateInterstitial( | 
| 419     const base::Optional<SkBitmap>& image) { | 411     const base::Optional<SkBitmap>& image) { | 
| 420   DCHECK(thread_checker_.CalledOnValidThread()); | 412   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 421   if (show_interstitial_cb_.is_null() || | 413   if (show_interstitial_cb_.is_null() || | 
| 422       pipeline_metadata_.natural_size.IsEmpty()) | 414       pipeline_metadata_.natural_size.IsEmpty()) | 
| 423     return; | 415     return; | 
| 424 | 416 | 
| 425   RemotingInterstitialType type = RemotingInterstitialType::BETWEEN_SESSIONS; | 417   InterstitialType type = InterstitialType::BETWEEN_SESSIONS; | 
| 426   switch (remoting_source_->state()) { | 418   switch (session_->state()) { | 
| 427     case SESSION_STARTED: | 419     case SharedSession::SESSION_STARTED: | 
| 428       type = RemotingInterstitialType::IN_SESSION; | 420       type = InterstitialType::IN_SESSION; | 
| 429       break; | 421       break; | 
| 430     case SESSION_PERMANENTLY_STOPPED: | 422     case SharedSession::SESSION_PERMANENTLY_STOPPED: | 
| 431       type = RemotingInterstitialType::ENCRYPTED_MEDIA_FATAL_ERROR; | 423       type = InterstitialType::ENCRYPTED_MEDIA_FATAL_ERROR; | 
| 432       break; | 424       break; | 
| 433     case SESSION_UNAVAILABLE: | 425     case SharedSession::SESSION_UNAVAILABLE: | 
| 434     case SESSION_CAN_START: | 426     case SharedSession::SESSION_CAN_START: | 
| 435     case SESSION_STARTING: | 427     case SharedSession::SESSION_STARTING: | 
| 436     case SESSION_STOPPING: | 428     case SharedSession::SESSION_STOPPING: | 
| 437       break; | 429       break; | 
| 438   } | 430   } | 
| 439 | 431 | 
| 440   show_interstitial_cb_.Run(image, pipeline_metadata_.natural_size, type); | 432   show_interstitial_cb_.Run(image, pipeline_metadata_.natural_size, type); | 
| 441 } | 433 } | 
| 442 | 434 | 
| 443 void RemotingRendererController::DownloadPosterImage() { | 435 void RendererController::DownloadPosterImage() { | 
| 444   if (download_poster_cb_.is_null() || show_interstitial_cb_.is_null()) | 436   if (download_poster_cb_.is_null() || show_interstitial_cb_.is_null()) | 
| 445     return; | 437     return; | 
| 446   DCHECK(!poster_url_.is_empty()); | 438   DCHECK(!poster_url_.is_empty()); | 
| 447 | 439 | 
| 448   const base::TimeTicks download_start_time = base::TimeTicks::Now(); | 440   const base::TimeTicks download_start_time = base::TimeTicks::Now(); | 
| 449   download_poster_cb_.Run( | 441   download_poster_cb_.Run( | 
| 450       poster_url_, | 442       poster_url_, | 
| 451       base::Bind(&RemotingRendererController::OnPosterImageDownloaded, | 443       base::Bind(&RendererController::OnPosterImageDownloaded, | 
| 452                  weak_factory_.GetWeakPtr(), poster_url_, download_start_time)); | 444                  weak_factory_.GetWeakPtr(), poster_url_, download_start_time)); | 
| 453 } | 445 } | 
| 454 | 446 | 
| 455 void RemotingRendererController::OnPosterImageDownloaded( | 447 void RendererController::OnPosterImageDownloaded( | 
| 456     const GURL& download_url, | 448     const GURL& download_url, | 
| 457     base::TimeTicks download_start_time, | 449     base::TimeTicks download_start_time, | 
| 458     const SkBitmap& image) { | 450     const SkBitmap& image) { | 
| 459   DCHECK(thread_checker_.CalledOnValidThread()); | 451   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 460 | 452 | 
| 461   metrics_recorder_.OnPosterImageDownloaded( | 453   metrics_recorder_.OnPosterImageDownloaded( | 
| 462       base::TimeTicks::Now() - download_start_time, !image.drawsNothing()); | 454       base::TimeTicks::Now() - download_start_time, !image.drawsNothing()); | 
| 463   if (download_url != poster_url_) | 455   if (download_url != poster_url_) | 
| 464     return;  // The poster image URL has changed during the download. | 456     return;  // The poster image URL has changed during the download. | 
| 465   UpdateInterstitial(image); | 457   UpdateInterstitial(image); | 
| 466 } | 458 } | 
| 467 | 459 | 
| 468 void RemotingRendererController::OnRendererFatalError( | 460 void RendererController::OnRendererFatalError(StopTrigger stop_trigger) { | 
| 469     remoting::StopTrigger stop_trigger) { |  | 
| 470   DCHECK(thread_checker_.CalledOnValidThread()); | 461   DCHECK(thread_checker_.CalledOnValidThread()); | 
| 471 | 462 | 
| 472   // Do not act on errors caused by things like Mojo pipes being closed during | 463   // Do not act on errors caused by things like Mojo pipes being closed during | 
| 473   // shutdown. | 464   // shutdown. | 
| 474   if (!remote_rendering_started_) | 465   if (!remote_rendering_started_) | 
| 475     return; | 466     return; | 
| 476 | 467 | 
| 477   encountered_renderer_fatal_error_ = true; | 468   encountered_renderer_fatal_error_ = true; | 
| 478   UpdateAndMaybeSwitch(remoting::UNKNOWN_START_TRIGGER, stop_trigger); | 469   UpdateAndMaybeSwitch(UNKNOWN_START_TRIGGER, stop_trigger); | 
| 479 } | 470 } | 
| 480 | 471 | 
|  | 472 }  // namespace remoting | 
| 481 }  // namespace media | 473 }  // namespace media | 
| OLD | NEW | 
|---|