/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "ClientHandler" #include "host/frontend/webrtc/lib/client_handler.h" #include #include #include #include #include #include #include "host/frontend/webrtc/lib/keyboard.h" #include "host/frontend/webrtc/lib/utils.h" #include "host/libs/config/cuttlefish_config.h" namespace cuttlefish { namespace webrtc_streaming { namespace { static constexpr auto kInputChannelLabel = "input-channel"; static constexpr auto kAdbChannelLabel = "adb-channel"; static constexpr auto kBluetoothChannelLabel = "bluetooth-channel"; static constexpr auto kCameraDataChannelLabel = "camera-data-channel"; static constexpr auto kCameraDataEof = "EOF"; class CvdCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver { public: CvdCreateSessionDescriptionObserver( std::weak_ptr client_handler) : client_handler_(client_handler) {} void OnSuccess(webrtc::SessionDescriptionInterface *desc) override { auto client_handler = client_handler_.lock(); if (client_handler) { client_handler->OnCreateSDPSuccess(desc); } } void OnFailure(webrtc::RTCError error) override { auto client_handler = client_handler_.lock(); if (client_handler) { client_handler->OnCreateSDPFailure(error); } } private: std::weak_ptr client_handler_; }; class CvdSetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { public: CvdSetSessionDescriptionObserver(std::weak_ptr client_handler) : client_handler_(client_handler) {} void OnSuccess() override { // local description set, nothing else to do } void OnFailure(webrtc::RTCError error) override { auto client_handler = client_handler_.lock(); if (client_handler) { client_handler->OnSetSDPFailure(error); } } private: std::weak_ptr client_handler_; }; class CvdOnSetRemoteDescription : public webrtc::SetRemoteDescriptionObserverInterface { public: CvdOnSetRemoteDescription( std::function on_error) : on_error_(on_error) {} void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { on_error_(error); } private: std::function on_error_; }; } // namespace // Video streams initiating in the client may be added and removed at unexpected // times, causing the webrtc objects to be destroyed and created every time. // This class hides away that complexity and allows to set up sinks only once. class ClientVideoTrackImpl : public ClientVideoTrackInterface { public: void AddOrUpdateSink(rtc::VideoSinkInterface *sink, const rtc::VideoSinkWants &wants) override { sink_ = sink; wants_ = wants; if (video_track_) { video_track_->AddOrUpdateSink(sink, wants); } } void SetVideoTrack(webrtc::VideoTrackInterface *track) { video_track_ = track; if (sink_) { video_track_->AddOrUpdateSink(sink_, wants_); } } void UnsetVideoTrack(webrtc::VideoTrackInterface *track) { if (track == video_track_) { video_track_ = nullptr; } } private: webrtc::VideoTrackInterface* video_track_; rtc::VideoSinkInterface *sink_ = nullptr; rtc::VideoSinkWants wants_ = {}; }; class InputChannelHandler : public webrtc::DataChannelObserver { public: InputChannelHandler( rtc::scoped_refptr input_channel, std::shared_ptr observer); ~InputChannelHandler() override; void OnStateChange() override; void OnMessage(const webrtc::DataBuffer &msg) override; private: rtc::scoped_refptr input_channel_; std::shared_ptr observer_; }; class AdbChannelHandler : public webrtc::DataChannelObserver { public: AdbChannelHandler( rtc::scoped_refptr adb_channel, std::shared_ptr observer); ~AdbChannelHandler() override; void OnStateChange() override; void OnMessage(const webrtc::DataBuffer &msg) override; private: rtc::scoped_refptr adb_channel_; std::shared_ptr observer_; bool channel_open_reported_ = false; }; class ControlChannelHandler : public webrtc::DataChannelObserver { public: ControlChannelHandler( rtc::scoped_refptr control_channel, std::shared_ptr observer); ~ControlChannelHandler() override; void OnStateChange() override; void OnMessage(const webrtc::DataBuffer &msg) override; void Send(const Json::Value &message); void Send(const uint8_t *msg, size_t size, bool binary); private: rtc::scoped_refptr control_channel_; std::shared_ptr observer_; }; class BluetoothChannelHandler : public webrtc::DataChannelObserver { public: BluetoothChannelHandler( rtc::scoped_refptr bluetooth_channel, std::shared_ptr observer); ~BluetoothChannelHandler() override; void OnStateChange() override; void OnMessage(const webrtc::DataBuffer &msg) override; private: rtc::scoped_refptr bluetooth_channel_; std::shared_ptr observer_; bool channel_open_reported_ = false; }; class CameraChannelHandler : public webrtc::DataChannelObserver { public: CameraChannelHandler( rtc::scoped_refptr bluetooth_channel, std::shared_ptr observer); ~CameraChannelHandler() override; void OnStateChange() override; void OnMessage(const webrtc::DataBuffer &msg) override; private: rtc::scoped_refptr camera_channel_; std::shared_ptr observer_; std::vector receive_buffer_; }; InputChannelHandler::InputChannelHandler( rtc::scoped_refptr input_channel, std::shared_ptr observer) : input_channel_(input_channel), observer_(observer) { input_channel->RegisterObserver(this); } InputChannelHandler::~InputChannelHandler() { input_channel_->UnregisterObserver(); } void InputChannelHandler::OnStateChange() { LOG(VERBOSE) << "Input channel state changed to " << webrtc::DataChannelInterface::DataStateString( input_channel_->state()); } void InputChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { if (msg.binary) { // TODO (jemoreira) consider binary protocol to avoid JSON parsing overhead LOG(ERROR) << "Received invalid (binary) data on input channel"; return; } auto size = msg.size(); Json::Value evt; Json::CharReaderBuilder builder; std::unique_ptr json_reader(builder.newCharReader()); std::string errorMessage; auto str = msg.data.cdata(); if (!json_reader->parse(str, str + size, &evt, &errorMessage) < 0) { LOG(ERROR) << "Received invalid JSON object over input channel: " << errorMessage; return; } if (!evt.isMember("type") || !evt["type"].isString()) { LOG(ERROR) << "Input event doesn't have a valid 'type' field: " << evt.toStyledString(); return; } auto event_type = evt["type"].asString(); if (event_type == "mouse") { auto result = ValidationResult::ValidateJsonObject(evt, "mouse", {{"down", Json::ValueType::intValue}, {"x", Json::ValueType::intValue}, {"y", Json::ValueType::intValue}, {"display_label", Json::ValueType::stringValue}}); if (!result.ok()) { LOG(ERROR) << result.error(); return; } auto label = evt["display_label"].asString(); int32_t down = evt["down"].asInt(); int32_t x = evt["x"].asInt(); int32_t y = evt["y"].asInt(); observer_->OnTouchEvent(label, x, y, down); } else if (event_type == "multi-touch") { auto result = ValidationResult::ValidateJsonObject(evt, "multi-touch", {{"id", Json::ValueType::arrayValue}, {"down", Json::ValueType::intValue}, {"x", Json::ValueType::arrayValue}, {"y", Json::ValueType::arrayValue}, {"slot", Json::ValueType::arrayValue}, {"display_label", Json::ValueType::stringValue}}); if (!result.ok()) { LOG(ERROR) << result.error(); return; } auto label = evt["display_label"].asString(); auto idArr = evt["id"]; int32_t down = evt["down"].asInt(); auto xArr = evt["x"]; auto yArr = evt["y"]; auto slotArr = evt["slot"]; int size = evt["id"].size(); observer_->OnMultiTouchEvent(label, idArr, slotArr, xArr, yArr, down, size); } else if (event_type == "keyboard") { auto result = ValidationResult::ValidateJsonObject(evt, "keyboard", {{"event_type", Json::ValueType::stringValue}, {"keycode", Json::ValueType::stringValue}}); if (!result.ok()) { LOG(ERROR) << result.error(); return; } auto down = evt["event_type"].asString() == std::string("keydown"); auto code = DomKeyCodeToLinux(evt["keycode"].asString()); observer_->OnKeyboardEvent(code, down); } else { LOG(ERROR) << "Unrecognized event type: " << event_type; return; } } AdbChannelHandler::AdbChannelHandler( rtc::scoped_refptr adb_channel, std::shared_ptr observer) : adb_channel_(adb_channel), observer_(observer) { adb_channel->RegisterObserver(this); } AdbChannelHandler::~AdbChannelHandler() { adb_channel_->UnregisterObserver(); } void AdbChannelHandler::OnStateChange() { LOG(VERBOSE) << "Adb channel state changed to " << webrtc::DataChannelInterface::DataStateString( adb_channel_->state()); } void AdbChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { // Report the adb channel as open on the first message received instead of at // channel open, this avoids unnecessarily connecting to the adb daemon for // clients that don't use ADB. if (!channel_open_reported_) { observer_->OnAdbChannelOpen([this](const uint8_t *msg, size_t size) { webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), true /*binary*/); // TODO (b/185832105): When the SCTP channel is congested data channel // messages are buffered up to 16MB, when the buffer is full the channel // is abruptly closed. Keep track of the buffered data to avoid losing the // adb data channel. adb_channel_->Send(buffer); return true; }); channel_open_reported_ = true; } observer_->OnAdbMessage(msg.data.cdata(), msg.size()); } ControlChannelHandler::ControlChannelHandler( rtc::scoped_refptr control_channel, std::shared_ptr observer) : control_channel_(control_channel), observer_(observer) { control_channel->RegisterObserver(this); observer_->OnControlChannelOpen([this](const Json::Value& message) { this->Send(message); return true; }); } ControlChannelHandler::~ControlChannelHandler() { control_channel_->UnregisterObserver(); } void ControlChannelHandler::OnStateChange() { LOG(VERBOSE) << "Control channel state changed to " << webrtc::DataChannelInterface::DataStateString( control_channel_->state()); } void ControlChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { observer_->OnControlMessage(msg.data.cdata(), msg.size()); } void ControlChannelHandler::Send(const Json::Value& message) { Json::StreamWriterBuilder factory; std::string message_string = Json::writeString(factory, message); Send(reinterpret_cast(message_string.c_str()), message_string.size(), /*binary=*/false); } void ControlChannelHandler::Send(const uint8_t *msg, size_t size, bool binary) { webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), binary); control_channel_->Send(buffer); } BluetoothChannelHandler::BluetoothChannelHandler( rtc::scoped_refptr bluetooth_channel, std::shared_ptr observer) : bluetooth_channel_(bluetooth_channel), observer_(observer) { bluetooth_channel_->RegisterObserver(this); } BluetoothChannelHandler::~BluetoothChannelHandler() { bluetooth_channel_->UnregisterObserver(); } void BluetoothChannelHandler::OnStateChange() { LOG(VERBOSE) << "Bluetooth channel state changed to " << webrtc::DataChannelInterface::DataStateString( bluetooth_channel_->state()); } void BluetoothChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { // Notify bluetooth channel opening when actually using the channel, // it has the same reason with AdbChannelHandler::OnMessage, // to avoid unnecessarily connection for Rootcanal. if (channel_open_reported_ == false) { channel_open_reported_ = true; observer_->OnBluetoothChannelOpen([this](const uint8_t *msg, size_t size) { webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size), true /*binary*/); // TODO (b/185832105): When the SCTP channel is congested data channel // messages are buffered up to 16MB, when the buffer is full the channel // is abruptly closed. Keep track of the buffered data to avoid losing the // adb data channel. bluetooth_channel_->Send(buffer); return true; }); } observer_->OnBluetoothMessage(msg.data.cdata(), msg.size()); } CameraChannelHandler::CameraChannelHandler( rtc::scoped_refptr camera_channel, std::shared_ptr observer) : camera_channel_(camera_channel), observer_(observer) { camera_channel_->RegisterObserver(this); } CameraChannelHandler::~CameraChannelHandler() { camera_channel_->UnregisterObserver(); } void CameraChannelHandler::OnStateChange() { LOG(VERBOSE) << "Camera channel state changed to " << webrtc::DataChannelInterface::DataStateString( camera_channel_->state()); } void CameraChannelHandler::OnMessage(const webrtc::DataBuffer &msg) { auto msg_data = msg.data.cdata(); if (msg.size() == strlen(kCameraDataEof) && !strncmp(msg_data, kCameraDataEof, msg.size())) { // Send complete buffer to observer on EOF marker observer_->OnCameraData(receive_buffer_); receive_buffer_.clear(); return; } // Otherwise buffer up data receive_buffer_.insert(receive_buffer_.end(), msg_data, msg_data + msg.size()); } std::shared_ptr ClientHandler::Create( int client_id, std::shared_ptr observer, std::function send_to_client_cb, std::function on_connection_changed_cb) { return std::shared_ptr(new ClientHandler( client_id, observer, send_to_client_cb, on_connection_changed_cb)); } ClientHandler::ClientHandler( int client_id, std::shared_ptr observer, std::function send_to_client_cb, std::function on_connection_changed_cb) : client_id_(client_id), observer_(observer), send_to_client_(send_to_client_cb), on_connection_changed_cb_(on_connection_changed_cb), camera_track_(new ClientVideoTrackImpl()) {} ClientHandler::~ClientHandler() { for (auto &data_channel : data_channels_) { data_channel->UnregisterObserver(); } } bool ClientHandler::SetPeerConnection( rtc::scoped_refptr peer_connection) { peer_connection_ = peer_connection; // libwebrtc configures the video encoder with a start bitrate of just 300kbs // which causes it to drop the first 4 frames it receives. Any value over 2Mbs // will be capped at 2Mbs when passed to the encoder by the peer_connection // object, so we pass the maximum possible value here. webrtc::BitrateSettings bitrate_settings; bitrate_settings.start_bitrate_bps = 2000000; // 2Mbs peer_connection_->SetBitrate(bitrate_settings); // At least one data channel needs to be created on the side that makes the // SDP offer (the device) for data channels to be enabled at all. // This channel is meant to carry control commands from the client. auto control_channel = peer_connection_->CreateDataChannel( "device-control", nullptr /* config */); if (!control_channel) { LOG(ERROR) << "Failed to create control data channel"; return false; } control_handler_.reset(new ControlChannelHandler(control_channel, observer_)); return true; } bool ClientHandler::AddDisplay( rtc::scoped_refptr video_track, const std::string &label) { // Send each track as part of a different stream with the label as id auto err_or_sender = peer_connection_->AddTrack(video_track, {label} /* stream_id */); if (!err_or_sender.ok()) { LOG(ERROR) << "Failed to add video track to the peer connection"; return false; } // TODO (b/154138394): use the returned sender (err_or_sender.value()) to // remove the display from the connection. return true; } bool ClientHandler::AddAudio( rtc::scoped_refptr audio_track, const std::string &label) { // Send each track as part of a different stream with the label as id auto err_or_sender = peer_connection_->AddTrack(audio_track, {label} /* stream_id */); if (!err_or_sender.ok()) { LOG(ERROR) << "Failed to add video track to the peer connection"; return false; } return true; } ClientVideoTrackInterface* ClientHandler::GetCameraStream() { return camera_track_.get(); } void ClientHandler::LogAndReplyError(const std::string &error_msg) const { LOG(ERROR) << error_msg; Json::Value reply; reply["type"] = "error"; reply["error"] = error_msg; send_to_client_(reply); } void ClientHandler::AddPendingIceCandidates() { // Add any ice candidates that arrived before the remote description for (auto& candidate: pending_ice_candidates_) { peer_connection_->AddIceCandidate(std::move(candidate), [this](webrtc::RTCError error) { if (!error.ok()) { LogAndReplyError(error.message()); } }); } pending_ice_candidates_.clear(); } void ClientHandler::OnCreateSDPSuccess( webrtc::SessionDescriptionInterface *desc) { std::string offer_str; desc->ToString(&offer_str); std::string sdp_type = desc->type(); peer_connection_->SetLocalDescription( // The peer connection wraps this raw pointer with a scoped_refptr, so // it's guaranteed to be deleted at some point new rtc::RefCountedObject( weak_from_this()), desc); // The peer connection takes ownership of the description so it should not be // used after this desc = nullptr; Json::Value reply; reply["type"] = sdp_type; reply["sdp"] = offer_str; state_ = State::kAwaitingAnswer; send_to_client_(reply); } void ClientHandler::OnCreateSDPFailure(webrtc::RTCError error) { state_ = State::kFailed; LogAndReplyError(error.message()); Close(); } void ClientHandler::OnSetSDPFailure(webrtc::RTCError error) { state_ = State::kFailed; LogAndReplyError(error.message()); LOG(ERROR) << "Error setting local description: Either there is a bug in " "libwebrtc or the local description was (incorrectly) modified " "after creating it"; Close(); } void ClientHandler::HandleMessage(const Json::Value &message) { { auto result = ValidationResult::ValidateJsonObject(message, "", {{"type", Json::ValueType::stringValue}}); if (!result.ok()) { LogAndReplyError(result.error()); return; } } auto type = message["type"].asString(); if (type == "request-offer") { // Can't check for state being different that kNew because renegotiation can // start in any state after the answer is returned. if (state_ == State::kCreatingOffer) { // An offer has been requested already LogAndReplyError("Multiple requests for offer received from single client"); return; } state_ = State::kCreatingOffer; peer_connection_->CreateOffer( // No memory leak here because this is a ref counted objects and the // peer connection immediately wraps it with a scoped_refptr new rtc::RefCountedObject( weak_from_this()), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); // The created offer wil be sent to the client on // OnSuccess(webrtc::SessionDescriptionInterface* desc) } else if (type == "offer") { auto result = ValidationResult::ValidateJsonObject( message, type, {{"sdp", Json::ValueType::stringValue}}); if (!result.ok()) { LogAndReplyError(result.error()); return; } auto remote_desc_str = message["sdp"].asString(); auto remote_desc = webrtc::CreateSessionDescription( webrtc::SdpType::kOffer, remote_desc_str, nullptr /*error*/); if (!remote_desc) { LogAndReplyError("Failed to parse answer."); return; } rtc::scoped_refptr observer( new rtc::RefCountedObject< CvdOnSetRemoteDescription>([this](webrtc::RTCError error) { if (!error.ok()) { LogAndReplyError(error.message()); // The remote description was rejected, this client can't be // trusted anymore. Close(); return; } remote_description_added_ = true; AddPendingIceCandidates(); peer_connection_->CreateAnswer( // No memory leak here because this is a ref counted objects and // the peer connection immediately wraps it with a scoped_refptr new rtc::RefCountedObject( weak_from_this()), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); })); peer_connection_->SetRemoteDescription(std::move(remote_desc), observer); state_ = State::kConnecting; } else if (type == "answer") { if (state_ != State::kAwaitingAnswer) { LogAndReplyError("Received unexpected SDP answer"); return; } auto result = ValidationResult::ValidateJsonObject(message, type, {{"sdp", Json::ValueType::stringValue}}); if (!result.ok()) { LogAndReplyError(result.error()); return; } auto remote_desc_str = message["sdp"].asString(); auto remote_desc = webrtc::CreateSessionDescription( webrtc::SdpType::kAnswer, remote_desc_str, nullptr /*error*/); if (!remote_desc) { LogAndReplyError("Failed to parse answer."); return; } rtc::scoped_refptr observer( new rtc::RefCountedObject( [this](webrtc::RTCError error) { if (!error.ok()) { LogAndReplyError(error.message()); // The remote description was rejected, this client can't be // trusted anymore. Close(); } })); peer_connection_->SetRemoteDescription(std::move(remote_desc), observer); remote_description_added_ = true; AddPendingIceCandidates(); state_ = State::kConnecting; } else if (type == "ice-candidate") { { auto result = ValidationResult::ValidateJsonObject( message, type, {{"candidate", Json::ValueType::objectValue}}); if (!result.ok()) { LogAndReplyError(result.error()); return; } } auto candidate_json = message["candidate"]; { auto result = ValidationResult::ValidateJsonObject(candidate_json, "ice-candidate/candidate", { {"sdpMid", Json::ValueType::stringValue}, {"candidate", Json::ValueType::stringValue}, {"sdpMLineIndex", Json::ValueType::intValue}, }); if (!result.ok()) { LogAndReplyError(result.error()); return; } } auto mid = candidate_json["sdpMid"].asString(); auto candidate_sdp = candidate_json["candidate"].asString(); auto line_index = candidate_json["sdpMLineIndex"].asInt(); std::unique_ptr candidate( webrtc::CreateIceCandidate(mid, line_index, candidate_sdp, nullptr /*error*/)); if (!candidate) { LogAndReplyError("Failed to parse ICE candidate"); return; } if (remote_description_added_) { peer_connection_->AddIceCandidate(std::move(candidate), [this](webrtc::RTCError error) { if (!error.ok()) { LogAndReplyError(error.message()); } }); } else { // Store the ice candidate to be added later if it arrives before the // remote description. This could happen if the client uses polling // instead of websockets because the candidates are generated immediately // after the remote (offer) description is set and the events and the ajax // calls are asynchronous. pending_ice_candidates_.push_back(std::move(candidate)); } } else { LogAndReplyError("Unknown client message type: " + type); return; } } void ClientHandler::Close() { // We can't simply call peer_connection_->Close() here because this method // could be called from one of the PeerConnectionObserver callbacks and that // would lead to a deadlock (Close eventually tries to destroy an object that // will then wait for the callback to return -> deadlock). Destroying the // peer_connection_ has the same effect. The only alternative is to postpone // that operation until after the callback returns. on_connection_changed_cb_(false); } void ClientHandler::OnConnectionChange( webrtc::PeerConnectionInterface::PeerConnectionState new_state) { switch (new_state) { case webrtc::PeerConnectionInterface::PeerConnectionState::kNew: break; case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting: break; case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: LOG(VERBOSE) << "Client " << client_id_ << ": WebRTC connected"; state_ = State::kConnected; observer_->OnConnected( [this](const uint8_t *msg, size_t size, bool binary) { control_handler_->Send(msg, size, binary); return true; }); on_connection_changed_cb_(true); break; case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: LOG(VERBOSE) << "Client " << client_id_ << ": Connection disconnected"; Close(); break; case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: LOG(ERROR) << "Client " << client_id_ << ": Connection failed"; Close(); break; case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed: LOG(VERBOSE) << "Client " << client_id_ << ": Connection closed"; Close(); break; } } void ClientHandler::OnIceCandidate( const webrtc::IceCandidateInterface *candidate) { std::string candidate_sdp; candidate->ToString(&candidate_sdp); auto sdp_mid = candidate->sdp_mid(); auto line_index = candidate->sdp_mline_index(); Json::Value reply; reply["type"] = "ice-candidate"; reply["mid"] = sdp_mid; reply["mLineIndex"] = static_cast(line_index); reply["candidate"] = candidate_sdp; send_to_client_(reply); } void ClientHandler::OnDataChannel( rtc::scoped_refptr data_channel) { auto label = data_channel->label(); if (label == kInputChannelLabel) { input_handler_.reset(new InputChannelHandler(data_channel, observer_)); } else if (label == kAdbChannelLabel) { adb_handler_.reset(new AdbChannelHandler(data_channel, observer_)); } else if (label == kBluetoothChannelLabel) { bluetooth_handler_.reset( new BluetoothChannelHandler(data_channel, observer_)); } else if (label == kCameraDataChannelLabel) { camera_data_handler_.reset( new CameraChannelHandler(data_channel, observer_)); } else { LOG(VERBOSE) << "Data channel connected: " << label; data_channels_.push_back(data_channel); } } void ClientHandler::OnRenegotiationNeeded() { state_ = State::kNew; LOG(VERBOSE) << "Client " << client_id_ << " needs renegotiation"; } void ClientHandler::OnIceGatheringChange( webrtc::PeerConnectionInterface::IceGatheringState new_state) { std::string state_str; switch (new_state) { case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew: state_str = "NEW"; break; case webrtc::PeerConnectionInterface::IceGatheringState:: kIceGatheringGathering: state_str = "GATHERING"; break; case webrtc::PeerConnectionInterface::IceGatheringState:: kIceGatheringComplete: state_str = "COMPLETE"; break; default: state_str = "UNKNOWN"; } LOG(VERBOSE) << "Client " << client_id_ << ": ICE Gathering state set to: " << state_str; } void ClientHandler::OnIceCandidateError(const std::string &host_candidate, const std::string &url, int error_code, const std::string &error_text) { LOG(VERBOSE) << "Gathering of an ICE candidate (host candidate: " << host_candidate << ", url: " << url << ") failed: " << error_text; } void ClientHandler::OnIceCandidateError(const std::string &address, int port, const std::string &url, int error_code, const std::string &error_text) { LOG(VERBOSE) << "Gathering of an ICE candidate (address: " << address << ", port: " << port << ", url: " << url << ") failed: " << error_text; } void ClientHandler::OnSignalingChange( webrtc::PeerConnectionInterface::SignalingState new_state) { // ignore } void ClientHandler::OnStandardizedIceConnectionChange( webrtc::PeerConnectionInterface::IceConnectionState new_state) { switch (new_state) { case webrtc::PeerConnectionInterface::kIceConnectionNew: LOG(DEBUG) << "ICE connection state: New"; break; case webrtc::PeerConnectionInterface::kIceConnectionChecking: LOG(DEBUG) << "ICE connection state: Checking"; break; case webrtc::PeerConnectionInterface::kIceConnectionConnected: LOG(DEBUG) << "ICE connection state: Connected"; break; case webrtc::PeerConnectionInterface::kIceConnectionCompleted: LOG(DEBUG) << "ICE connection state: Completed"; break; case webrtc::PeerConnectionInterface::kIceConnectionFailed: state_ = State::kFailed; LOG(DEBUG) << "ICE connection state: Failed"; break; case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: LOG(DEBUG) << "ICE connection state: Disconnected"; break; case webrtc::PeerConnectionInterface::kIceConnectionClosed: LOG(DEBUG) << "ICE connection state: Closed"; break; case webrtc::PeerConnectionInterface::kIceConnectionMax: LOG(DEBUG) << "ICE connection state: Max"; break; } } void ClientHandler::OnIceCandidatesRemoved( const std::vector &candidates) { // ignore } void ClientHandler::OnTrack( rtc::scoped_refptr transceiver) { auto track = transceiver->receiver()->track(); if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { // It's ok to take the raw pointer here because we make sure to unset it // when the track is removed camera_track_->SetVideoTrack( static_cast(track.get())); } } void ClientHandler::OnRemoveTrack( rtc::scoped_refptr receiver) { auto track = receiver->track(); if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { // this only unsets if the track matches the one already in store camera_track_->UnsetVideoTrack( reinterpret_cast(track.get())); } } } // namespace webrtc_streaming } // namespace cuttlefish