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_source_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/callback_helpers.h" | |
| 10 #include "base/logging.h" | 8 #include "base/logging.h" |
| 11 #include "base/single_thread_task_runner.h" | |
| 12 #include "media/remoting/rpc/proto_utils.h" | 9 #include "media/remoting/rpc/proto_utils.h" |
| 13 #include "media/remoting/rpc/rpc_broker.h" | |
| 14 | 10 |
| 15 namespace media { | 11 namespace media { |
| 16 | 12 |
| 17 RemotingController::RemotingController( | 13 RemotingSourceImpl::RemotingSourceImpl( |
| 18 mojom::RemotingSourceRequest source_request, | 14 mojom::RemotingSourceRequest source_request, |
| 19 mojom::RemoterPtr remoter) | 15 mojom::RemoterPtr remoter) |
| 20 : binding_(this, std::move(source_request)), | 16 : rpc_broker_(base::Bind(&RemotingSourceImpl::SendMessageToSink, this)), |
| 21 remoter_(std::move(remoter)), | 17 binding_(this, std::move(source_request)), |
| 22 task_runner_(base::ThreadTaskRunnerHandle::Get()), | 18 remoter_(std::move(remoter)) { |
| 23 weak_factory_(this) { | |
| 24 DCHECK(remoter_); | 19 DCHECK(remoter_); |
| 25 rpc_broker_.reset(new remoting::RpcBroker(base::Bind( | 20 } |
| 26 &RemotingController::OnSendMessageToSink, weak_factory_.GetWeakPtr()))); | 21 |
| 27 } | 22 RemotingSourceImpl::~RemotingSourceImpl() { |
| 28 | 23 DCHECK(thread_checker_.CalledOnValidThread()); |
| 29 RemotingController::~RemotingController() {} | 24 |
| 30 | 25 if (!clients_.empty()) { |
| 31 void RemotingController::StartDataPipe( | 26 Shutdown(); |
| 27 clients_.clear(); | |
| 28 } | |
| 29 } | |
| 30 | |
| 31 void RemotingSourceImpl::OnSinkAvailable() { | |
| 32 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 33 | |
| 34 if (state_ == RemotingSessionState::SESSION_UNAVAILABLE) | |
| 35 UpdateAndNotifyState(RemotingSessionState::SESSION_CAN_START); | |
| 36 } | |
| 37 | |
| 38 void RemotingSourceImpl::OnSinkGone() { | |
| 39 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 40 | |
| 41 if (state_ == RemotingSessionState::SESSION_PERMANENTLY_STOPPED) | |
| 42 return; | |
| 43 if (state_ == RemotingSessionState::SESSION_CAN_START) { | |
| 44 UpdateAndNotifyState(RemotingSessionState::SESSION_UNAVAILABLE); | |
| 45 return; | |
| 46 } | |
| 47 if (state_ == RemotingSessionState::SESSION_STARTED || | |
| 48 state_ == RemotingSessionState::SESSION_STARTING) { | |
| 49 VLOG(1) << "Sink is gone in a remoting session."; | |
| 50 // Remoting is being stopped by Remoter. | |
| 51 UpdateAndNotifyState(RemotingSessionState::SESSION_STOPPING); | |
| 52 } | |
| 53 } | |
| 54 | |
| 55 void RemotingSourceImpl::OnStarted() { | |
| 56 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 57 | |
| 58 VLOG(1) << "Remoting started successively."; | |
| 59 if (clients_.empty() || | |
| 60 state_ == RemotingSessionState::SESSION_PERMANENTLY_STOPPED || | |
| 61 state_ == RemotingSessionState::SESSION_STOPPING) { | |
| 62 for (Client* client : clients_) | |
| 63 client->OnStarted(false); | |
| 64 return; | |
| 65 } | |
| 66 for (Client* client : clients_) | |
| 67 client->OnStarted(true); | |
| 68 state_ = RemotingSessionState::SESSION_STARTED; | |
| 69 } | |
| 70 | |
| 71 void RemotingSourceImpl::OnStartFailed(mojom::RemotingStartFailReason reason) { | |
| 72 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 73 | |
| 74 VLOG(1) << "Failed to start remoting:" << reason; | |
| 75 for (Client* client : clients_) | |
| 76 client->OnStarted(false); | |
| 77 if (state_ == RemotingSessionState::SESSION_PERMANENTLY_STOPPED) | |
| 78 return; | |
| 79 state_ = RemotingSessionState::SESSION_UNAVAILABLE; | |
| 80 } | |
| 81 | |
| 82 void RemotingSourceImpl::OnStopped(mojom::RemotingStopReason reason) { | |
| 83 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 84 | |
| 85 VLOG(1) << "Remoting stopped: " << reason; | |
| 86 if (state_ == RemotingSessionState::SESSION_PERMANENTLY_STOPPED) | |
| 87 return; | |
| 88 RemotingSessionState state = RemotingSessionState::SESSION_UNAVAILABLE; | |
| 89 UpdateAndNotifyState(state); | |
| 90 } | |
| 91 | |
| 92 void RemotingSourceImpl::OnMessageFromSink( | |
| 93 const std::vector<uint8_t>& message) { | |
| 94 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 95 | |
| 96 std::unique_ptr<remoting::pb::RpcMessage> rpc(new remoting::pb::RpcMessage()); | |
| 97 if (!rpc->ParseFromArray(message.data(), message.size())) { | |
| 98 LOG(ERROR) << "corrupted Rpc message"; | |
| 99 Shutdown(); | |
| 100 return; | |
| 101 } | |
| 102 rpc_broker_.ProcessMessageFromRemote(std::move(rpc)); | |
| 103 } | |
| 104 | |
| 105 void RemotingSourceImpl::UpdateAndNotifyState(RemotingSessionState state) { | |
| 106 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 107 | |
| 108 if (state_ == state) | |
| 109 return; | |
| 110 state_ = state; | |
| 111 for (Client* client : clients_) | |
| 112 client->OnSessionStateChanged(); | |
| 113 } | |
| 114 | |
| 115 void RemotingSourceImpl::StartRemoting(Client* client) { | |
| 116 DCHECK(std::find(clients_.begin(), clients_.end(), client) != clients_.end()); | |
| 117 | |
| 118 switch (state_) { | |
| 119 case SESSION_CAN_START: | |
| 120 remoter_->Start(); | |
| 121 UpdateAndNotifyState(RemotingSessionState::SESSION_STARTING); | |
| 122 break; | |
| 123 case SESSION_STARTING: | |
| 124 break; | |
| 125 case SESSION_STARTED: | |
| 126 client->OnStarted(true); | |
| 127 break; | |
| 128 case SESSION_STOPPING: | |
| 129 case SESSION_UNAVAILABLE: | |
| 130 case SESSION_PERMANENTLY_STOPPED: | |
| 131 client->OnStarted(false); | |
| 132 break; | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 void RemotingSourceImpl::StopRemoting(Client* client) { | |
| 137 DCHECK(std::find(clients_.begin(), clients_.end(), client) != clients_.end()); | |
| 138 | |
| 139 VLOG(1) << "RemotingSourceImpl::StopRemoting: " << state_; | |
| 140 | |
| 141 if (state_ != RemotingSessionState::SESSION_STARTING && | |
| 142 state_ != RemotingSessionState::SESSION_STARTED) | |
| 143 return; | |
| 144 | |
| 145 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | |
| 146 UpdateAndNotifyState(RemotingSessionState::SESSION_STOPPING); | |
| 147 } | |
| 148 | |
| 149 void RemotingSourceImpl::AddClient(Client* client) { | |
| 150 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 151 DCHECK(std::find(clients_.begin(), clients_.end(), client) == clients_.end()); | |
| 152 | |
| 153 clients_.push_back(client); | |
| 154 client->OnSessionStateChanged(); | |
| 155 } | |
| 156 | |
| 157 void RemotingSourceImpl::RemoveClient(Client* client) { | |
| 158 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 159 | |
| 160 auto it = std::find(clients_.begin(), clients_.end(), client); | |
| 161 DCHECK(it != clients_.end()); | |
| 162 | |
| 163 clients_.erase(it); | |
| 164 if (clients_.empty() && (state_ == RemotingSessionState::SESSION_STARTED || | |
| 165 state_ == RemotingSessionState::SESSION_STARTING)) { | |
| 166 remoter_->Stop(mojom::RemotingStopReason::SOURCE_GONE); | |
| 167 state_ = RemotingSessionState::SESSION_STOPPING; | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 void RemotingSourceImpl::Shutdown() { | |
| 172 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 173 | |
| 174 if (state_ == RemotingSessionState::SESSION_STARTED || | |
| 175 state_ == RemotingSessionState::SESSION_STARTING) | |
| 176 remoter_->Stop(mojom::RemotingStopReason::UNEXPECTED_FAILURE); | |
| 177 UpdateAndNotifyState(RemotingSessionState::SESSION_PERMANENTLY_STOPPED); | |
| 178 } | |
| 179 | |
| 180 void RemotingSourceImpl::StartDataPipe( | |
| 32 std::unique_ptr<mojo::DataPipe> audio_data_pipe, | 181 std::unique_ptr<mojo::DataPipe> audio_data_pipe, |
| 33 std::unique_ptr<mojo::DataPipe> video_data_pipe, | 182 std::unique_ptr<mojo::DataPipe> video_data_pipe, |
| 34 const DataPipeStartCallback& done_callback) { | 183 const DataPipeStartCallback& done_callback) { |
| 35 VLOG(2) << __FUNCTION__; | 184 DCHECK(thread_checker_.CalledOnValidThread()); |
| 36 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 37 DCHECK(!done_callback.is_null()); | 185 DCHECK(!done_callback.is_null()); |
| 186 | |
| 38 bool audio = audio_data_pipe != nullptr; | 187 bool audio = audio_data_pipe != nullptr; |
| 39 bool video = video_data_pipe != nullptr; | 188 bool video = video_data_pipe != nullptr; |
| 40 | |
| 41 if (!audio && !video) { | 189 if (!audio && !video) { |
| 42 LOG(ERROR) << "No audio and video to establish data pipe"; | 190 LOG(ERROR) << "No audio and video to establish data pipe"; |
| 43 done_callback.Run(mojom::RemotingDataStreamSenderPtrInfo(), | 191 done_callback.Run(mojom::RemotingDataStreamSenderPtrInfo(), |
| 44 mojom::RemotingDataStreamSenderPtrInfo(), | 192 mojom::RemotingDataStreamSenderPtrInfo(), |
| 45 mojo::ScopedDataPipeProducerHandle(), | 193 mojo::ScopedDataPipeProducerHandle(), |
| 46 mojo::ScopedDataPipeProducerHandle()); | 194 mojo::ScopedDataPipeProducerHandle()); |
| 47 return; | 195 return; |
| 48 } | 196 } |
| 49 | |
| 50 mojom::RemotingDataStreamSenderPtr audio_stream_sender; | 197 mojom::RemotingDataStreamSenderPtr audio_stream_sender; |
| 51 mojom::RemotingDataStreamSenderPtr video_stream_sender; | 198 mojom::RemotingDataStreamSenderPtr video_stream_sender; |
| 52 remoter_->StartDataStreams( | 199 remoter_->StartDataStreams( |
| 53 audio ? std::move(audio_data_pipe->consumer_handle) | 200 audio ? std::move(audio_data_pipe->consumer_handle) |
| 54 : mojo::ScopedDataPipeConsumerHandle(), | 201 : mojo::ScopedDataPipeConsumerHandle(), |
| 55 video ? std::move(video_data_pipe->consumer_handle) | 202 video ? std::move(video_data_pipe->consumer_handle) |
| 56 : mojo::ScopedDataPipeConsumerHandle(), | 203 : mojo::ScopedDataPipeConsumerHandle(), |
| 57 audio ? mojo::GetProxy(&audio_stream_sender) | 204 audio ? mojo::GetProxy(&audio_stream_sender) |
| 58 : media::mojom::RemotingDataStreamSenderRequest(), | 205 : media::mojom::RemotingDataStreamSenderRequest(), |
| 59 video ? mojo::GetProxy(&video_stream_sender) | 206 video ? mojo::GetProxy(&video_stream_sender) |
| 60 : media::mojom::RemotingDataStreamSenderRequest()); | 207 : media::mojom::RemotingDataStreamSenderRequest()); |
| 61 | |
| 62 done_callback.Run(audio_stream_sender.PassInterface(), | 208 done_callback.Run(audio_stream_sender.PassInterface(), |
| 63 video_stream_sender.PassInterface(), | 209 video_stream_sender.PassInterface(), |
| 64 audio ? std::move(audio_data_pipe->producer_handle) | 210 audio ? std::move(audio_data_pipe->producer_handle) |
| 65 : mojo::ScopedDataPipeProducerHandle(), | 211 : mojo::ScopedDataPipeProducerHandle(), |
| 66 video ? std::move(video_data_pipe->producer_handle) | 212 video ? std::move(video_data_pipe->producer_handle) |
| 67 : mojo::ScopedDataPipeProducerHandle()); | 213 : mojo::ScopedDataPipeProducerHandle()); |
| 68 } | 214 } |
| 69 | 215 |
| 70 base::WeakPtr<remoting::RpcBroker> RemotingController::GetRpcBroker() const { | 216 remoting::RpcBroker* RemotingSourceImpl::GetRpcBroker() const { |
| 71 DCHECK(task_runner_->BelongsToCurrentThread()); | 217 DCHECK(thread_checker_.CalledOnValidThread()); |
| 72 return rpc_broker_->GetWeakPtr(); | 218 return const_cast<remoting::RpcBroker*>(&rpc_broker_); |
|
miu
2016/11/07 21:54:02
nit: Please add a TODO comment for fixing const-co
xjz
2016/11/08 00:59:06
Done.
| |
| 73 } | 219 } |
| 74 | 220 |
| 75 void RemotingController::OnSendMessageToSink( | 221 void RemotingSourceImpl::SendMessageToSink( |
| 76 std::unique_ptr<std::vector<uint8_t>> message) { | 222 std::unique_ptr<std::vector<uint8_t>> message) { |
| 77 DCHECK(task_runner_->BelongsToCurrentThread()); | 223 DCHECK(thread_checker_.CalledOnValidThread()); |
| 78 remoter_->SendMessageToSink(*message); | 224 remoter_->SendMessageToSink(*message); |
| 79 } | 225 } |
| 80 | 226 |
| 81 void RemotingController::OnSinkAvailable() { | |
| 82 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 83 | |
| 84 is_sink_available_ = true; | |
| 85 UpdateAndMaybeSwitch(); | |
| 86 } | |
| 87 | |
| 88 void RemotingController::OnSinkGone() { | |
| 89 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 90 | |
| 91 is_sink_available_ = false; | |
| 92 UpdateAndMaybeSwitch(); | |
| 93 } | |
| 94 | |
| 95 void RemotingController::OnStarted() { | |
| 96 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 97 | |
| 98 VLOG(1) << "Remoting started successively."; | |
| 99 if (is_remoting_) | |
| 100 switch_renderer_cb_.Run(); | |
| 101 else | |
| 102 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | |
| 103 } | |
| 104 | |
| 105 void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) { | |
| 106 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 107 | |
| 108 VLOG(1) << "Failed to start remoting:" << reason; | |
| 109 is_remoting_ = false; | |
| 110 } | |
| 111 | |
| 112 void RemotingController::OnMessageFromSink( | |
| 113 const std::vector<uint8_t>& message) { | |
| 114 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 115 std::unique_ptr<remoting::pb::RpcMessage> rpc(new remoting::pb::RpcMessage()); | |
| 116 if (!rpc->ParseFromArray(message.data(), message.size())) { | |
| 117 LOG(ERROR) << "corrupted Rpc message"; | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 rpc_broker_->ProcessMessageFromRemote(std::move(rpc)); | |
| 122 } | |
| 123 | |
| 124 void RemotingController::OnStopped(mojom::RemotingStopReason reason) { | |
| 125 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 126 | |
| 127 VLOG(1) << "Remoting stopped: " << reason; | |
| 128 is_remoting_ = false; | |
| 129 } | |
| 130 | |
| 131 void RemotingController::OnEnteredFullscreen() { | |
| 132 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 133 | |
| 134 is_fullscreen_ = true; | |
| 135 UpdateAndMaybeSwitch(); | |
| 136 } | |
| 137 | |
| 138 void RemotingController::OnExitedFullscreen() { | |
| 139 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 140 | |
| 141 is_fullscreen_ = false; | |
| 142 UpdateAndMaybeSwitch(); | |
| 143 } | |
| 144 | |
| 145 void RemotingController::OnSetCdm(CdmContext* cdm_context) { | |
| 146 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 147 | |
| 148 // TODO(xjz): Not implemented. Will add in up-coming change. | |
| 149 NOTIMPLEMENTED(); | |
| 150 } | |
| 151 | |
| 152 void RemotingController::SetSwitchRendererCallback( | |
| 153 const SwitchRendererCallback& cb) { | |
| 154 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 155 DCHECK(!cb.is_null()); | |
| 156 | |
| 157 switch_renderer_cb_ = cb; | |
| 158 } | |
| 159 | |
| 160 void RemotingController::OnMetadataChanged(const PipelineMetadata& metadata) { | |
| 161 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 162 | |
| 163 has_video_ = metadata.has_video; | |
| 164 has_audio_ = metadata.has_audio; | |
| 165 if (!has_video_ && !has_audio_) | |
| 166 return; | |
| 167 | |
| 168 // On Android, when using the MediaPlayerRenderer, |has_video_| and | |
| 169 // |has_audio_| will be true, but the respective configs will be empty. | |
| 170 // We cannot make any assumptions on the validity of configs. | |
| 171 if (has_video_) { | |
| 172 video_decoder_config_ = metadata.video_decoder_config; | |
| 173 is_encrypted_ |= video_decoder_config_.is_encrypted(); | |
| 174 } | |
| 175 if (has_audio_) { | |
| 176 audio_decoder_config_ = metadata.audio_decoder_config; | |
| 177 is_encrypted_ |= audio_decoder_config_.is_encrypted(); | |
| 178 } | |
| 179 UpdateAndMaybeSwitch(); | |
| 180 } | |
| 181 | |
| 182 bool RemotingController::IsVideoCodecSupported() { | |
| 183 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 184 DCHECK(has_video_); | |
| 185 | |
| 186 switch (video_decoder_config_.codec()) { | |
| 187 case VideoCodec::kCodecH264: | |
| 188 case VideoCodec::kCodecVP8: | |
| 189 return true; | |
| 190 default: | |
| 191 VLOG(2) << "Remoting does not support video codec: " | |
| 192 << video_decoder_config_.codec(); | |
| 193 return false; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 bool RemotingController::IsAudioCodecSupported() { | |
| 198 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 199 DCHECK(has_audio_); | |
| 200 | |
| 201 switch (audio_decoder_config_.codec()) { | |
| 202 case AudioCodec::kCodecAAC: | |
| 203 case AudioCodec::kCodecMP3: | |
| 204 case AudioCodec::kCodecPCM: | |
| 205 case AudioCodec::kCodecVorbis: | |
| 206 case AudioCodec::kCodecFLAC: | |
| 207 case AudioCodec::kCodecAMR_NB: | |
| 208 case AudioCodec::kCodecAMR_WB: | |
| 209 case AudioCodec::kCodecPCM_MULAW: | |
| 210 case AudioCodec::kCodecGSM_MS: | |
| 211 case AudioCodec::kCodecPCM_S16BE: | |
| 212 case AudioCodec::kCodecPCM_S24BE: | |
| 213 case AudioCodec::kCodecOpus: | |
| 214 case AudioCodec::kCodecEAC3: | |
| 215 case AudioCodec::kCodecPCM_ALAW: | |
| 216 case AudioCodec::kCodecALAC: | |
| 217 case AudioCodec::kCodecAC3: | |
| 218 return true; | |
| 219 default: | |
| 220 VLOG(2) << "Remoting does not support audio codec: " | |
| 221 << audio_decoder_config_.codec(); | |
| 222 return false; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 bool RemotingController::ShouldBeRemoting() { | |
| 227 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 228 | |
| 229 // TODO(xjz): The control logic for EME will be added in a later CL. | |
| 230 if (is_encrypted_) | |
| 231 return false; | |
| 232 | |
| 233 if (!is_sink_available_) | |
| 234 return false; | |
| 235 if (!is_fullscreen_) | |
| 236 return false; | |
| 237 if (has_video_ && !IsVideoCodecSupported()) | |
| 238 return false; | |
| 239 if (has_audio_ && !IsAudioCodecSupported()) | |
| 240 return false; | |
| 241 return true; | |
| 242 } | |
| 243 | |
| 244 void RemotingController::UpdateAndMaybeSwitch() { | |
| 245 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 246 | |
| 247 // TODO(xjz): The switching logic for encrypted content will be added in a | |
| 248 // later CL. | |
| 249 | |
| 250 // Demuxer is not initialized yet. | |
| 251 if (!has_audio_ && !has_video_) | |
| 252 return; | |
| 253 | |
| 254 DCHECK(!switch_renderer_cb_.is_null()); | |
| 255 | |
| 256 bool should_be_remoting = ShouldBeRemoting(); | |
| 257 if (is_remoting_ == should_be_remoting) | |
| 258 return; | |
| 259 | |
| 260 // Switch between local and remoting. | |
| 261 is_remoting_ = should_be_remoting; | |
| 262 if (is_remoting_) { | |
| 263 // |swithc_renderer_cb_.Run()| will be called after remoting is started | |
| 264 // successfully. | |
| 265 remoter_->Start(); | |
| 266 } else { | |
| 267 switch_renderer_cb_.Run(); | |
| 268 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 } // namespace media | 227 } // namespace media |
| OLD | NEW |