// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/remoting/shared_session.h" #include "base/bind.h" #include "base/logging.h" #include "media/remoting/proto_utils.h" namespace media { namespace remoting { SharedSession::SharedSession(mojom::RemotingSourceRequest source_request, mojom::RemoterPtr remoter) : rpc_broker_(base::Bind(&SharedSession::SendMessageToSink, base::Unretained(this))), binding_(this, std::move(source_request)), remoter_(std::move(remoter)) { DCHECK(remoter_); } SharedSession::~SharedSession() { DCHECK(thread_checker_.CalledOnValidThread()); if (!clients_.empty()) { Shutdown(); clients_.clear(); } } void SharedSession::OnSinkAvailable( mojom::RemotingSinkCapabilities capabilities) { DCHECK(thread_checker_.CalledOnValidThread()); if (capabilities == mojom::RemotingSinkCapabilities::NONE) { OnSinkGone(); return; } sink_capabilities_ = capabilities; if (state_ == SESSION_UNAVAILABLE) UpdateAndNotifyState(SESSION_CAN_START); } void SharedSession::OnSinkGone() { DCHECK(thread_checker_.CalledOnValidThread()); sink_capabilities_ = mojom::RemotingSinkCapabilities::NONE; if (state_ == SESSION_PERMANENTLY_STOPPED) return; if (state_ == SESSION_CAN_START) { UpdateAndNotifyState(SESSION_UNAVAILABLE); return; } if (state_ == SESSION_STARTED || state_ == SESSION_STARTING) { VLOG(1) << "Sink is gone in a remoting session."; // Remoting is being stopped by Remoter. UpdateAndNotifyState(SESSION_STOPPING); } } void SharedSession::OnStarted() { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "Remoting started successively."; if (clients_.empty() || state_ == SESSION_PERMANENTLY_STOPPED || state_ == SESSION_STOPPING) { for (Client* client : clients_) client->OnStarted(false); return; } for (Client* client : clients_) client->OnStarted(true); state_ = SESSION_STARTED; } void SharedSession::OnStartFailed(mojom::RemotingStartFailReason reason) { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "Failed to start remoting:" << reason; for (Client* client : clients_) client->OnStarted(false); if (state_ == SESSION_PERMANENTLY_STOPPED) return; state_ = SESSION_UNAVAILABLE; } void SharedSession::OnStopped(mojom::RemotingStopReason reason) { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "Remoting stopped: " << reason; if (state_ == SESSION_PERMANENTLY_STOPPED) return; UpdateAndNotifyState(SESSION_UNAVAILABLE); } void SharedSession::OnMessageFromSink(const std::vector& message) { DCHECK(thread_checker_.CalledOnValidThread()); std::unique_ptr rpc(new pb::RpcMessage()); if (!rpc->ParseFromArray(message.data(), message.size())) { VLOG(1) << "corrupted Rpc message"; Shutdown(); return; } rpc_broker_.ProcessMessageFromRemote(std::move(rpc)); } void SharedSession::UpdateAndNotifyState(SessionState state) { DCHECK(thread_checker_.CalledOnValidThread()); if (state_ == state) return; state_ = state; for (Client* client : clients_) client->OnSessionStateChanged(); } void SharedSession::StartRemoting(Client* client) { DCHECK(std::find(clients_.begin(), clients_.end(), client) != clients_.end()); switch (state_) { case SESSION_CAN_START: remoter_->Start(); UpdateAndNotifyState(SESSION_STARTING); break; case SESSION_STARTING: break; case SESSION_STARTED: client->OnStarted(true); break; case SESSION_STOPPING: case SESSION_UNAVAILABLE: case SESSION_PERMANENTLY_STOPPED: client->OnStarted(false); break; } } void SharedSession::StopRemoting(Client* client) { DCHECK(std::find(clients_.begin(), clients_.end(), client) != clients_.end()); VLOG(1) << "SharedSession::StopRemoting: " << state_; if (state_ != SESSION_STARTING && state_ != SESSION_STARTED) return; remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); UpdateAndNotifyState(SESSION_STOPPING); } void SharedSession::AddClient(Client* client) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(std::find(clients_.begin(), clients_.end(), client) == clients_.end()); clients_.push_back(client); } void SharedSession::RemoveClient(Client* client) { DCHECK(thread_checker_.CalledOnValidThread()); auto it = std::find(clients_.begin(), clients_.end(), client); DCHECK(it != clients_.end()); clients_.erase(it); if (clients_.empty() && (state_ == SESSION_STARTED || state_ == SESSION_STARTING)) { remoter_->Stop(mojom::RemotingStopReason::SOURCE_GONE); state_ = SESSION_STOPPING; } } void SharedSession::Shutdown() { DCHECK(thread_checker_.CalledOnValidThread()); if (state_ == SESSION_STARTED || state_ == SESSION_STARTING) remoter_->Stop(mojom::RemotingStopReason::UNEXPECTED_FAILURE); UpdateAndNotifyState(SESSION_PERMANENTLY_STOPPED); } void SharedSession::StartDataPipe( std::unique_ptr audio_data_pipe, std::unique_ptr video_data_pipe, const DataPipeStartCallback& done_callback) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!done_callback.is_null()); bool audio = audio_data_pipe != nullptr; bool video = video_data_pipe != nullptr; if (!audio && !video) { LOG(ERROR) << "No audio nor video to establish data pipe"; done_callback.Run(mojom::RemotingDataStreamSenderPtrInfo(), mojom::RemotingDataStreamSenderPtrInfo(), mojo::ScopedDataPipeProducerHandle(), mojo::ScopedDataPipeProducerHandle()); return; } mojom::RemotingDataStreamSenderPtr audio_stream_sender; mojom::RemotingDataStreamSenderPtr video_stream_sender; remoter_->StartDataStreams(audio ? std::move(audio_data_pipe->consumer_handle) : mojo::ScopedDataPipeConsumerHandle(), video ? std::move(video_data_pipe->consumer_handle) : mojo::ScopedDataPipeConsumerHandle(), audio ? mojo::MakeRequest(&audio_stream_sender) : mojom::RemotingDataStreamSenderRequest(), video ? mojo::MakeRequest(&video_stream_sender) : mojom::RemotingDataStreamSenderRequest()); done_callback.Run(audio_stream_sender.PassInterface(), video_stream_sender.PassInterface(), audio ? std::move(audio_data_pipe->producer_handle) : mojo::ScopedDataPipeProducerHandle(), video ? std::move(video_data_pipe->producer_handle) : mojo::ScopedDataPipeProducerHandle()); } void SharedSession::SendMessageToSink( std::unique_ptr> message) { DCHECK(thread_checker_.CalledOnValidThread()); remoter_->SendMessageToSink(*message); } } // namespace remoting } // namespace media