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