| // Copyright (c) 2012 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 "content/renderer/media/peer_connection_handler_jsep.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/string_number_conversions.h" |
| #include "base/stringprintf.h" |
| #include "base/utf_string_conversions.h" |
| #include "content/renderer/media/media_stream_dependency_factory.h" |
| #include "content/renderer/media/media_stream_impl.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICECandidateDescriptor.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebICEOptions.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConnection00HandlerClient.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaHints.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSessionDescriptionDescriptor.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" |
| |
| PeerConnectionHandlerJsep::PeerConnectionHandlerJsep( |
| WebKit::WebPeerConnection00HandlerClient* client, |
| MediaStreamDependencyFactory* dependency_factory) |
| : PeerConnectionHandlerBase(dependency_factory), |
| client_(client) { |
| } |
| |
| PeerConnectionHandlerJsep::~PeerConnectionHandlerJsep() { |
| } |
| |
| void PeerConnectionHandlerJsep::initialize( |
| const WebKit::WebString& server_configuration, |
| const WebKit::WebString& username) { |
| native_peer_connection_ = dependency_factory_->CreatePeerConnection( |
| UTF16ToUTF8(server_configuration), |
| this); |
| CHECK(native_peer_connection_); |
| } |
| |
| WebKit::WebSessionDescriptionDescriptor PeerConnectionHandlerJsep::createOffer( |
| const WebKit::WebMediaHints& hints) { |
| WebKit::WebSessionDescriptionDescriptor offer; |
| |
| webrtc::MediaHints native_hints(hints.audio(), hints.video()); |
| scoped_ptr<webrtc::SessionDescriptionInterface> native_offer( |
| native_peer_connection_->CreateOffer(native_hints)); |
| if (!native_offer.get()) { |
| LOG(ERROR) << "Failed to create native offer"; |
| return offer; |
| } |
| |
| offer = CreateWebKitSessionDescription(native_offer.get()); |
| return offer; |
| } |
| |
| WebKit::WebSessionDescriptionDescriptor PeerConnectionHandlerJsep::createAnswer( |
| const WebKit::WebString& offer, |
| const WebKit::WebMediaHints& hints) { |
| WebKit::WebSessionDescriptionDescriptor answer; |
| |
| webrtc::MediaHints native_hints(hints.audio(), hints.video()); |
| scoped_ptr<webrtc::SessionDescriptionInterface> native_offer( |
| dependency_factory_->CreateSessionDescription(UTF16ToUTF8(offer))); |
| if (!native_offer.get()) { |
| LOG(ERROR) << "Failed to create native offer"; |
| return answer; |
| } |
| |
| scoped_ptr<webrtc::SessionDescriptionInterface> native_answer( |
| native_peer_connection_->CreateAnswer(native_hints, native_offer.get())); |
| if (!native_answer.get()) { |
| LOG(ERROR) << "Failed to create native answer"; |
| return answer; |
| } |
| |
| answer = CreateWebKitSessionDescription(native_answer.get()); |
| return answer; |
| } |
| |
| bool PeerConnectionHandlerJsep::setLocalDescription( |
| Action action, |
| const WebKit::WebSessionDescriptionDescriptor& description) { |
| webrtc::PeerConnectionInterface::Action native_action; |
| if (!GetNativeAction(action, &native_action)) |
| return false; |
| |
| webrtc::SessionDescriptionInterface* native_desc = |
| CreateNativeSessionDescription(description); |
| if (!native_desc) |
| return false; |
| |
| return native_peer_connection_->SetLocalDescription(native_action, |
| native_desc); |
| } |
| |
| bool PeerConnectionHandlerJsep::setRemoteDescription( |
| Action action, |
| const WebKit::WebSessionDescriptionDescriptor& description) { |
| webrtc::PeerConnectionInterface::Action native_action; |
| if (!GetNativeAction(action, &native_action)) |
| return false; |
| |
| webrtc::SessionDescriptionInterface* native_desc = |
| CreateNativeSessionDescription(description); |
| if (!native_desc) |
| return false; |
| |
| return native_peer_connection_->SetRemoteDescription(native_action, |
| native_desc); |
| } |
| |
| WebKit::WebSessionDescriptionDescriptor |
| PeerConnectionHandlerJsep::localDescription() { |
| const webrtc::SessionDescriptionInterface* native_desc = |
| native_peer_connection_->local_description(); |
| WebKit::WebSessionDescriptionDescriptor description = |
| CreateWebKitSessionDescription(native_desc); |
| return description; |
| } |
| |
| WebKit::WebSessionDescriptionDescriptor |
| PeerConnectionHandlerJsep::remoteDescription() { |
| const webrtc::SessionDescriptionInterface* native_desc = |
| native_peer_connection_->remote_description(); |
| WebKit::WebSessionDescriptionDescriptor description = |
| CreateWebKitSessionDescription(native_desc); |
| return description; |
| } |
| |
| bool PeerConnectionHandlerJsep::startIce(const WebKit::WebICEOptions& options) { |
| webrtc::PeerConnectionInterface::IceOptions native_options; |
| switch (options.candidateTypeToUse()) { |
| case WebKit::WebICEOptions::CandidateTypeAll: |
| native_options = webrtc::PeerConnectionInterface::kUseAll; |
| break; |
| case WebKit::WebICEOptions::CandidateTypeNoRelay: |
| native_options = webrtc::PeerConnectionInterface::kNoRelay; |
| break; |
| case WebKit::WebICEOptions::CandidateTypeOnlyRelay: |
| native_options = webrtc::PeerConnectionInterface::kOnlyRelay; |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| native_peer_connection_->StartIce(native_options); |
| return true; |
| } |
| |
| bool PeerConnectionHandlerJsep::processIceMessage( |
| const WebKit::WebICECandidateDescriptor& candidate) { |
| int m_line_index = -1; |
| if (!base::StringToInt(UTF16ToUTF8(candidate.label()), &m_line_index)) { |
| LOG(ERROR) << "Invalid candidate label: " |
| << UTF16ToUTF8(candidate.label()); |
| return false; |
| } |
| // TODO(ronghuawu): Use sdp_mid when its available, for now its empty string. |
| const std::string sdp_mid = ""; |
| scoped_ptr<webrtc::IceCandidateInterface> native_candidate( |
| dependency_factory_->CreateIceCandidate( |
| sdp_mid, |
| m_line_index, |
| UTF16ToUTF8(candidate.candidateLine()))); |
| if (!native_candidate.get()) { |
| LOG(ERROR) << "Could not create native ICE candidate"; |
| return false; |
| } |
| |
| bool return_value = |
| native_peer_connection_->ProcessIceMessage(native_candidate.get()); |
| if (!return_value) |
| LOG(ERROR) << "Error processing ICE message"; |
| return return_value; |
| } |
| |
| void PeerConnectionHandlerJsep::addStream( |
| const WebKit::WebMediaStreamDescriptor& stream) { |
| AddStream(stream); |
| native_peer_connection_->CommitStreamChanges(); |
| } |
| |
| void PeerConnectionHandlerJsep::removeStream( |
| const WebKit::WebMediaStreamDescriptor& stream) { |
| RemoveStream(stream); |
| native_peer_connection_->CommitStreamChanges(); |
| } |
| |
| void PeerConnectionHandlerJsep::stop() { |
| DVLOG(1) << "PeerConnectionHandlerJsep::stop"; |
| native_peer_connection_ = NULL; |
| } |
| |
| void PeerConnectionHandlerJsep::OnError() { |
| // TODO(grunell): Implement. |
| NOTIMPLEMENTED(); |
| } |
| |
| void PeerConnectionHandlerJsep::OnMessage(const std::string& msg) { |
| // TODO(grunell): Implement. |
| NOTIMPLEMENTED(); |
| } |
| |
| void PeerConnectionHandlerJsep::OnSignalingMessage(const std::string& msg) { |
| // Not used by JSEP PeerConnection. |
| NOTREACHED(); |
| } |
| |
| void PeerConnectionHandlerJsep::OnStateChange(StateType state_changed) { |
| switch (state_changed) { |
| case kReadyState: |
| WebKit::WebPeerConnection00HandlerClient::ReadyState ready_state; |
| switch (native_peer_connection_->ready_state()) { |
| case webrtc::PeerConnectionInterface::kNew: |
| ready_state = WebKit::WebPeerConnection00HandlerClient::ReadyStateNew; |
| break; |
| case webrtc::PeerConnectionInterface::kOpening: |
| ready_state = |
| WebKit::WebPeerConnection00HandlerClient::ReadyStateNegotiating; |
| break; |
| case webrtc::PeerConnectionInterface::kActive: |
| ready_state = |
| WebKit::WebPeerConnection00HandlerClient::ReadyStateActive; |
| break; |
| case webrtc::PeerConnectionInterface::kClosing: |
| // Not used by JSEP. |
| NOTREACHED(); |
| return; |
| case webrtc::PeerConnectionInterface::kClosed: |
| ready_state = |
| WebKit::WebPeerConnection00HandlerClient::ReadyStateClosed; |
| break; |
| default: |
| NOTREACHED(); |
| return; |
| } |
| client_->didChangeReadyState(ready_state); |
| break; |
| case kIceState: |
| // TODO(grunell): Implement when available in native PeerConnection. |
| NOTIMPLEMENTED(); |
| break; |
| case kSdpState: |
| // Not used by JSEP. |
| NOTREACHED(); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void PeerConnectionHandlerJsep::OnAddStream( |
| webrtc::MediaStreamInterface* stream) { |
| if (!stream) |
| return; |
| |
| DCHECK(remote_streams_.find(stream) == remote_streams_.end()); |
| WebKit::WebMediaStreamDescriptor descriptor = |
| CreateWebKitStreamDescriptor(stream); |
| remote_streams_.insert( |
| std::pair<webrtc::MediaStreamInterface*, |
| WebKit::WebMediaStreamDescriptor>(stream, descriptor)); |
| client_->didAddRemoteStream(descriptor); |
| } |
| |
| void PeerConnectionHandlerJsep::OnRemoveStream( |
| webrtc::MediaStreamInterface* stream) { |
| if (!stream) |
| return; |
| |
| RemoteStreamMap::iterator it = remote_streams_.find(stream); |
| if (it == remote_streams_.end()) { |
| NOTREACHED() << "Stream not found"; |
| return; |
| } |
| WebKit::WebMediaStreamDescriptor descriptor = it->second; |
| DCHECK(!descriptor.isNull()); |
| remote_streams_.erase(it); |
| client_->didRemoveRemoteStream(descriptor); |
| } |
| |
| void PeerConnectionHandlerJsep::OnIceCandidate( |
| const webrtc::IceCandidateInterface* candidate) { |
| WebKit::WebICECandidateDescriptor web_candidate; |
| |
| std::string label = StringPrintf("%d", candidate->sdp_mline_index()); |
| std::string sdp; |
| if (!candidate->ToString(&sdp)) { |
| LOG(ERROR) << "Could not get SDP string"; |
| return; |
| } |
| |
| web_candidate.initialize(UTF8ToUTF16(label), UTF8ToUTF16(sdp)); |
| |
| // moreToFollow parameter isn't supported in native PeerConnection, so we |
| // always use true here, and then false in OnIceComplete(). |
| client_->didGenerateICECandidate(web_candidate, true); |
| } |
| |
| void PeerConnectionHandlerJsep::OnIceComplete() { |
| // moreToFollow parameter isn't supported in native PeerConnection, so we |
| // send an empty WebIseCandidate with moreToFollow=false. |
| WebKit::WebICECandidateDescriptor web_candidate; |
| client_->didGenerateICECandidate(web_candidate, false); |
| } |
| |
| webrtc::SessionDescriptionInterface* |
| PeerConnectionHandlerJsep::CreateNativeSessionDescription( |
| const WebKit::WebSessionDescriptionDescriptor& description) { |
| std::string initial_sdp = UTF16ToUTF8(description.initialSDP()); |
| webrtc::SessionDescriptionInterface* native_desc = |
| dependency_factory_->CreateSessionDescription(initial_sdp); |
| if (!native_desc) { |
| LOG(ERROR) << "Failed to create native session description"; |
| return NULL; |
| } |
| // TODO(ronghuawu): Apply sdp_mid when its available, now its empty string. |
| const std::string sdp_mid = ""; |
| for (size_t i = 0; i < description.numberOfAddedCandidates(); ++i) { |
| WebKit::WebICECandidateDescriptor candidate = description.candidate(i); |
| int m_line_index = -1; |
| if (!base::StringToInt(UTF16ToUTF8(candidate.label()), &m_line_index)) { |
| LOG(ERROR) << "Invalid candidate label: " |
| << UTF16ToUTF8(candidate.label()); |
| continue; |
| } |
| scoped_ptr<webrtc::IceCandidateInterface> native_candidate( |
| dependency_factory_->CreateIceCandidate( |
| sdp_mid, |
| m_line_index, |
| UTF16ToUTF8(candidate.candidateLine()))); |
| if (!native_desc->AddCandidate(native_candidate.get())) |
| LOG(ERROR) << "Failed to add candidate to native session description"; |
| } |
| |
| return native_desc; |
| } |
| |
| WebKit::WebSessionDescriptionDescriptor |
| PeerConnectionHandlerJsep::CreateWebKitSessionDescription( |
| const webrtc::SessionDescriptionInterface* native_desc) { |
| WebKit::WebSessionDescriptionDescriptor description; |
| if (!native_desc) { |
| VLOG(1) << "Native session description is null"; |
| return description; |
| } |
| |
| std::string sdp; |
| if (!native_desc->ToString(&sdp)) { |
| LOG(ERROR) << "Failed to get SDP string of native session description"; |
| return description; |
| } |
| |
| description.initialize(UTF8ToUTF16(sdp)); |
| return description; |
| } |
| |
| bool PeerConnectionHandlerJsep::GetNativeAction( |
| const Action action, |
| webrtc::PeerConnectionInterface::Action* native_action) { |
| switch (action) { |
| case ActionSDPOffer: |
| *native_action = webrtc::PeerConnectionInterface::kOffer; |
| break; |
| case ActionSDPPRanswer: |
| *native_action = webrtc::PeerConnectionInterface::kPrAnswer; |
| break; |
| case ActionSDPAnswer: |
| *native_action = webrtc::PeerConnectionInterface::kAnswer; |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| return true; |
| } |