OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2012 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #include "talk/examples/peerconnection/client/conductor.h" | |
29 | |
30 #include <utility> | |
31 #include <vector> | |
32 | |
33 #include "talk/app/webrtc/videosourceinterface.h" | |
34 #include "talk/examples/peerconnection/client/defaults.h" | |
35 #include "talk/media/devices/devicemanager.h" | |
36 #include "talk/app/webrtc/test/fakeconstraints.h" | |
37 #include "webrtc/base/common.h" | |
38 #include "webrtc/base/json.h" | |
39 #include "webrtc/base/logging.h" | |
40 | |
41 // Names used for a IceCandidate JSON object. | |
42 const char kCandidateSdpMidName[] = "sdpMid"; | |
43 const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex"; | |
44 const char kCandidateSdpName[] = "candidate"; | |
45 | |
46 // Names used for a SessionDescription JSON object. | |
47 const char kSessionDescriptionTypeName[] = "type"; | |
48 const char kSessionDescriptionSdpName[] = "sdp"; | |
49 | |
50 #define DTLS_ON true | |
51 #define DTLS_OFF false | |
52 | |
53 class DummySetSessionDescriptionObserver | |
54 : public webrtc::SetSessionDescriptionObserver { | |
55 public: | |
56 static DummySetSessionDescriptionObserver* Create() { | |
57 return | |
58 new rtc::RefCountedObject<DummySetSessionDescriptionObserver>(); | |
59 } | |
60 virtual void OnSuccess() { | |
61 LOG(INFO) << __FUNCTION__; | |
62 } | |
63 virtual void OnFailure(const std::string& error) { | |
64 LOG(INFO) << __FUNCTION__ << " " << error; | |
65 } | |
66 | |
67 protected: | |
68 DummySetSessionDescriptionObserver() {} | |
69 ~DummySetSessionDescriptionObserver() {} | |
70 }; | |
71 | |
72 Conductor::Conductor(PeerConnectionClient* client, MainWindow* main_wnd) | |
73 : peer_id_(-1), | |
74 loopback_(false), | |
75 client_(client), | |
76 main_wnd_(main_wnd) { | |
77 client_->RegisterObserver(this); | |
78 main_wnd->RegisterObserver(this); | |
79 } | |
80 | |
81 Conductor::~Conductor() { | |
82 ASSERT(peer_connection_.get() == NULL); | |
83 } | |
84 | |
85 bool Conductor::connection_active() const { | |
86 return peer_connection_.get() != NULL; | |
87 } | |
88 | |
89 void Conductor::Close() { | |
90 client_->SignOut(); | |
91 DeletePeerConnection(); | |
92 } | |
93 | |
94 bool Conductor::InitializePeerConnection() { | |
95 ASSERT(peer_connection_factory_.get() == NULL); | |
96 ASSERT(peer_connection_.get() == NULL); | |
97 | |
98 peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(); | |
99 | |
100 if (!peer_connection_factory_.get()) { | |
101 main_wnd_->MessageBox("Error", | |
102 "Failed to initialize PeerConnectionFactory", true); | |
103 DeletePeerConnection(); | |
104 return false; | |
105 } | |
106 | |
107 if (!CreatePeerConnection(DTLS_ON)) { | |
108 main_wnd_->MessageBox("Error", | |
109 "CreatePeerConnection failed", true); | |
110 DeletePeerConnection(); | |
111 } | |
112 AddStreams(); | |
113 return peer_connection_.get() != NULL; | |
114 } | |
115 | |
116 bool Conductor::ReinitializePeerConnectionForLoopback() { | |
117 loopback_ = true; | |
118 rtc::scoped_refptr<webrtc::StreamCollectionInterface> streams( | |
119 peer_connection_->local_streams()); | |
120 peer_connection_ = NULL; | |
121 if (CreatePeerConnection(DTLS_OFF)) { | |
122 for (size_t i = 0; i < streams->count(); ++i) | |
123 peer_connection_->AddStream(streams->at(i)); | |
124 peer_connection_->CreateOffer(this, NULL); | |
125 } | |
126 return peer_connection_.get() != NULL; | |
127 } | |
128 | |
129 bool Conductor::CreatePeerConnection(bool dtls) { | |
130 ASSERT(peer_connection_factory_.get() != NULL); | |
131 ASSERT(peer_connection_.get() == NULL); | |
132 | |
133 webrtc::PeerConnectionInterface::IceServers servers; | |
134 webrtc::PeerConnectionInterface::IceServer server; | |
135 server.uri = GetPeerConnectionString(); | |
136 servers.push_back(server); | |
137 | |
138 webrtc::FakeConstraints constraints; | |
139 if (dtls) { | |
140 constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, | |
141 "true"); | |
142 } | |
143 else | |
144 { | |
145 constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, | |
146 "false"); | |
147 } | |
148 | |
149 peer_connection_ = | |
150 peer_connection_factory_->CreatePeerConnection(servers, | |
151 &constraints, | |
152 NULL, | |
153 NULL, | |
154 this); | |
155 return peer_connection_.get() != NULL; | |
156 } | |
157 | |
158 void Conductor::DeletePeerConnection() { | |
159 peer_connection_ = NULL; | |
160 active_streams_.clear(); | |
161 main_wnd_->StopLocalRenderer(); | |
162 main_wnd_->StopRemoteRenderer(); | |
163 peer_connection_factory_ = NULL; | |
164 peer_id_ = -1; | |
165 loopback_ = false; | |
166 } | |
167 | |
168 void Conductor::EnsureStreamingUI() { | |
169 ASSERT(peer_connection_.get() != NULL); | |
170 if (main_wnd_->IsWindow()) { | |
171 if (main_wnd_->current_ui() != MainWindow::STREAMING) | |
172 main_wnd_->SwitchToStreamingUI(); | |
173 } | |
174 } | |
175 | |
176 // | |
177 // PeerConnectionObserver implementation. | |
178 // | |
179 | |
180 // Called when a remote stream is added | |
181 void Conductor::OnAddStream(webrtc::MediaStreamInterface* stream) { | |
182 LOG(INFO) << __FUNCTION__ << " " << stream->label(); | |
183 | |
184 stream->AddRef(); | |
185 main_wnd_->QueueUIThreadCallback(NEW_STREAM_ADDED, | |
186 stream); | |
187 } | |
188 | |
189 void Conductor::OnRemoveStream(webrtc::MediaStreamInterface* stream) { | |
190 LOG(INFO) << __FUNCTION__ << " " << stream->label(); | |
191 stream->AddRef(); | |
192 main_wnd_->QueueUIThreadCallback(STREAM_REMOVED, | |
193 stream); | |
194 } | |
195 | |
196 void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { | |
197 LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); | |
198 // For loopback test. To save some connecting delay. | |
199 if (loopback_) { | |
200 if (!peer_connection_->AddIceCandidate(candidate)) { | |
201 LOG(WARNING) << "Failed to apply the received candidate"; | |
202 } | |
203 return; | |
204 } | |
205 | |
206 Json::StyledWriter writer; | |
207 Json::Value jmessage; | |
208 | |
209 jmessage[kCandidateSdpMidName] = candidate->sdp_mid(); | |
210 jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index(); | |
211 std::string sdp; | |
212 if (!candidate->ToString(&sdp)) { | |
213 LOG(LS_ERROR) << "Failed to serialize candidate"; | |
214 return; | |
215 } | |
216 jmessage[kCandidateSdpName] = sdp; | |
217 SendMessage(writer.write(jmessage)); | |
218 } | |
219 | |
220 // | |
221 // PeerConnectionClientObserver implementation. | |
222 // | |
223 | |
224 void Conductor::OnSignedIn() { | |
225 LOG(INFO) << __FUNCTION__; | |
226 main_wnd_->SwitchToPeerList(client_->peers()); | |
227 } | |
228 | |
229 void Conductor::OnDisconnected() { | |
230 LOG(INFO) << __FUNCTION__; | |
231 | |
232 DeletePeerConnection(); | |
233 | |
234 if (main_wnd_->IsWindow()) | |
235 main_wnd_->SwitchToConnectUI(); | |
236 } | |
237 | |
238 void Conductor::OnPeerConnected(int id, const std::string& name) { | |
239 LOG(INFO) << __FUNCTION__; | |
240 // Refresh the list if we're showing it. | |
241 if (main_wnd_->current_ui() == MainWindow::LIST_PEERS) | |
242 main_wnd_->SwitchToPeerList(client_->peers()); | |
243 } | |
244 | |
245 void Conductor::OnPeerDisconnected(int id) { | |
246 LOG(INFO) << __FUNCTION__; | |
247 if (id == peer_id_) { | |
248 LOG(INFO) << "Our peer disconnected"; | |
249 main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL); | |
250 } else { | |
251 // Refresh the list if we're showing it. | |
252 if (main_wnd_->current_ui() == MainWindow::LIST_PEERS) | |
253 main_wnd_->SwitchToPeerList(client_->peers()); | |
254 } | |
255 } | |
256 | |
257 void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { | |
258 ASSERT(peer_id_ == peer_id || peer_id_ == -1); | |
259 ASSERT(!message.empty()); | |
260 | |
261 if (!peer_connection_.get()) { | |
262 ASSERT(peer_id_ == -1); | |
263 peer_id_ = peer_id; | |
264 | |
265 if (!InitializePeerConnection()) { | |
266 LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance"; | |
267 client_->SignOut(); | |
268 return; | |
269 } | |
270 } else if (peer_id != peer_id_) { | |
271 ASSERT(peer_id_ != -1); | |
272 LOG(WARNING) << "Received a message from unknown peer while already in a " | |
273 "conversation with a different peer."; | |
274 return; | |
275 } | |
276 | |
277 Json::Reader reader; | |
278 Json::Value jmessage; | |
279 if (!reader.parse(message, jmessage)) { | |
280 LOG(WARNING) << "Received unknown message. " << message; | |
281 return; | |
282 } | |
283 std::string type; | |
284 std::string json_object; | |
285 | |
286 rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type); | |
287 if (!type.empty()) { | |
288 if (type == "offer-loopback") { | |
289 // This is a loopback call. | |
290 // Recreate the peerconnection with DTLS disabled. | |
291 if (!ReinitializePeerConnectionForLoopback()) { | |
292 LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance"; | |
293 DeletePeerConnection(); | |
294 client_->SignOut(); | |
295 } | |
296 return; | |
297 } | |
298 | |
299 std::string sdp; | |
300 if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, | |
301 &sdp)) { | |
302 LOG(WARNING) << "Can't parse received session description message."; | |
303 return; | |
304 } | |
305 webrtc::SdpParseError error; | |
306 webrtc::SessionDescriptionInterface* session_description( | |
307 webrtc::CreateSessionDescription(type, sdp, &error)); | |
308 if (!session_description) { | |
309 LOG(WARNING) << "Can't parse received session description message. " | |
310 << "SdpParseError was: " << error.description; | |
311 return; | |
312 } | |
313 LOG(INFO) << " Received session description :" << message; | |
314 peer_connection_->SetRemoteDescription( | |
315 DummySetSessionDescriptionObserver::Create(), session_description); | |
316 if (session_description->type() == | |
317 webrtc::SessionDescriptionInterface::kOffer) { | |
318 peer_connection_->CreateAnswer(this, NULL); | |
319 } | |
320 return; | |
321 } else { | |
322 std::string sdp_mid; | |
323 int sdp_mlineindex = 0; | |
324 std::string sdp; | |
325 if (!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName, | |
326 &sdp_mid) || | |
327 !rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName, | |
328 &sdp_mlineindex) || | |
329 !rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) { | |
330 LOG(WARNING) << "Can't parse received message."; | |
331 return; | |
332 } | |
333 webrtc::SdpParseError error; | |
334 rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate( | |
335 webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error)); | |
336 if (!candidate.get()) { | |
337 LOG(WARNING) << "Can't parse received candidate message. " | |
338 << "SdpParseError was: " << error.description; | |
339 return; | |
340 } | |
341 if (!peer_connection_->AddIceCandidate(candidate.get())) { | |
342 LOG(WARNING) << "Failed to apply the received candidate"; | |
343 return; | |
344 } | |
345 LOG(INFO) << " Received candidate :" << message; | |
346 return; | |
347 } | |
348 } | |
349 | |
350 void Conductor::OnMessageSent(int err) { | |
351 // Process the next pending message if any. | |
352 main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, NULL); | |
353 } | |
354 | |
355 void Conductor::OnServerConnectionFailure() { | |
356 main_wnd_->MessageBox("Error", ("Failed to connect to " + server_).c_str(), | |
357 true); | |
358 } | |
359 | |
360 // | |
361 // MainWndCallback implementation. | |
362 // | |
363 | |
364 void Conductor::StartLogin(const std::string& server, int port) { | |
365 if (client_->is_connected()) | |
366 return; | |
367 server_ = server; | |
368 client_->Connect(server, port, GetPeerName()); | |
369 } | |
370 | |
371 void Conductor::DisconnectFromServer() { | |
372 if (client_->is_connected()) | |
373 client_->SignOut(); | |
374 } | |
375 | |
376 void Conductor::ConnectToPeer(int peer_id) { | |
377 ASSERT(peer_id_ == -1); | |
378 ASSERT(peer_id != -1); | |
379 | |
380 if (peer_connection_.get()) { | |
381 main_wnd_->MessageBox("Error", | |
382 "We only support connecting to one peer at a time", true); | |
383 return; | |
384 } | |
385 | |
386 if (InitializePeerConnection()) { | |
387 peer_id_ = peer_id; | |
388 peer_connection_->CreateOffer(this, NULL); | |
389 } else { | |
390 main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true); | |
391 } | |
392 } | |
393 | |
394 cricket::VideoCapturer* Conductor::OpenVideoCaptureDevice() { | |
395 rtc::scoped_ptr<cricket::DeviceManagerInterface> dev_manager( | |
396 cricket::DeviceManagerFactory::Create()); | |
397 if (!dev_manager->Init()) { | |
398 LOG(LS_ERROR) << "Can't create device manager"; | |
399 return NULL; | |
400 } | |
401 std::vector<cricket::Device> devs; | |
402 if (!dev_manager->GetVideoCaptureDevices(&devs)) { | |
403 LOG(LS_ERROR) << "Can't enumerate video devices"; | |
404 return NULL; | |
405 } | |
406 std::vector<cricket::Device>::iterator dev_it = devs.begin(); | |
407 cricket::VideoCapturer* capturer = NULL; | |
408 for (; dev_it != devs.end(); ++dev_it) { | |
409 capturer = dev_manager->CreateVideoCapturer(*dev_it); | |
410 if (capturer != NULL) | |
411 break; | |
412 } | |
413 return capturer; | |
414 } | |
415 | |
416 void Conductor::AddStreams() { | |
417 if (active_streams_.find(kStreamLabel) != active_streams_.end()) | |
418 return; // Already added. | |
419 | |
420 rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track( | |
421 peer_connection_factory_->CreateAudioTrack( | |
422 kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL))); | |
423 | |
424 rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track( | |
425 peer_connection_factory_->CreateVideoTrack( | |
426 kVideoLabel, | |
427 peer_connection_factory_->CreateVideoSource(OpenVideoCaptureDevice(), | |
428 NULL))); | |
429 main_wnd_->StartLocalRenderer(video_track); | |
430 | |
431 rtc::scoped_refptr<webrtc::MediaStreamInterface> stream = | |
432 peer_connection_factory_->CreateLocalMediaStream(kStreamLabel); | |
433 | |
434 stream->AddTrack(audio_track); | |
435 stream->AddTrack(video_track); | |
436 if (!peer_connection_->AddStream(stream)) { | |
437 LOG(LS_ERROR) << "Adding stream to PeerConnection failed"; | |
438 } | |
439 typedef std::pair<std::string, | |
440 rtc::scoped_refptr<webrtc::MediaStreamInterface> > | |
441 MediaStreamPair; | |
442 active_streams_.insert(MediaStreamPair(stream->label(), stream)); | |
443 main_wnd_->SwitchToStreamingUI(); | |
444 } | |
445 | |
446 void Conductor::DisconnectFromCurrentPeer() { | |
447 LOG(INFO) << __FUNCTION__; | |
448 if (peer_connection_.get()) { | |
449 client_->SendHangUp(peer_id_); | |
450 DeletePeerConnection(); | |
451 } | |
452 | |
453 if (main_wnd_->IsWindow()) | |
454 main_wnd_->SwitchToPeerList(client_->peers()); | |
455 } | |
456 | |
457 void Conductor::UIThreadCallback(int msg_id, void* data) { | |
458 switch (msg_id) { | |
459 case PEER_CONNECTION_CLOSED: | |
460 LOG(INFO) << "PEER_CONNECTION_CLOSED"; | |
461 DeletePeerConnection(); | |
462 | |
463 ASSERT(active_streams_.empty()); | |
464 | |
465 if (main_wnd_->IsWindow()) { | |
466 if (client_->is_connected()) { | |
467 main_wnd_->SwitchToPeerList(client_->peers()); | |
468 } else { | |
469 main_wnd_->SwitchToConnectUI(); | |
470 } | |
471 } else { | |
472 DisconnectFromServer(); | |
473 } | |
474 break; | |
475 | |
476 case SEND_MESSAGE_TO_PEER: { | |
477 LOG(INFO) << "SEND_MESSAGE_TO_PEER"; | |
478 std::string* msg = reinterpret_cast<std::string*>(data); | |
479 if (msg) { | |
480 // For convenience, we always run the message through the queue. | |
481 // This way we can be sure that messages are sent to the server | |
482 // in the same order they were signaled without much hassle. | |
483 pending_messages_.push_back(msg); | |
484 } | |
485 | |
486 if (!pending_messages_.empty() && !client_->IsSendingMessage()) { | |
487 msg = pending_messages_.front(); | |
488 pending_messages_.pop_front(); | |
489 | |
490 if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) { | |
491 LOG(LS_ERROR) << "SendToPeer failed"; | |
492 DisconnectFromServer(); | |
493 } | |
494 delete msg; | |
495 } | |
496 | |
497 if (!peer_connection_.get()) | |
498 peer_id_ = -1; | |
499 | |
500 break; | |
501 } | |
502 | |
503 case NEW_STREAM_ADDED: { | |
504 webrtc::MediaStreamInterface* stream = | |
505 reinterpret_cast<webrtc::MediaStreamInterface*>( | |
506 data); | |
507 webrtc::VideoTrackVector tracks = stream->GetVideoTracks(); | |
508 // Only render the first track. | |
509 if (!tracks.empty()) { | |
510 webrtc::VideoTrackInterface* track = tracks[0]; | |
511 main_wnd_->StartRemoteRenderer(track); | |
512 } | |
513 stream->Release(); | |
514 break; | |
515 } | |
516 | |
517 case STREAM_REMOVED: { | |
518 // Remote peer stopped sending a stream. | |
519 webrtc::MediaStreamInterface* stream = | |
520 reinterpret_cast<webrtc::MediaStreamInterface*>( | |
521 data); | |
522 stream->Release(); | |
523 break; | |
524 } | |
525 | |
526 default: | |
527 ASSERT(false); | |
528 break; | |
529 } | |
530 } | |
531 | |
532 void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) { | |
533 peer_connection_->SetLocalDescription( | |
534 DummySetSessionDescriptionObserver::Create(), desc); | |
535 | |
536 std::string sdp; | |
537 desc->ToString(&sdp); | |
538 | |
539 // For loopback test. To save some connecting delay. | |
540 if (loopback_) { | |
541 // Replace message type from "offer" to "answer" | |
542 webrtc::SessionDescriptionInterface* session_description( | |
543 webrtc::CreateSessionDescription("answer", sdp, nullptr)); | |
544 peer_connection_->SetRemoteDescription( | |
545 DummySetSessionDescriptionObserver::Create(), session_description); | |
546 return; | |
547 } | |
548 | |
549 Json::StyledWriter writer; | |
550 Json::Value jmessage; | |
551 jmessage[kSessionDescriptionTypeName] = desc->type(); | |
552 jmessage[kSessionDescriptionSdpName] = sdp; | |
553 SendMessage(writer.write(jmessage)); | |
554 } | |
555 | |
556 void Conductor::OnFailure(const std::string& error) { | |
557 LOG(LERROR) << error; | |
558 } | |
559 | |
560 void Conductor::SendMessage(const std::string& json_object) { | |
561 std::string* msg = new std::string(json_object); | |
562 main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg); | |
563 } | |
OLD | NEW |