OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/remoting/remoting_controller.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/logging.h" | |
9 #include "base/single_thread_task_runner.h" | |
10 #include "media/remoting/remoting_cdm.h" | |
11 | |
12 namespace media { | |
13 | |
14 RemotingController::RemotingController( | |
15 mojom::RemotingSourceRequest source_request, | |
16 mojom::RemoterPtr remoter) | |
17 : binding_(this, std::move(source_request)), | |
18 remoter_(std::move(remoter)), | |
19 task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
20 weak_factory_(this) {} | |
21 | |
22 RemotingController::~RemotingController() {} | |
23 | |
24 void RemotingController::OnSinkAvailable() { | |
25 DCHECK(task_runner_->BelongsToCurrentThread()); | |
26 | |
27 is_sink_available_ = true; | |
28 UpdateAndMaybeSwitch(); | |
29 } | |
30 | |
31 void RemotingController::OnSinkGone() { | |
32 DCHECK(task_runner_->BelongsToCurrentThread()); | |
33 | |
34 is_sink_available_ = false; | |
35 UpdateAndMaybeSwitch(); | |
36 } | |
37 | |
38 void RemotingController::OnStarted() { | |
39 DCHECK(task_runner_->BelongsToCurrentThread()); | |
40 | |
41 VLOG(1) << "Remoting started successively."; | |
42 | |
43 // |switch_renderer_cb_| not being set indicates that this remoting session | |
44 // is started for creating CDM. | |
45 if (switch_renderer_cb_.is_null()) { | |
46 is_remoting_cdm_ = true; | |
47 DCHECK(!cdm_check_cb_.is_null()); | |
48 cdm_check_cb_.Run(true); | |
49 } else if (is_remoting_) { | |
50 switch_renderer_cb_.Run(); | |
51 } else { | |
52 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | |
53 } | |
54 } | |
55 | |
56 void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) { | |
57 DCHECK(task_runner_->BelongsToCurrentThread()); | |
58 | |
59 VLOG(1) << "Failed to start remoting:" << reason; | |
60 | |
61 is_remoting_ = false; | |
62 if (switch_renderer_cb_.is_null()) { | |
63 DCHECK(!cdm_check_cb_.is_null()); | |
64 is_remoting_cdm_ = false; | |
65 cdm_check_cb_.Run(false); | |
66 } | |
67 } | |
68 | |
69 void RemotingController::OnMessageFromSink( | |
70 const std::vector<uint8_t>& message) { | |
71 DCHECK(task_runner_->BelongsToCurrentThread()); | |
72 | |
73 // TODO(xjz): Merge with Eric's CL to handle the RPC messages here. | |
74 NOTIMPLEMENTED(); | |
75 } | |
76 | |
77 void RemotingController::OnStopped(mojom::RemotingStopReason reason) { | |
78 DCHECK(task_runner_->BelongsToCurrentThread()); | |
79 | |
80 VLOG(1) << "Remoting stopped: " << reason; | |
81 | |
82 is_remoting_ = false; | |
83 if (is_remoting_cdm_) | |
84 is_remoting_failed_ = true; | |
85 } | |
86 | |
87 void RemotingController::OnEnteredFullscreen() { | |
88 DCHECK(task_runner_->BelongsToCurrentThread()); | |
89 | |
90 is_fullscreen_ = true; | |
91 UpdateAndMaybeSwitch(); | |
92 } | |
93 | |
94 void RemotingController::OnExitedFullscreen() { | |
95 DCHECK(task_runner_->BelongsToCurrentThread()); | |
96 | |
97 is_fullscreen_ = false; | |
98 UpdateAndMaybeSwitch(); | |
99 } | |
100 | |
101 void RemotingController::OnSetCdm(CdmContext* cdm_context) { | |
102 DCHECK(task_runner_->BelongsToCurrentThread()); | |
103 | |
104 if (auto* cdm = RemotingCdm::From(cdm_context)) { | |
miu
2016/10/08 01:06:16
Possible edge-case issue: What if a remoting sessi
xjz
2016/10/20 21:25:28
This seems not a problem now. If a remoting sessio
| |
105 cdm_remoting_controller_ = cdm->remoting_controller(); | |
106 if (!cdm_remoting_controller_) | |
107 return; | |
108 cdm_remoting_controller_->SetSwitchRendererCallback(switch_renderer_cb_); | |
109 // This may trigger the switching renderer call in | |
110 // |cdm_remoting_controller_|. | |
111 cdm_remoting_controller_->OnMetadataChanged(metadata_); | |
112 } | |
113 } | |
114 | |
115 void RemotingController::SetSwitchRendererCallback( | |
116 const SwitchRendererCallback& cb) { | |
117 DCHECK(task_runner_->BelongsToCurrentThread()); | |
118 DCHECK(!cb.is_null()); | |
119 | |
120 switch_renderer_cb_ = cb; | |
121 } | |
122 | |
123 void RemotingController::OnMetadataChanged(const PipelineMetadata& metadata) { | |
124 DCHECK(task_runner_->BelongsToCurrentThread()); | |
125 | |
126 if (cdm_remoting_controller_) { | |
127 cdm_remoting_controller_->OnMetadataChanged(metadata); | |
128 return; | |
129 } | |
130 | |
131 metadata_ = metadata; | |
132 has_video_ = metadata.has_video; | |
133 has_audio_ = metadata.has_audio; | |
134 if (!has_video_ && !has_audio_) | |
135 return; | |
136 | |
137 if (has_video_) { | |
138 DCHECK(metadata.video_decoder_config.IsValidConfig()); | |
139 video_decoder_config_ = metadata.video_decoder_config; | |
140 is_encrypted_ |= video_decoder_config_.is_encrypted(); | |
141 } | |
142 if (has_audio_) { | |
143 DCHECK(metadata.audio_decoder_config.IsValidConfig()); | |
144 audio_decoder_config_ = metadata.audio_decoder_config; | |
145 is_encrypted_ |= audio_decoder_config_.is_encrypted(); | |
146 } | |
147 UpdateAndMaybeSwitch(); | |
148 } | |
149 | |
150 bool RemotingController::IsVideoCodecSupported() { | |
151 DCHECK(task_runner_->BelongsToCurrentThread()); | |
152 DCHECK(has_video_); | |
153 | |
154 switch (video_decoder_config_.codec()) { | |
155 case VideoCodec::kCodecH264: | |
156 case VideoCodec::kCodecVP8: | |
157 return true; | |
158 default: | |
159 VLOG(2) << "Remoting does not support video codec: " | |
160 << video_decoder_config_.codec(); | |
161 return false; | |
162 } | |
163 } | |
164 | |
165 bool RemotingController::IsAudioCodecSupported() { | |
166 DCHECK(task_runner_->BelongsToCurrentThread()); | |
167 DCHECK(has_audio_); | |
168 | |
169 switch (audio_decoder_config_.codec()) { | |
170 case AudioCodec::kCodecAAC: | |
171 case AudioCodec::kCodecMP3: | |
172 case AudioCodec::kCodecPCM: | |
173 case AudioCodec::kCodecVorbis: | |
174 case AudioCodec::kCodecFLAC: | |
175 case AudioCodec::kCodecAMR_NB: | |
176 case AudioCodec::kCodecAMR_WB: | |
177 case AudioCodec::kCodecPCM_MULAW: | |
178 case AudioCodec::kCodecGSM_MS: | |
179 case AudioCodec::kCodecPCM_S16BE: | |
180 case AudioCodec::kCodecPCM_S24BE: | |
181 case AudioCodec::kCodecOpus: | |
182 case AudioCodec::kCodecEAC3: | |
183 case AudioCodec::kCodecPCM_ALAW: | |
184 case AudioCodec::kCodecALAC: | |
185 case AudioCodec::kCodecAC3: | |
186 return true; | |
187 default: | |
188 VLOG(2) << "Remoting does not support audio codec: " | |
189 << audio_decoder_config_.codec(); | |
190 return false; | |
191 } | |
192 } | |
193 | |
194 bool RemotingController::ShouldBeRemoting() { | |
195 DCHECK(task_runner_->BelongsToCurrentThread()); | |
196 | |
197 if (has_video_ && !IsVideoCodecSupported()) | |
198 return false; | |
199 if (has_audio_ && !IsAudioCodecSupported()) | |
200 return false; | |
201 | |
202 if (is_encrypted_) { | |
203 DCHECK(!cdm_remoting_controller_); | |
204 return is_remoting_cdm_ && is_sink_available_; | |
205 } | |
206 | |
207 if (!is_sink_available_) | |
208 return false; | |
209 if (!is_fullscreen_) | |
210 return false; | |
211 return true; | |
212 } | |
213 | |
214 void RemotingController::UpdateAndMaybeSwitch() { | |
215 DCHECK(task_runner_->BelongsToCurrentThread()); | |
216 | |
217 // Demuxer is not initialized yet. | |
218 if (!has_audio_ && !has_video_) | |
219 return; | |
220 | |
221 // This RemotingController is created when creating CDM, and is not attached | |
222 // to media element yet. | |
223 if (switch_renderer_cb_.is_null()) | |
224 return; | |
225 | |
226 // When cdm is attached, renderer switching will only happen in | |
227 // |cdm_remoting_controller_| instead of here. | |
228 if (cdm_remoting_controller_) | |
229 return; | |
230 | |
231 // Remoting was failed and is not able to be restarted until user explicitly | |
232 // reloads the page. | |
233 if (is_remoting_failed_) | |
234 return; | |
235 | |
236 bool should_be_remoting = ShouldBeRemoting(); | |
237 if (is_remoting_ == should_be_remoting) | |
238 return; | |
239 | |
240 // Switch between local renderer and remoting renderer. | |
241 is_remoting_ = should_be_remoting; | |
242 if (is_remoting_) { | |
243 if (!is_remoting_cdm_) | |
244 // |swithc_renderer_cb_.Run()| will be called after remoting is started | |
245 // successfully for non-encrypted contents. | |
246 remoter_->Start(); | |
247 else | |
248 // Remoting was already started for encrypted contents. Calling | |
249 // |switch_renderer_cb_| to switch to remoting renderer. | |
250 switch_renderer_cb_.Run(); | |
251 } else { | |
252 switch_renderer_cb_.Run(); | |
253 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | |
254 if (is_remoting_cdm_) | |
255 // This is also set in OnStopped(). | |
256 is_remoting_failed_ = true; | |
257 } | |
258 } | |
259 | |
260 void RemotingController::ShouldCreateRemotingCdm(const CdmCheckCallback& cb) { | |
261 DCHECK(task_runner_->BelongsToCurrentThread()); | |
262 DCHECK(!cb.is_null()); | |
263 DCHECK(switch_renderer_cb_.is_null()); | |
264 | |
265 cdm_check_cb_ = cb; | |
266 if (!is_sink_available_) { | |
267 cdm_check_cb_.Run(false); | |
miu
2016/10/08 01:06:16
What if the sink becomes available later? Say, in
xjz
2016/10/20 21:25:28
Can we just consider simple case? If the sink is n
| |
268 is_remoting_cdm_ = false; | |
269 } else { | |
270 // Will run |cdm_check_cb_| in OnStarted() or OnStartFailed. | |
271 remoter_->Start(); | |
272 } | |
273 } | |
274 | |
275 } // namespace media | |
OLD | NEW |