| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "remoting/protocol/webrtc_transport.h" | 5 #include "remoting/protocol/webrtc_transport.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/base64.h" | 11 #include "base/base64.h" |
| 12 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
| 13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 14 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| 16 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_split.h" | 18 #include "base/strings/string_split.h" |
| 19 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| 20 #include "base/task_runner_util.h" | 20 #include "base/task_runner_util.h" |
| 21 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
| 22 #include "jingle/glue/thread_wrapper.h" | 22 #include "jingle/glue/thread_wrapper.h" |
| 23 #include "remoting/protocol/authenticator.h" | 23 #include "remoting/protocol/authenticator.h" |
| 24 #include "remoting/protocol/port_allocator_factory.h" | 24 #include "remoting/protocol/port_allocator_factory.h" |
| 25 #include "remoting/protocol/sdp_message.h" |
| 25 #include "remoting/protocol/stream_message_pipe_adapter.h" | 26 #include "remoting/protocol/stream_message_pipe_adapter.h" |
| 26 #include "remoting/protocol/transport_context.h" | 27 #include "remoting/protocol/transport_context.h" |
| 27 #include "remoting/protocol/webrtc_audio_module.h" | 28 #include "remoting/protocol/webrtc_audio_module.h" |
| 28 #include "remoting/protocol/webrtc_dummy_video_encoder.h" | 29 #include "remoting/protocol/webrtc_dummy_video_encoder.h" |
| 29 #include "third_party/webrtc/api/test/fakeconstraints.h" | 30 #include "third_party/webrtc/api/test/fakeconstraints.h" |
| 30 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h" | 31 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h" |
| 31 | 32 |
| 32 using buzz::QName; | 33 using buzz::QName; |
| 33 using buzz::XmlElement; | 34 using buzz::XmlElement; |
| 34 | 35 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 49 // Command line switch used to disable signature verification. | 50 // Command line switch used to disable signature verification. |
| 50 // TODO(sergeyu): Remove this flag. | 51 // TODO(sergeyu): Remove this flag. |
| 51 const char kDisableAuthenticationSwitchName[] = "disable-authentication"; | 52 const char kDisableAuthenticationSwitchName[] = "disable-authentication"; |
| 52 #endif | 53 #endif |
| 53 | 54 |
| 54 bool IsValidSessionDescriptionType(const std::string& type) { | 55 bool IsValidSessionDescriptionType(const std::string& type) { |
| 55 return type == webrtc::SessionDescriptionInterface::kOffer || | 56 return type == webrtc::SessionDescriptionInterface::kOffer || |
| 56 type == webrtc::SessionDescriptionInterface::kAnswer; | 57 type == webrtc::SessionDescriptionInterface::kAnswer; |
| 57 } | 58 } |
| 58 | 59 |
| 59 // Normalizes the SDP message to make sure the text used for HMAC signatures | 60 void UpdateCodecParameters(SdpMessage* sdp_message, bool incoming) { |
| 60 // verifications is the same that was signed on the sending side. This is | 61 // Set bitrate range to 1-100 Mbps. |
| 61 // necessary because WebRTC generates SDP with CRLF line endings which are | 62 // - Setting min bitrate here enables padding. |
| 62 // sometimes converted to LF after passing the signaling channel. | 63 // - The default max bitrate is 600 kbps. Setting it to 100 Mbps allows to |
| 63 std::string NormalizeSessionDescription(const std::string& sdp) { | 64 // use higher bandwidth when it's available. |
| 64 // Split SDP lines. The CR symbols is removed by the TRIM_WHITESPACE flag. | 65 // |
| 65 std::vector<std::string> lines = | 66 // TODO(sergeyu): Padding needs to be enabled to workaround BW estimator not |
| 66 SplitString(sdp, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | 67 // handling spiky traffic patterns well. This won't be necessary with a |
| 67 return base::JoinString(lines, "\n") + "\n"; | 68 // better bandwidth estimator. |
| 69 // TODO(isheriff): The need for this should go away once we have a proper |
| 70 // API to provide max bitrate for the case of handing over encoded |
| 71 // frames to webrtc. |
| 72 if (sdp_message->has_video() && |
| 73 !sdp_message->AddCodecParameter( |
| 74 "VP8", "x-google-min-bitrate=1000; x-google-max-bitrate=100000")) { |
| 75 if (incoming) { |
| 76 LOG(WARNING) << "VP8 not found in an incoming SDP."; |
| 77 } else { |
| 78 LOG(FATAL) << "VP8 not found in SDP generated by WebRTC."; |
| 79 } |
| 80 } |
| 81 |
| 82 // Update SDP format to use stereo for opus codec. |
| 83 if (sdp_message->has_audio() && |
| 84 !sdp_message->AddCodecParameter("opus", |
| 85 "stereo=1; x-google-min-bitrate=160")) { |
| 86 if (incoming) { |
| 87 LOG(WARNING) << "Opus not found in an incoming SDP."; |
| 88 } else { |
| 89 LOG(FATAL) << "Opus not found in SDP generated by WebRTC."; |
| 90 } |
| 91 } |
| 68 } | 92 } |
| 69 | 93 |
| 70 // A webrtc::CreateSessionDescriptionObserver implementation used to receive the | 94 // A webrtc::CreateSessionDescriptionObserver implementation used to receive the |
| 71 // results of creating descriptions for this end of the PeerConnection. | 95 // results of creating descriptions for this end of the PeerConnection. |
| 72 class CreateSessionDescriptionObserver | 96 class CreateSessionDescriptionObserver |
| 73 : public webrtc::CreateSessionDescriptionObserver { | 97 : public webrtc::CreateSessionDescriptionObserver { |
| 74 public: | 98 public: |
| 75 typedef base::Callback<void( | 99 typedef base::Callback<void( |
| 76 std::unique_ptr<webrtc::SessionDescriptionInterface> description, | 100 std::unique_ptr<webrtc::SessionDescriptionInterface> description, |
| 77 const std::string& error)> | 101 const std::string& error)> |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 webrtc::PeerConnectionInterface::SignalingState expected_state = | 346 webrtc::PeerConnectionInterface::SignalingState expected_state = |
| 323 transport_context_->role() == TransportRole::CLIENT | 347 transport_context_->role() == TransportRole::CLIENT |
| 324 ? webrtc::PeerConnectionInterface::kStable | 348 ? webrtc::PeerConnectionInterface::kStable |
| 325 : webrtc::PeerConnectionInterface::kHaveLocalOffer; | 349 : webrtc::PeerConnectionInterface::kHaveLocalOffer; |
| 326 if (peer_connection()->signaling_state() != expected_state) { | 350 if (peer_connection()->signaling_state() != expected_state) { |
| 327 LOG(ERROR) << "Received unexpected WebRTC session_description."; | 351 LOG(ERROR) << "Received unexpected WebRTC session_description."; |
| 328 return false; | 352 return false; |
| 329 } | 353 } |
| 330 | 354 |
| 331 std::string type = session_description->Attr(QName(std::string(), "type")); | 355 std::string type = session_description->Attr(QName(std::string(), "type")); |
| 332 std::string sdp = | 356 std::string raw_sdp = session_description->BodyText(); |
| 333 NormalizeSessionDescription(session_description->BodyText()); | 357 if (!IsValidSessionDescriptionType(type) || raw_sdp.empty()) { |
| 334 if (!IsValidSessionDescriptionType(type) || sdp.empty()) { | |
| 335 LOG(ERROR) << "Incorrect session description format."; | 358 LOG(ERROR) << "Incorrect session description format."; |
| 336 return false; | 359 return false; |
| 337 } | 360 } |
| 338 | 361 |
| 362 SdpMessage sdp_message(raw_sdp); |
| 363 |
| 339 std::string signature_base64 = | 364 std::string signature_base64 = |
| 340 session_description->Attr(QName(std::string(), "signature")); | 365 session_description->Attr(QName(std::string(), "signature")); |
| 341 std::string signature; | 366 std::string signature; |
| 342 if (!base::Base64Decode(signature_base64, &signature) || | 367 if (!base::Base64Decode(signature_base64, &signature) || |
| 343 !handshake_hmac_.Verify(type + " " + sdp, signature)) { | 368 !handshake_hmac_.Verify(type + " " + sdp_message.ToString(), |
| 369 signature)) { |
| 344 LOG(WARNING) << "Received session-description with invalid signature."; | 370 LOG(WARNING) << "Received session-description with invalid signature."; |
| 345 bool ignore_error = false; | 371 bool ignore_error = false; |
| 346 #if !defined(NDEBUG) | 372 #if !defined(NDEBUG) |
| 347 ignore_error = base::CommandLine::ForCurrentProcess()->HasSwitch( | 373 ignore_error = base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 348 kDisableAuthenticationSwitchName); | 374 kDisableAuthenticationSwitchName); |
| 349 #endif | 375 #endif |
| 350 if (!ignore_error) { | 376 if (!ignore_error) { |
| 351 Close(AUTHENTICATION_FAILED); | 377 Close(AUTHENTICATION_FAILED); |
| 352 return true; | 378 return true; |
| 353 } | 379 } |
| 354 } | 380 } |
| 355 | 381 |
| 356 // Set bitrate range to 1-100 Mbps. | 382 UpdateCodecParameters(&sdp_message, /*incoming=*/true); |
| 357 // - Setting min bitrate here enables padding. | |
| 358 // - The default max bitrate is 600 kbps. Setting it to 100 Mbps allows to | |
| 359 // use higher bandwidth when it's available. | |
| 360 // | |
| 361 // TODO(sergeyu): Padding needs to be enabled to workaround BW estimator not | |
| 362 // handling spiky traffic patterns well. This won't be necessary with a | |
| 363 // better bandwidth estimator. | |
| 364 // TODO(isheriff): The need for this should go away once we have a proper | |
| 365 // API to provide max bitrate for the case of handing over encoded | |
| 366 // frames to webrtc. | |
| 367 std::string vp8line = "a=rtpmap:100 VP8/90000\n"; | |
| 368 std::string vp8line_with_bitrate = | |
| 369 vp8line + | |
| 370 "a=fmtp:100 x-google-min-bitrate=1000\n" | |
| 371 "a=fmtp:100 x-google-max-bitrate=100000\n"; | |
| 372 base::ReplaceSubstringsAfterOffset(&sdp, 0, vp8line, vp8line_with_bitrate); | |
| 373 | 383 |
| 374 webrtc::SdpParseError error; | 384 webrtc::SdpParseError error; |
| 375 std::unique_ptr<webrtc::SessionDescriptionInterface> session_description( | 385 std::unique_ptr<webrtc::SessionDescriptionInterface> session_description( |
| 376 webrtc::CreateSessionDescription(type, sdp, &error)); | 386 webrtc::CreateSessionDescription(type, sdp_message.ToString(), &error)); |
| 377 if (!session_description) { | 387 if (!session_description) { |
| 378 LOG(ERROR) << "Failed to parse the session description: " | 388 LOG(ERROR) << "Failed to parse the session description: " |
| 379 << error.description << " line: " << error.line; | 389 << error.description << " line: " << error.line; |
| 380 return false; | 390 return false; |
| 381 } | 391 } |
| 382 | 392 |
| 383 peer_connection()->SetRemoteDescription( | 393 peer_connection()->SetRemoteDescription( |
| 384 SetSessionDescriptionObserver::Create( | 394 SetSessionDescriptionObserver::Create( |
| 385 base::Bind(&WebrtcTransport::OnRemoteDescriptionSet, | 395 base::Bind(&WebrtcTransport::OnRemoteDescriptionSet, |
| 386 weak_factory_.GetWeakPtr(), | 396 weak_factory_.GetWeakPtr(), |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 442 Close(CHANNEL_CONNECTION_ERROR); | 452 Close(CHANNEL_CONNECTION_ERROR); |
| 443 return; | 453 return; |
| 444 } | 454 } |
| 445 | 455 |
| 446 std::string description_sdp; | 456 std::string description_sdp; |
| 447 if (!description->ToString(&description_sdp)) { | 457 if (!description->ToString(&description_sdp)) { |
| 448 LOG(ERROR) << "Failed to serialize description."; | 458 LOG(ERROR) << "Failed to serialize description."; |
| 449 Close(CHANNEL_CONNECTION_ERROR); | 459 Close(CHANNEL_CONNECTION_ERROR); |
| 450 return; | 460 return; |
| 451 } | 461 } |
| 452 description_sdp = NormalizeSessionDescription(description_sdp); | |
| 453 | 462 |
| 454 // Update SDP format to use stereo for opus codec. | 463 SdpMessage sdp_message(description_sdp); |
| 455 std::string opus_line = "a=rtpmap:111 opus/48000/2\n"; | 464 UpdateCodecParameters(&sdp_message, /*incoming=*/false); |
| 456 std::string opus_line_with_bitrate = | 465 description_sdp = sdp_message.ToString(); |
| 457 opus_line + "a=fmtp:111 stereo=1; x-google-min-bitrate=160\n"; | |
| 458 base::ReplaceSubstringsAfterOffset(&description_sdp, 0, opus_line, | |
| 459 opus_line_with_bitrate); | |
| 460 | 466 |
| 461 // Format and send the session description to the peer. | 467 // Format and send the session description to the peer. |
| 462 std::unique_ptr<XmlElement> transport_info( | 468 std::unique_ptr<XmlElement> transport_info( |
| 463 new XmlElement(QName(kTransportNamespace, "transport"), true)); | 469 new XmlElement(QName(kTransportNamespace, "transport"), true)); |
| 464 XmlElement* offer_tag = | 470 XmlElement* offer_tag = |
| 465 new XmlElement(QName(kTransportNamespace, "session-description")); | 471 new XmlElement(QName(kTransportNamespace, "session-description")); |
| 466 transport_info->AddElement(offer_tag); | 472 transport_info->AddElement(offer_tag); |
| 467 offer_tag->SetAttr(QName(std::string(), "type"), description->type()); | 473 offer_tag->SetAttr(QName(std::string(), "type"), description->type()); |
| 468 offer_tag->SetBodyText(description_sdp); | 474 offer_tag->SetBodyText(description_sdp); |
| 469 | 475 |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 687 // the stack and so it must be destroyed later. | 693 // the stack and so it must be destroyed later. |
| 688 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( | 694 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( |
| 689 FROM_HERE, peer_connection_wrapper_.release()); | 695 FROM_HERE, peer_connection_wrapper_.release()); |
| 690 | 696 |
| 691 if (error != OK) | 697 if (error != OK) |
| 692 event_handler_->OnWebrtcTransportError(error); | 698 event_handler_->OnWebrtcTransportError(error); |
| 693 } | 699 } |
| 694 | 700 |
| 695 } // namespace protocol | 701 } // namespace protocol |
| 696 } // namespace remoting | 702 } // namespace remoting |
| OLD | NEW |