1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "ClientHandler"
18
19 #include "host/frontend/webrtc/libdevice/client_handler.h"
20
21 #include <netdb.h>
22 #include <openssl/rand.h>
23
24 #include <android-base/logging.h>
25
26 #include "host/libs/config/cuttlefish_config.h"
27
28 namespace cuttlefish {
29 namespace webrtc_streaming {
30
31 // Video streams initiating in the client may be added and removed at unexpected
32 // times, causing the webrtc objects to be destroyed and created every time.
33 // This class hides away that complexity and allows to set up sinks only once.
34 class ClientVideoTrackImpl : public ClientVideoTrackInterface {
35 public:
AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame> * sink,const rtc::VideoSinkWants & wants)36 void AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame> *sink,
37 const rtc::VideoSinkWants &wants) override {
38 sink_ = sink;
39 wants_ = wants;
40 if (video_track_) {
41 video_track_->AddOrUpdateSink(sink, wants);
42 }
43 }
44
SetVideoTrack(webrtc::VideoTrackInterface * track)45 void SetVideoTrack(webrtc::VideoTrackInterface *track) {
46 video_track_ = track;
47 if (sink_) {
48 video_track_->AddOrUpdateSink(sink_, wants_);
49 }
50 }
51
UnsetVideoTrack(webrtc::VideoTrackInterface * track)52 void UnsetVideoTrack(webrtc::VideoTrackInterface *track) {
53 if (track == video_track_) {
54 video_track_ = nullptr;
55 }
56 }
57
58 private:
59 webrtc::VideoTrackInterface* video_track_;
60 rtc::VideoSinkInterface<webrtc::VideoFrame> *sink_ = nullptr;
61 rtc::VideoSinkWants wants_ = {};
62 };
63
Create(int client_id,std::shared_ptr<ConnectionObserver> observer,PeerConnectionBuilder & connection_builder,std::function<void (const Json::Value &)> send_to_client_cb,std::function<void (bool)> on_connection_changed_cb)64 std::shared_ptr<ClientHandler> ClientHandler::Create(
65 int client_id, std::shared_ptr<ConnectionObserver> observer,
66 PeerConnectionBuilder &connection_builder,
67 std::function<void(const Json::Value &)> send_to_client_cb,
68 std::function<void(bool)> on_connection_changed_cb) {
69 return std::shared_ptr<ClientHandler>(
70 new ClientHandler(client_id, observer, connection_builder,
71 send_to_client_cb, on_connection_changed_cb));
72 }
73
ClientHandler(int client_id,std::shared_ptr<ConnectionObserver> observer,PeerConnectionBuilder & connection_builder,std::function<void (const Json::Value &)> send_to_client_cb,std::function<void (bool)> on_connection_changed_cb)74 ClientHandler::ClientHandler(
75 int client_id, std::shared_ptr<ConnectionObserver> observer,
76 PeerConnectionBuilder &connection_builder,
77 std::function<void(const Json::Value &)> send_to_client_cb,
78 std::function<void(bool)> on_connection_changed_cb)
79 : client_id_(client_id),
80 observer_(observer),
81 send_to_client_(send_to_client_cb),
82 on_connection_changed_cb_(on_connection_changed_cb),
83 connection_builder_(connection_builder),
84 controller_(*this, *this, *this),
85 data_channels_handler_(observer),
86 camera_track_(new ClientVideoTrackImpl()) {}
87
88 rtc::scoped_refptr<webrtc::RtpSenderInterface>
AddTrackToConnection(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection,const std::string & label)89 ClientHandler::AddTrackToConnection(
90 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
91 rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection,
92 const std::string &label) {
93 if (!peer_connection) {
94 return nullptr;
95 }
96 // Send each track as part of a different stream with the label as id
97 auto err_or_sender =
98 peer_connection->AddTrack(track, {label} /* stream_id */);
99 if (!err_or_sender.ok()) {
100 LOG(ERROR) << "Failed to add track to the peer connection";
101 return nullptr;
102 }
103 return err_or_sender.MoveValue();
104 }
105
AddDisplay(rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track,const std::string & label)106 bool ClientHandler::AddDisplay(
107 rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track,
108 const std::string &label) {
109 auto [it, inserted] = displays_.emplace(label, DisplayTrackAndSender{
110 .track = video_track,
111 });
112 auto sender =
113 AddTrackToConnection(video_track, controller_.peer_connection(), label);
114 if (sender) {
115 DisplayTrackAndSender &info = it->second;
116 info.sender = sender;
117 }
118 // Succeed if the peer connection is null or the track was added
119 return controller_.peer_connection() == nullptr || sender;
120 }
121
RemoveDisplay(const std::string & label)122 bool ClientHandler::RemoveDisplay(const std::string &label) {
123 auto it = displays_.find(label);
124 if (it == displays_.end()) {
125 return false;
126 }
127
128 if (controller_.peer_connection()) {
129 DisplayTrackAndSender &info = it->second;
130
131 auto error = controller_.peer_connection()->RemoveTrackOrError(info.sender);
132 if (!error.ok()) {
133 LOG(ERROR) << "Failed to remove video track for display " << label << ": "
134 << error.message();
135 return false;
136 }
137 }
138
139 displays_.erase(it);
140 return true;
141 }
142
AddAudio(rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track,const std::string & label)143 bool ClientHandler::AddAudio(
144 rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track,
145 const std::string &label) {
146 audio_streams_.emplace_back(audio_track, label);
147 auto peer_connection = controller_.peer_connection();
148 if (!peer_connection) {
149 return true;
150 }
151 return AddTrackToConnection(audio_track, controller_.peer_connection(), label)
152 .get();
153 }
154
GetCameraStream()155 ClientVideoTrackInterface* ClientHandler::GetCameraStream() {
156 return camera_track_.get();
157 }
158
SendMessage(const Json::Value & msg)159 Result<void> ClientHandler::SendMessage(const Json::Value &msg) {
160 send_to_client_(msg);
161 return {};
162 }
163
164 Result<rtc::scoped_refptr<webrtc::PeerConnectionInterface>>
Build(webrtc::PeerConnectionObserver & observer,const std::vector<webrtc::PeerConnectionInterface::IceServer> & per_connection_servers)165 ClientHandler::Build(
166 webrtc::PeerConnectionObserver &observer,
167 const std::vector<webrtc::PeerConnectionInterface::IceServer>
168 &per_connection_servers) {
169 auto peer_connection =
170 CF_EXPECT(connection_builder_.Build(observer, per_connection_servers));
171
172 // Re-add the video and audio tracks after the peer connection has been
173 // created
174 for (auto &[label, info] : displays_) {
175 info.sender =
176 CF_EXPECT(AddTrackToConnection(info.track, peer_connection, label).get());
177 }
178 // Add the audio tracks to the peer connection
179 for (auto &[audio_track, label] : audio_streams_) {
180 // Audio channels are never removed from the connection by the device, so
181 // it's ok to discard the returned sender here. The peer connection keeps
182 // track of it anyways.
183 CF_EXPECT(AddTrackToConnection(audio_track, peer_connection, label).get());
184 }
185
186 // libwebrtc configures the video encoder with a start bitrate of just 300kbs
187 // which causes it to drop the first 4 frames it receives. Any value over 2Mbs
188 // will be capped at 2Mbs when passed to the encoder by the peer_connection
189 // object, so we pass the maximum possible value here.
190 webrtc::BitrateSettings bitrate_settings;
191 bitrate_settings.start_bitrate_bps = 2000000; // 2Mbs
192 peer_connection->SetBitrate(bitrate_settings);
193
194 // At least one data channel needs to be created on the side that creates the
195 // SDP offer (the device) for data channels to be enabled at all.
196 // This channel is meant to carry control commands from the client.
197 auto control_channel = peer_connection->CreateDataChannel(
198 kControlChannelLabel, nullptr /* config */);
199 CF_EXPECT(control_channel.get(), "Failed to create control data channel");
200
201 data_channels_handler_.OnDataChannelOpen(control_channel);
202
203 return peer_connection;
204 }
205
HandleMessage(const Json::Value & message)206 void ClientHandler::HandleMessage(const Json::Value &message) {
207 controller_.HandleSignalingMessage(message);
208 }
209
Close()210 void ClientHandler::Close() {
211 // We can't simply call peer_connection_->Close() here because this method
212 // could be called from one of the PeerConnectionObserver callbacks and that
213 // would lead to a deadlock (Close eventually tries to destroy an object that
214 // will then wait for the callback to return -> deadlock). Destroying the
215 // peer_connection_ has the same effect. The only alternative is to postpone
216 // that operation until after the callback returns.
217 on_connection_changed_cb_(false);
218 }
219
OnConnectionStateChange(Result<webrtc::PeerConnectionInterface::PeerConnectionState> new_state)220 void ClientHandler::OnConnectionStateChange(
221 Result<webrtc::PeerConnectionInterface::PeerConnectionState> new_state) {
222 if (!new_state.ok()) {
223 LOG(ERROR) << "Connection error: " << new_state.error().FormatForEnv();
224 Close();
225 return;
226 }
227 switch (*new_state) {
228 case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
229 LOG(VERBOSE) << "Client " << client_id_ << ": WebRTC connected";
230 observer_->OnConnected();
231 on_connection_changed_cb_(true);
232 break;
233 case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
234 LOG(VERBOSE) << "Client " << client_id_ << ": Connection disconnected";
235 Close();
236 break;
237 case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed:
238 LOG(ERROR) << "Client " << client_id_ << ": Connection failed";
239 Close();
240 break;
241 case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed:
242 LOG(VERBOSE) << "Client " << client_id_ << ": Connection closed";
243 Close();
244 break;
245 case webrtc::PeerConnectionInterface::PeerConnectionState::kNew:
246 LOG(VERBOSE) << "Client " << client_id_ << ": Connection new";
247 break;
248 case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting:
249 LOG(VERBOSE) << "Client " << client_id_ << ": Connection started";
250 break;
251 }
252 }
253
OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)254 void ClientHandler::OnDataChannel(
255 rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) {
256 data_channels_handler_.OnDataChannelOpen(data_channel);
257 }
258
OnTrack(rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver)259 void ClientHandler::OnTrack(
260 rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
261 auto track = transceiver->receiver()->track();
262 if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
263 // It's ok to take the raw pointer here because we make sure to unset it
264 // when the track is removed
265 camera_track_->SetVideoTrack(
266 static_cast<webrtc::VideoTrackInterface *>(track.get()));
267 }
268 }
OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)269 void ClientHandler::OnRemoveTrack(
270 rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) {
271 auto track = receiver->track();
272 if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
273 // this only unsets if the track matches the one already in store
274 camera_track_->UnsetVideoTrack(
275 reinterpret_cast<webrtc::VideoTrackInterface *>(track.get()));
276 }
277 }
278
279 } // namespace webrtc_streaming
280 } // namespace cuttlefish
281