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 |