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