| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * 3. Neither the name of Google Inc. nor the names of its contributors |
| * may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "modules/mediastream/RTCPeerConnection.h" |
| |
| #include "bindings/core/v8/ArrayValue.h" |
| #include "bindings/core/v8/ExceptionMessages.h" |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExceptionCode.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/html/VoidCallback.h" |
| #include "core/loader/FrameLoader.h" |
| #include "core/loader/FrameLoaderClient.h" |
| #include "modules/mediastream/MediaConstraintsImpl.h" |
| #include "modules/mediastream/MediaStreamEvent.h" |
| #include "modules/mediastream/RTCDTMFSender.h" |
| #include "modules/mediastream/RTCDataChannel.h" |
| #include "modules/mediastream/RTCDataChannelEvent.h" |
| #include "modules/mediastream/RTCErrorCallback.h" |
| #include "modules/mediastream/RTCIceCandidateEvent.h" |
| #include "modules/mediastream/RTCSessionDescription.h" |
| #include "modules/mediastream/RTCSessionDescriptionCallback.h" |
| #include "modules/mediastream/RTCSessionDescriptionRequestImpl.h" |
| #include "modules/mediastream/RTCStatsCallback.h" |
| #include "modules/mediastream/RTCStatsRequestImpl.h" |
| #include "modules/mediastream/RTCVoidRequestImpl.h" |
| #include "platform/mediastream/RTCConfiguration.h" |
| #include "platform/mediastream/RTCOfferOptions.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebMediaStream.h" |
| #include "public/platform/WebRTCConfiguration.h" |
| #include "public/platform/WebRTCDataChannelHandler.h" |
| #include "public/platform/WebRTCDataChannelInit.h" |
| #include "public/platform/WebRTCICECandidate.h" |
| #include "public/platform/WebRTCOfferOptions.h" |
| #include "public/platform/WebRTCSessionDescription.h" |
| #include "public/platform/WebRTCSessionDescriptionRequest.h" |
| #include "public/platform/WebRTCStatsRequest.h" |
| #include "public/platform/WebRTCVoidRequest.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| static bool throwExceptionIfSignalingStateClosed(RTCPeerConnection::SignalingState state, ExceptionState& exceptionState) |
| { |
| if (state == RTCPeerConnection::SignalingStateClosed) { |
| exceptionState.throwDOMException(InvalidStateError, "The RTCPeerConnection's signalingState is 'closed'."); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| RTCConfiguration* RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionState& exceptionState) |
| { |
| if (configuration.isUndefinedOrNull()) |
| return 0; |
| |
| RTCIceTransports iceTransports = RTCIceTransportsAll; |
| String iceTransportsString; |
| if (DictionaryHelper::get(configuration, "iceTransports", iceTransportsString)) { |
| if (iceTransportsString == "none") { |
| iceTransports = RTCIceTransportsNone; |
| } else if (iceTransportsString == "relay") { |
| iceTransports = RTCIceTransportsRelay; |
| } else if (iceTransportsString != "all") { |
| exceptionState.throwTypeError("Malformed RTCIceTransports"); |
| return 0; |
| } |
| } |
| |
| ArrayValue iceServers; |
| bool ok = DictionaryHelper::get(configuration, "iceServers", iceServers); |
| if (!ok || iceServers.isUndefinedOrNull()) { |
| exceptionState.throwTypeError("Malformed RTCConfiguration"); |
| return 0; |
| } |
| |
| size_t numberOfServers; |
| ok = iceServers.length(numberOfServers); |
| if (!ok) { |
| exceptionState.throwTypeError("Malformed RTCConfiguration"); |
| return 0; |
| } |
| |
| RTCConfiguration* rtcConfiguration = RTCConfiguration::create(); |
| rtcConfiguration->setIceTransports(iceTransports); |
| |
| for (size_t i = 0; i < numberOfServers; ++i) { |
| Dictionary iceServer; |
| ok = iceServers.get(i, iceServer); |
| if (!ok) { |
| exceptionState.throwTypeError("Malformed RTCIceServer"); |
| return 0; |
| } |
| |
| Vector<String> names; |
| iceServer.getOwnPropertyNames(names); |
| |
| Vector<String> urlStrings; |
| if (names.contains("urls")) { |
| if (!DictionaryHelper::get(iceServer, "urls", urlStrings) || !urlStrings.size()) { |
| String urlString; |
| if (DictionaryHelper::get(iceServer, "urls", urlString)) { |
| urlStrings.append(urlString); |
| } else { |
| exceptionState.throwTypeError("Malformed RTCIceServer"); |
| return 0; |
| } |
| } |
| } else if (names.contains("url")) { |
| String urlString; |
| if (DictionaryHelper::get(iceServer, "url", urlString)) { |
| urlStrings.append(urlString); |
| } else { |
| exceptionState.throwTypeError("Malformed RTCIceServer"); |
| return 0; |
| } |
| } else { |
| exceptionState.throwTypeError("Malformed RTCIceServer"); |
| return 0; |
| } |
| |
| String username, credential; |
| DictionaryHelper::get(iceServer, "username", username); |
| DictionaryHelper::get(iceServer, "credential", credential); |
| |
| for (Vector<String>::iterator iter = urlStrings.begin(); iter != urlStrings.end(); ++iter) { |
| KURL url(KURL(), *iter); |
| if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("turns") || url.protocolIs("stun"))) { |
| exceptionState.throwTypeError("Malformed URL"); |
| return 0; |
| } |
| |
| rtcConfiguration->appendServer(RTCIceServer::create(url, username, credential)); |
| } |
| } |
| |
| return rtcConfiguration; |
| } |
| |
| RTCOfferOptions* RTCPeerConnection::parseOfferOptions(const Dictionary& options, ExceptionState& exceptionState) |
| { |
| if (options.isUndefinedOrNull()) |
| return 0; |
| |
| Vector<String> propertyNames; |
| options.getOwnPropertyNames(propertyNames); |
| |
| // Treat |options| as MediaConstraints if it is empty or has "optional" or "mandatory" properties for compatibility. |
| // TODO(jiayl): remove constraints when RTCOfferOptions reaches Stable and client code is ready. |
| if (propertyNames.isEmpty() || propertyNames.contains("optional") || propertyNames.contains("mandatory")) |
| return 0; |
| |
| int32_t offerToReceiveVideo = -1; |
| int32_t offerToReceiveAudio = -1; |
| bool voiceActivityDetection = true; |
| bool iceRestart = false; |
| |
| if (DictionaryHelper::get(options, "offerToReceiveVideo", offerToReceiveVideo) && offerToReceiveVideo < 0) { |
| exceptionState.throwTypeError("Invalid offerToReceiveVideo"); |
| return 0; |
| } |
| |
| if (DictionaryHelper::get(options, "offerToReceiveAudio", offerToReceiveAudio) && offerToReceiveAudio < 0) { |
| exceptionState.throwTypeError("Invalid offerToReceiveAudio"); |
| return 0; |
| } |
| |
| DictionaryHelper::get(options, "voiceActivityDetection", voiceActivityDetection); |
| DictionaryHelper::get(options, "iceRestart", iceRestart); |
| |
| RTCOfferOptions* rtcOfferOptions = RTCOfferOptions::create(offerToReceiveVideo, offerToReceiveAudio, voiceActivityDetection, iceRestart); |
| return rtcOfferOptions; |
| } |
| |
| RTCPeerConnection* RTCPeerConnection::create(ExecutionContext* context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState) |
| { |
| RTCConfiguration* configuration = parseConfiguration(rtcConfiguration, exceptionState); |
| if (exceptionState.hadException()) |
| return 0; |
| |
| WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); |
| if (exceptionState.hadException()) |
| return 0; |
| |
| RTCPeerConnection* peerConnection = new RTCPeerConnection(context, configuration, constraints, exceptionState); |
| peerConnection->suspendIfNeeded(); |
| if (exceptionState.hadException()) |
| return 0; |
| |
| return peerConnection; |
| } |
| |
| RTCPeerConnection::RTCPeerConnection(ExecutionContext* context, RTCConfiguration* configuration, WebMediaConstraints constraints, ExceptionState& exceptionState) |
| : ActiveDOMObject(context) |
| , m_signalingState(SignalingStateStable) |
| , m_iceGatheringState(ICEGatheringStateNew) |
| , m_iceConnectionState(ICEConnectionStateNew) |
| , m_dispatchScheduledEventRunner(this, &RTCPeerConnection::dispatchScheduledEvent) |
| , m_stopped(false) |
| , m_closed(false) |
| { |
| Document* document = toDocument(executionContext()); |
| |
| // If we fail, set |m_closed| and |m_stopped| to true, to avoid hitting the assert in the destructor. |
| |
| if (!document->frame()) { |
| m_closed = true; |
| m_stopped = true; |
| exceptionState.throwDOMException(NotSupportedError, "PeerConnections may not be created in detached documents."); |
| return; |
| } |
| |
| m_peerHandler = adoptPtr(Platform::current()->createRTCPeerConnectionHandler(this)); |
| if (!m_peerHandler) { |
| m_closed = true; |
| m_stopped = true; |
| exceptionState.throwDOMException(NotSupportedError, "No PeerConnection handler can be created, perhaps WebRTC is disabled?"); |
| return; |
| } |
| |
| document->frame()->loader().client()->dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get()); |
| |
| if (!m_peerHandler->initialize(configuration, constraints)) { |
| m_closed = true; |
| m_stopped = true; |
| exceptionState.throwDOMException(NotSupportedError, "Failed to initialize native PeerConnection."); |
| return; |
| } |
| } |
| |
| RTCPeerConnection::~RTCPeerConnection() |
| { |
| // This checks that close() or stop() is called before the destructor. |
| // We are assuming that a wrapper is always created when RTCPeerConnection is created. |
| ASSERT(m_closed || m_stopped); |
| } |
| |
| void RTCPeerConnection::createOffer(RTCSessionDescriptionCallback* successCallback, RTCErrorCallback* errorCallback, const Dictionary& rtcOfferOptions, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| ASSERT(successCallback); |
| |
| RTCOfferOptions* offerOptions = parseOfferOptions(rtcOfferOptions, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| RTCSessionDescriptionRequest* request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback); |
| |
| if (offerOptions) { |
| m_peerHandler->createOffer(request, offerOptions); |
| } else { |
| WebMediaConstraints constraints = MediaConstraintsImpl::create(rtcOfferOptions, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| m_peerHandler->createOffer(request, constraints); |
| } |
| } |
| |
| void RTCPeerConnection::createAnswer(RTCSessionDescriptionCallback* successCallback, RTCErrorCallback* errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| ASSERT(successCallback); |
| |
| WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| RTCSessionDescriptionRequest* request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback); |
| m_peerHandler->createAnswer(request, constraints); |
| } |
| |
| void RTCPeerConnection::setLocalDescription(RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!sessionDescription) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription")); |
| return; |
| } |
| |
| RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); |
| m_peerHandler->setLocalDescription(request, sessionDescription->webSessionDescription()); |
| } |
| |
| RTCSessionDescription* RTCPeerConnection::localDescription(ExceptionState& exceptionState) |
| { |
| WebRTCSessionDescription webSessionDescription = m_peerHandler->localDescription(); |
| if (webSessionDescription.isNull()) |
| return nullptr; |
| |
| return RTCSessionDescription::create(webSessionDescription); |
| } |
| |
| void RTCPeerConnection::setRemoteDescription(RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!sessionDescription) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription")); |
| return; |
| } |
| |
| RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); |
| m_peerHandler->setRemoteDescription(request, sessionDescription->webSessionDescription()); |
| } |
| |
| RTCSessionDescription* RTCPeerConnection::remoteDescription(ExceptionState& exceptionState) |
| { |
| WebRTCSessionDescription webSessionDescription = m_peerHandler->remoteDescription(); |
| if (webSessionDescription.isNull()) |
| return nullptr; |
| |
| return RTCSessionDescription::create(webSessionDescription); |
| } |
| |
| void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| RTCConfiguration* configuration = parseConfiguration(rtcConfiguration, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| bool valid = m_peerHandler->updateICE(configuration, constraints); |
| if (!valid) |
| exceptionState.throwDOMException(SyntaxError, "Could not update the ICE Agent with the given configuration."); |
| } |
| |
| void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!iceCandidate) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate")); |
| return; |
| } |
| |
| bool valid = m_peerHandler->addICECandidate(iceCandidate->webCandidate()); |
| if (!valid) |
| exceptionState.throwDOMException(SyntaxError, "The ICE candidate could not be added."); |
| } |
| |
| void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!iceCandidate) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate")); |
| return; |
| } |
| ASSERT(successCallback); |
| ASSERT(errorCallback); |
| |
| RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); |
| |
| bool implemented = m_peerHandler->addICECandidate(request, iceCandidate->webCandidate()); |
| if (!implemented) { |
| exceptionState.throwDOMException(NotSupportedError, "This method is not yet implemented."); |
| } |
| } |
| |
| String RTCPeerConnection::signalingState() const |
| { |
| switch (m_signalingState) { |
| case SignalingStateStable: |
| return "stable"; |
| case SignalingStateHaveLocalOffer: |
| return "have-local-offer"; |
| case SignalingStateHaveRemoteOffer: |
| return "have-remote-offer"; |
| case SignalingStateHaveLocalPrAnswer: |
| return "have-local-pranswer"; |
| case SignalingStateHaveRemotePrAnswer: |
| return "have-remote-pranswer"; |
| case SignalingStateClosed: |
| return "closed"; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return String(); |
| } |
| |
| String RTCPeerConnection::iceGatheringState() const |
| { |
| switch (m_iceGatheringState) { |
| case ICEGatheringStateNew: |
| return "new"; |
| case ICEGatheringStateGathering: |
| return "gathering"; |
| case ICEGatheringStateComplete: |
| return "complete"; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return String(); |
| } |
| |
| String RTCPeerConnection::iceConnectionState() const |
| { |
| switch (m_iceConnectionState) { |
| case ICEConnectionStateNew: |
| return "new"; |
| case ICEConnectionStateChecking: |
| return "checking"; |
| case ICEConnectionStateConnected: |
| return "connected"; |
| case ICEConnectionStateCompleted: |
| return "completed"; |
| case ICEConnectionStateFailed: |
| return "failed"; |
| case ICEConnectionStateDisconnected: |
| return "disconnected"; |
| case ICEConnectionStateClosed: |
| return "closed"; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return String(); |
| } |
| |
| void RTCPeerConnection::addStream(MediaStream* stream, const Dictionary& mediaConstraints, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!stream) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream")); |
| return; |
| } |
| |
| if (m_localStreams.contains(stream)) |
| return; |
| |
| WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); |
| if (exceptionState.hadException()) |
| return; |
| |
| m_localStreams.append(stream); |
| |
| bool valid = m_peerHandler->addStream(stream->descriptor(), constraints); |
| if (!valid) |
| exceptionState.throwDOMException(SyntaxError, "Unable to add the provided stream."); |
| } |
| |
| void RTCPeerConnection::removeStream(MediaStream* stream, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| if (!stream) { |
| exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream")); |
| return; |
| } |
| |
| size_t pos = m_localStreams.find(stream); |
| if (pos == kNotFound) |
| return; |
| |
| m_localStreams.remove(pos); |
| |
| m_peerHandler->removeStream(stream->descriptor()); |
| } |
| |
| MediaStreamVector RTCPeerConnection::getLocalStreams() const |
| { |
| return m_localStreams; |
| } |
| |
| MediaStreamVector RTCPeerConnection::getRemoteStreams() const |
| { |
| return m_remoteStreams; |
| } |
| |
| MediaStream* RTCPeerConnection::getStreamById(const String& streamId) |
| { |
| for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) { |
| if ((*iter)->id() == streamId) |
| return iter->get(); |
| } |
| |
| for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) { |
| if ((*iter)->id() == streamId) |
| return iter->get(); |
| } |
| |
| return 0; |
| } |
| |
| void RTCPeerConnection::getStats(RTCStatsCallback* successCallback, MediaStreamTrack* selector) |
| { |
| RTCStatsRequest* statsRequest = RTCStatsRequestImpl::create(executionContext(), this, successCallback, selector); |
| // FIXME: Add passing selector as part of the statsRequest. |
| m_peerHandler->getStats(statsRequest); |
| } |
| |
| RTCDataChannel* RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return nullptr; |
| |
| WebRTCDataChannelInit init; |
| DictionaryHelper::get(options, "ordered", init.ordered); |
| DictionaryHelper::get(options, "negotiated", init.negotiated); |
| |
| unsigned short value = 0; |
| if (DictionaryHelper::get(options, "id", value)) |
| init.id = value; |
| if (DictionaryHelper::get(options, "maxRetransmits", value)) |
| init.maxRetransmits = value; |
| if (DictionaryHelper::get(options, "maxRetransmitTime", value)) |
| init.maxRetransmitTime = value; |
| |
| String protocolString; |
| DictionaryHelper::get(options, "protocol", protocolString); |
| init.protocol = protocolString; |
| |
| RTCDataChannel* channel = RTCDataChannel::create(executionContext(), this, m_peerHandler.get(), label, init, exceptionState); |
| if (exceptionState.hadException()) |
| return nullptr; |
| m_dataChannels.append(channel); |
| return channel; |
| } |
| |
| bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId) |
| { |
| for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) { |
| if ((*iter)->getTrackById(trackId)) |
| return true; |
| } |
| return false; |
| } |
| |
| RTCDTMFSender* RTCPeerConnection::createDTMFSender(MediaStreamTrack* track, ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return nullptr; |
| |
| if (!track) { |
| exceptionState.throwTypeError(ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStreamTrack")); |
| return nullptr; |
| } |
| |
| if (!hasLocalStreamWithTrackId(track->id())) { |
| exceptionState.throwDOMException(SyntaxError, "No local stream is available for the track provided."); |
| return nullptr; |
| } |
| |
| RTCDTMFSender* dtmfSender = RTCDTMFSender::create(executionContext(), m_peerHandler.get(), track, exceptionState); |
| if (exceptionState.hadException()) |
| return nullptr; |
| return dtmfSender; |
| } |
| |
| void RTCPeerConnection::close(ExceptionState& exceptionState) |
| { |
| if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) |
| return; |
| |
| closeInternal(); |
| } |
| |
| void RTCPeerConnection::negotiationNeeded() |
| { |
| ASSERT(!m_closed); |
| scheduleDispatchEvent(Event::create(EventTypeNames::negotiationneeded)); |
| } |
| |
| void RTCPeerConnection::didGenerateICECandidate(const WebRTCICECandidate& webCandidate) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| if (webCandidate.isNull()) |
| scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, nullptr)); |
| else { |
| RTCIceCandidate* iceCandidate = RTCIceCandidate::create(webCandidate); |
| scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate)); |
| } |
| } |
| |
| void RTCPeerConnection::didChangeSignalingState(SignalingState newState) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| changeSignalingState(newState); |
| } |
| |
| void RTCPeerConnection::didChangeICEGatheringState(ICEGatheringState newState) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| changeIceGatheringState(newState); |
| } |
| |
| void RTCPeerConnection::didChangeICEConnectionState(ICEConnectionState newState) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| changeIceConnectionState(newState); |
| } |
| |
| void RTCPeerConnection::didAddRemoteStream(const WebMediaStream& remoteStream) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| |
| if (m_signalingState == SignalingStateClosed) |
| return; |
| |
| MediaStream* stream = MediaStream::create(executionContext(), remoteStream); |
| m_remoteStreams.append(stream); |
| |
| scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::addstream, false, false, stream)); |
| } |
| |
| void RTCPeerConnection::didRemoveRemoteStream(const WebMediaStream& remoteStream) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| |
| MediaStreamDescriptor* streamDescriptor = remoteStream; |
| ASSERT(streamDescriptor->client()); |
| |
| MediaStream* stream = static_cast<MediaStream*>(streamDescriptor->client()); |
| stream->streamEnded(); |
| |
| if (m_signalingState == SignalingStateClosed) |
| return; |
| |
| size_t pos = m_remoteStreams.find(stream); |
| ASSERT(pos != kNotFound); |
| m_remoteStreams.remove(pos); |
| |
| scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::removestream, false, false, stream)); |
| } |
| |
| void RTCPeerConnection::didAddRemoteDataChannel(WebRTCDataChannelHandler* handler) |
| { |
| ASSERT(!m_closed); |
| ASSERT(executionContext()->isContextThread()); |
| |
| if (m_signalingState == SignalingStateClosed) |
| return; |
| |
| RTCDataChannel* channel = RTCDataChannel::create(executionContext(), this, adoptPtr(handler)); |
| m_dataChannels.append(channel); |
| |
| scheduleDispatchEvent(RTCDataChannelEvent::create(EventTypeNames::datachannel, false, false, channel)); |
| } |
| |
| void RTCPeerConnection::releasePeerConnectionHandler() |
| { |
| stop(); |
| } |
| |
| void RTCPeerConnection::closePeerConnection() |
| { |
| ASSERT(m_signalingState != RTCPeerConnection::SignalingStateClosed); |
| closeInternal(); |
| } |
| |
| const AtomicString& RTCPeerConnection::interfaceName() const |
| { |
| return EventTargetNames::RTCPeerConnection; |
| } |
| |
| ExecutionContext* RTCPeerConnection::executionContext() const |
| { |
| return ActiveDOMObject::executionContext(); |
| } |
| |
| void RTCPeerConnection::suspend() |
| { |
| m_dispatchScheduledEventRunner.suspend(); |
| } |
| |
| void RTCPeerConnection::resume() |
| { |
| m_dispatchScheduledEventRunner.resume(); |
| } |
| |
| void RTCPeerConnection::stop() |
| { |
| if (m_stopped) |
| return; |
| |
| m_stopped = true; |
| m_iceConnectionState = ICEConnectionStateClosed; |
| m_signalingState = SignalingStateClosed; |
| |
| HeapVector<Member<RTCDataChannel> >::iterator i = m_dataChannels.begin(); |
| for (; i != m_dataChannels.end(); ++i) |
| (*i)->stop(); |
| m_dataChannels.clear(); |
| |
| m_dispatchScheduledEventRunner.stop(); |
| |
| m_peerHandler.clear(); |
| } |
| |
| void RTCPeerConnection::changeSignalingState(SignalingState signalingState) |
| { |
| if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) { |
| m_signalingState = signalingState; |
| scheduleDispatchEvent(Event::create(EventTypeNames::signalingstatechange)); |
| } |
| } |
| |
| void RTCPeerConnection::changeIceGatheringState(ICEGatheringState iceGatheringState) |
| { |
| m_iceGatheringState = iceGatheringState; |
| } |
| |
| void RTCPeerConnection::changeIceConnectionState(ICEConnectionState iceConnectionState) |
| { |
| if (m_iceConnectionState != ICEConnectionStateClosed && m_iceConnectionState != iceConnectionState) { |
| m_iceConnectionState = iceConnectionState; |
| scheduleDispatchEvent(Event::create(EventTypeNames::iceconnectionstatechange)); |
| } |
| } |
| |
| void RTCPeerConnection::closeInternal() |
| { |
| ASSERT(m_signalingState != RTCPeerConnection::SignalingStateClosed); |
| m_peerHandler->stop(); |
| m_closed = true; |
| |
| changeIceConnectionState(ICEConnectionStateClosed); |
| changeIceGatheringState(ICEGatheringStateComplete); |
| changeSignalingState(SignalingStateClosed); |
| } |
| |
| void RTCPeerConnection::scheduleDispatchEvent(PassRefPtrWillBeRawPtr<Event> event) |
| { |
| m_scheduledEvents.append(event); |
| |
| m_dispatchScheduledEventRunner.runAsync(); |
| } |
| |
| void RTCPeerConnection::dispatchScheduledEvent() |
| { |
| if (m_stopped) |
| return; |
| |
| WillBeHeapVector<RefPtrWillBeMember<Event> > events; |
| events.swap(m_scheduledEvents); |
| |
| WillBeHeapVector<RefPtrWillBeMember<Event> >::iterator it = events.begin(); |
| for (; it != events.end(); ++it) |
| dispatchEvent((*it).release()); |
| |
| events.clear(); |
| } |
| |
| void RTCPeerConnection::trace(Visitor* visitor) |
| { |
| visitor->trace(m_localStreams); |
| visitor->trace(m_remoteStreams); |
| visitor->trace(m_dataChannels); |
| #if ENABLE(OILPAN) |
| visitor->trace(m_scheduledEvents); |
| #endif |
| EventTargetWithInlineData::trace(visitor); |
| } |
| |
| } // namespace blink |