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_controller.h" | 5 #include "media/remoting/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/single_thread_task_runner.h" | 9 #include "base/threading/thread_checker.h" |
10 #include "media/remoting/remoting_cdm.h" | |
10 | 11 |
11 namespace media { | 12 namespace media { |
12 | 13 |
13 RemotingController::RemotingController( | 14 RemotingRendererController::RemotingRendererController( |
14 mojom::RemotingSourceRequest source_request, | 15 scoped_refptr<RemotingSourceImpl> remoting_source) |
15 mojom::RemoterPtr remoter) | 16 : remoting_source_(remoting_source), weak_factory_(this) { |
16 : binding_(this, std::move(source_request)), | 17 remoting_source_->AddClient(this); |
17 remoter_(std::move(remoter)), | |
18 task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
19 weak_factory_(this) {} | |
20 | |
21 RemotingController::~RemotingController() {} | |
22 | |
23 void RemotingController::OnSinkAvailable() { | |
24 DCHECK(task_runner_->BelongsToCurrentThread()); | |
25 | |
26 is_sink_available_ = true; | |
27 UpdateAndMaybeSwitch(); | |
28 } | 18 } |
29 | 19 |
30 void RemotingController::OnSinkGone() { | 20 RemotingRendererController::~RemotingRendererController() { |
31 DCHECK(task_runner_->BelongsToCurrentThread()); | 21 DCHECK(thread_checker_.CalledOnValidThread()); |
32 | 22 remoting_source_->RemoveClient(this); |
33 is_sink_available_ = false; | |
34 UpdateAndMaybeSwitch(); | |
35 } | 23 } |
36 | 24 |
37 void RemotingController::OnStarted() { | 25 void RemotingRendererController::OnStarted(bool success) { |
38 DCHECK(task_runner_->BelongsToCurrentThread()); | 26 DCHECK(thread_checker_.CalledOnValidThread()); |
39 | 27 |
40 VLOG(1) << "Remoting started successively."; | 28 if (success) { |
41 if (is_remoting_) | 29 VLOG(1) << "Remoting started successively."; |
42 switch_renderer_cb_.Run(); | 30 if (remote_rendering_started_) { |
43 else | 31 DCHECK(!switch_renderer_cb_.is_null()); |
44 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | 32 switch_renderer_cb_.Run(); |
33 } else { | |
34 remoting_source_->StopRemoting(this); | |
35 } | |
36 } else { | |
37 VLOG(1) << "Failed to start remoting."; | |
38 remote_rendering_started_ = false; | |
39 } | |
45 } | 40 } |
46 | 41 |
47 void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) { | 42 void RemotingRendererController::OnSessionStateChanged() { |
48 DCHECK(task_runner_->BelongsToCurrentThread()); | 43 DCHECK(thread_checker_.CalledOnValidThread()); |
49 | 44 |
50 VLOG(1) << "Failed to start remoting:" << reason; | 45 VLOG(1) << "OnSessionStateChanged: " << remoting_source_->state(); |
51 is_remoting_ = false; | 46 bool session_could_remote = session_can_remote_; |
47 switch (remoting_source_->state()) { | |
miu
2016/10/29 02:22:16
Looking at this code again, I think you can simult
xjz
2016/10/31 17:24:00
Done.
| |
48 case SESSION_CAN_START: | |
49 case SESSION_STARTING: | |
50 case SESSION_STARTED: | |
51 session_can_remote_ = true; | |
52 break; | |
53 case SESSION_STOPPING: | |
54 case SESSION_UNAVAILABLE: | |
55 case SESSION_PERMANENTLY_STOPPED: | |
56 session_can_remote_ = false; | |
57 break; | |
58 } | |
59 if (session_can_remote_ != session_could_remote) | |
60 UpdateAndMaybeSwitch(); | |
52 } | 61 } |
53 | 62 |
54 void RemotingController::OnMessageFromSink( | 63 void RemotingRendererController::OnEnteredFullscreen() { |
55 const std::vector<uint8_t>& message) { | 64 DCHECK(thread_checker_.CalledOnValidThread()); |
56 DCHECK(task_runner_->BelongsToCurrentThread()); | |
57 | |
58 // TODO(xjz): Merge with Eric's CL to handle the RPC messages here. | |
59 NOTIMPLEMENTED(); | |
60 } | |
61 | |
62 void RemotingController::OnStopped(mojom::RemotingStopReason reason) { | |
63 DCHECK(task_runner_->BelongsToCurrentThread()); | |
64 | |
65 VLOG(1) << "Remoting stopped: " << reason; | |
66 is_remoting_ = false; | |
67 } | |
68 | |
69 void RemotingController::OnEnteredFullscreen() { | |
70 DCHECK(task_runner_->BelongsToCurrentThread()); | |
71 | 65 |
72 is_fullscreen_ = true; | 66 is_fullscreen_ = true; |
73 UpdateAndMaybeSwitch(); | 67 UpdateAndMaybeSwitch(); |
74 } | 68 } |
75 | 69 |
76 void RemotingController::OnExitedFullscreen() { | 70 void RemotingRendererController::OnExitedFullscreen() { |
77 DCHECK(task_runner_->BelongsToCurrentThread()); | 71 DCHECK(thread_checker_.CalledOnValidThread()); |
78 | 72 |
79 is_fullscreen_ = false; | 73 is_fullscreen_ = false; |
80 UpdateAndMaybeSwitch(); | 74 UpdateAndMaybeSwitch(); |
81 } | 75 } |
82 | 76 |
83 void RemotingController::OnSetCdm(CdmContext* cdm_context) { | 77 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) { |
84 DCHECK(task_runner_->BelongsToCurrentThread()); | 78 DCHECK(thread_checker_.CalledOnValidThread()); |
85 | 79 |
86 // TODO(xjz): Not implemented. Will add in up-coming change. | 80 auto* cdm = RemotingCdm::From(cdm_context); |
87 NOTIMPLEMENTED(); | 81 if (!cdm) |
82 return; | |
83 | |
84 remoting_source_->RemoveClient(this); | |
85 remoting_source_ = cdm->GetRemotingSource(); | |
86 remoting_source_->AddClient(this); // Calls OnSessionStateChanged(). | |
87 UpdateAndMaybeSwitch(); | |
88 } | 88 } |
89 | 89 |
90 void RemotingController::SetSwitchRendererCallback( | 90 void RemotingRendererController::SetSwitchRendererCallback( |
91 const SwitchRendererCallback& cb) { | 91 const base::Closure& cb) { |
92 DCHECK(task_runner_->BelongsToCurrentThread()); | 92 DCHECK(thread_checker_.CalledOnValidThread()); |
93 DCHECK(!cb.is_null()); | 93 DCHECK(!cb.is_null()); |
94 | 94 |
95 switch_renderer_cb_ = cb; | 95 switch_renderer_cb_ = cb; |
96 UpdateAndMaybeSwitch(); | |
96 } | 97 } |
97 | 98 |
98 void RemotingController::OnMetadataChanged(const PipelineMetadata& metadata) { | 99 void RemotingRendererController::OnMetadataChanged( |
99 DCHECK(task_runner_->BelongsToCurrentThread()); | 100 const PipelineMetadata& metadata) { |
101 DCHECK(thread_checker_.CalledOnValidThread()); | |
100 | 102 |
101 has_video_ = metadata.has_video; | 103 has_video_ = |
102 has_audio_ = metadata.has_audio; | 104 metadata.has_video && metadata.video_decoder_config.IsValidConfig(); |
103 if (!has_video_ && !has_audio_) | 105 has_audio_ = |
104 return; | 106 metadata.has_audio && metadata.audio_decoder_config.IsValidConfig(); |
105 | 107 |
106 // On Android, when using the MediaPlayerRenderer, |has_video_| and | 108 is_encrypted_ = false; |
107 // |has_audio_| will be true, but the respective configs will be empty. | |
108 // We cannot make any assumptions on the validity of configs. | |
109 if (has_video_) { | 109 if (has_video_) { |
110 video_decoder_config_ = metadata.video_decoder_config; | 110 video_decoder_config_ = metadata.video_decoder_config; |
111 is_encrypted_ |= video_decoder_config_.is_encrypted(); | 111 is_encrypted_ |= video_decoder_config_.is_encrypted(); |
112 } | 112 } |
113 if (has_audio_) { | 113 if (has_audio_) { |
114 audio_decoder_config_ = metadata.audio_decoder_config; | 114 audio_decoder_config_ = metadata.audio_decoder_config; |
115 is_encrypted_ |= audio_decoder_config_.is_encrypted(); | 115 is_encrypted_ |= audio_decoder_config_.is_encrypted(); |
116 } | 116 } |
117 UpdateAndMaybeSwitch(); | 117 UpdateAndMaybeSwitch(); |
118 } | 118 } |
119 | 119 |
120 bool RemotingController::IsVideoCodecSupported() { | 120 bool RemotingRendererController::IsVideoCodecSupported() { |
121 DCHECK(task_runner_->BelongsToCurrentThread()); | 121 DCHECK(thread_checker_.CalledOnValidThread()); |
122 DCHECK(has_video_); | 122 DCHECK(has_video_); |
123 | 123 |
124 switch (video_decoder_config_.codec()) { | 124 switch (video_decoder_config_.codec()) { |
125 case VideoCodec::kCodecH264: | 125 case VideoCodec::kCodecH264: |
126 case VideoCodec::kCodecVP8: | 126 case VideoCodec::kCodecVP8: |
127 return true; | 127 return true; |
128 default: | 128 default: |
129 VLOG(2) << "Remoting does not support video codec: " | 129 VLOG(2) << "Remoting does not support video codec: " |
130 << video_decoder_config_.codec(); | 130 << video_decoder_config_.codec(); |
131 return false; | 131 return false; |
132 } | 132 } |
133 } | 133 } |
134 | 134 |
135 bool RemotingController::IsAudioCodecSupported() { | 135 bool RemotingRendererController::IsAudioCodecSupported() { |
136 DCHECK(task_runner_->BelongsToCurrentThread()); | 136 DCHECK(thread_checker_.CalledOnValidThread()); |
137 DCHECK(has_audio_); | 137 DCHECK(has_audio_); |
138 | 138 |
139 switch (audio_decoder_config_.codec()) { | 139 switch (audio_decoder_config_.codec()) { |
140 case AudioCodec::kCodecAAC: | 140 case AudioCodec::kCodecAAC: |
141 case AudioCodec::kCodecMP3: | 141 case AudioCodec::kCodecMP3: |
142 case AudioCodec::kCodecPCM: | 142 case AudioCodec::kCodecPCM: |
143 case AudioCodec::kCodecVorbis: | 143 case AudioCodec::kCodecVorbis: |
144 case AudioCodec::kCodecFLAC: | 144 case AudioCodec::kCodecFLAC: |
145 case AudioCodec::kCodecAMR_NB: | 145 case AudioCodec::kCodecAMR_NB: |
146 case AudioCodec::kCodecAMR_WB: | 146 case AudioCodec::kCodecAMR_WB: |
147 case AudioCodec::kCodecPCM_MULAW: | 147 case AudioCodec::kCodecPCM_MULAW: |
148 case AudioCodec::kCodecGSM_MS: | 148 case AudioCodec::kCodecGSM_MS: |
149 case AudioCodec::kCodecPCM_S16BE: | 149 case AudioCodec::kCodecPCM_S16BE: |
150 case AudioCodec::kCodecPCM_S24BE: | 150 case AudioCodec::kCodecPCM_S24BE: |
151 case AudioCodec::kCodecOpus: | 151 case AudioCodec::kCodecOpus: |
152 case AudioCodec::kCodecEAC3: | 152 case AudioCodec::kCodecEAC3: |
153 case AudioCodec::kCodecPCM_ALAW: | 153 case AudioCodec::kCodecPCM_ALAW: |
154 case AudioCodec::kCodecALAC: | 154 case AudioCodec::kCodecALAC: |
155 case AudioCodec::kCodecAC3: | 155 case AudioCodec::kCodecAC3: |
156 return true; | 156 return true; |
157 default: | 157 default: |
158 VLOG(2) << "Remoting does not support audio codec: " | 158 VLOG(2) << "Remoting does not support audio codec: " |
159 << audio_decoder_config_.codec(); | 159 << audio_decoder_config_.codec(); |
160 return false; | 160 return false; |
161 } | 161 } |
162 } | 162 } |
163 | 163 |
164 bool RemotingController::ShouldBeRemoting() { | 164 bool RemotingRendererController::ShouldBeRemoting() { |
165 DCHECK(task_runner_->BelongsToCurrentThread()); | 165 DCHECK(thread_checker_.CalledOnValidThread()); |
166 | 166 |
167 // TODO(xjz): The control logic for EME will be added in a later CL. | 167 // Explicitly ignoring |is_fullscreen_| since all EME content should trigger |
168 if (is_encrypted_) | 168 // the start of remoting immediately, and not later after switching into |
169 // fullscreen. This is required by current technical limitations. | |
170 // Note: For encrypted contents, once the CDM is created remotely, we will let | |
171 // RemotingRendererImpl handle all failures, e.g., video/audio codec is not | |
172 // supported, or session is permanently terminated. | |
173 if (is_encrypted_) { | |
174 return remoting_source_->state() == RemotingSessionState::SESSION_STARTED || | |
miu
2016/10/29 02:22:16
Hmm...Things moved around and now I'm confused aga
xjz
2016/10/31 17:24:00
Yes, I should include SESSION_STOPPING here. But t
| |
175 remoting_source_->state() == | |
176 RemotingSessionState::SESSION_PERMANENTLY_STOPPED; | |
177 } | |
178 | |
179 if (!session_can_remote_) | |
169 return false; | 180 return false; |
170 | 181 if (switch_renderer_cb_.is_null()) |
171 if (!is_sink_available_) | |
172 return false; | 182 return false; |
173 if (!is_fullscreen_) | 183 if (!has_audio_ && !has_video_) |
174 return false; | 184 return false; |
175 if (has_video_ && !IsVideoCodecSupported()) | 185 if (has_video_ && !IsVideoCodecSupported()) |
176 return false; | 186 return false; |
177 if (has_audio_ && !IsAudioCodecSupported()) | 187 if (has_audio_ && !IsAudioCodecSupported()) |
178 return false; | 188 return false; |
179 return true; | 189 return is_fullscreen_; |
180 } | 190 } |
181 | 191 |
182 void RemotingController::UpdateAndMaybeSwitch() { | 192 void RemotingRendererController::UpdateAndMaybeSwitch() { |
183 DCHECK(task_runner_->BelongsToCurrentThread()); | 193 DCHECK(thread_checker_.CalledOnValidThread()); |
184 | 194 |
185 // TODO(xjz): The switching logic for encrypted content will be added in a | 195 bool should_be_remoting = ShouldBeRemoting(); |
186 // later CL. | |
187 | 196 |
188 // Demuxer is not initialized yet. | 197 if (remote_rendering_started_ == should_be_remoting) |
189 if (!has_audio_ && !has_video_) | |
190 return; | 198 return; |
191 | 199 |
192 DCHECK(!switch_renderer_cb_.is_null()); | 200 // Switch between local renderer and remoting renderer. |
201 remote_rendering_started_ = should_be_remoting; | |
193 | 202 |
194 bool should_be_remoting = ShouldBeRemoting(); | 203 if (remote_rendering_started_) { |
195 if (is_remoting_ == should_be_remoting) | 204 DCHECK(!switch_renderer_cb_.is_null()); |
196 return; | 205 if (remoting_source_->state() == |
197 | 206 RemotingSessionState::SESSION_PERMANENTLY_STOPPED) { |
198 // Switch between local and remoting. | 207 switch_renderer_cb_.Run(); |
199 is_remoting_ = should_be_remoting; | 208 return; |
200 if (is_remoting_) { | 209 } |
201 // |swithc_renderer_cb_.Run()| will be called after remoting is started | 210 // |switch_renderer_cb_.Run()| will be called after remoting is started |
202 // successfully. | 211 // successfully. |
203 remoter_->Start(); | 212 remoting_source_->StartRemoting(this); |
204 } else { | 213 } else { |
214 DCHECK(!is_encrypted_); | |
miu
2016/10/29 02:22:16
Please add comment to explain this DCHECK (somethi
xjz
2016/10/31 17:24:00
Done.
| |
205 switch_renderer_cb_.Run(); | 215 switch_renderer_cb_.Run(); |
206 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | 216 remoting_source_->StopRemoting(this); |
207 } | 217 } |
208 } | 218 } |
209 | 219 |
210 } // namespace media | 220 } // namespace media |
OLD | NEW |