// 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 #include #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" namespace content { 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 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 native_offer( dependency_factory_->CreateSessionDescription(UTF16ToUTF8(offer))); if (!native_offer.get()) { LOG(ERROR) << "Failed to create native offer"; return answer; } scoped_ptr 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 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); } void PeerConnectionHandlerJsep::removeStream( const WebKit::WebMediaStreamDescriptor& stream) { RemoveStream(stream); } void PeerConnectionHandlerJsep::stop() { DVLOG(1) << "PeerConnectionHandlerJsep::stop"; native_peer_connection_ = NULL; } void PeerConnectionHandlerJsep::OnError() { // TODO(grunell): Implement. NOTIMPLEMENTED(); } 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; 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(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 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; } } // namespace content