summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/mediastream/SDPProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/mediastream/SDPProcessor.cpp')
-rw-r--r--Source/WebCore/Modules/mediastream/SDPProcessor.cpp547
1 files changed, 547 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/mediastream/SDPProcessor.cpp b/Source/WebCore/Modules/mediastream/SDPProcessor.cpp
new file mode 100644
index 000000000..9254fdc07
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/SDPProcessor.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ * Copyright (C) 2016 Apple 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 Ericsson 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"
+
+#if ENABLE(WEB_RTC)
+#include "SDPProcessor.h"
+
+#include "CommonVM.h"
+#include "Document.h"
+#include "Frame.h"
+#include "SDPProcessorScriptResource.h"
+#include "ScriptController.h"
+#include "ScriptSourceCode.h"
+#include "inspector/InspectorValues.h"
+#include <bindings/ScriptObject.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace Inspector;
+
+namespace WebCore {
+
+#define STRING_FUNCTION(name) \
+ static const String& name##String() \
+ { \
+ static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
+ return name; \
+ }
+
+STRING_FUNCTION(address)
+STRING_FUNCTION(apt)
+STRING_FUNCTION(candidates)
+STRING_FUNCTION(ccmfir)
+STRING_FUNCTION(channels)
+STRING_FUNCTION(clockRate)
+STRING_FUNCTION(cname)
+STRING_FUNCTION(componentId)
+STRING_FUNCTION(dtls)
+STRING_FUNCTION(encodingName)
+STRING_FUNCTION(fingerprint)
+STRING_FUNCTION(fingerprintHashFunction)
+STRING_FUNCTION(foundation)
+STRING_FUNCTION(ice)
+STRING_FUNCTION(mediaDescriptions)
+STRING_FUNCTION(mediaStreamId)
+STRING_FUNCTION(mediaStreamTrackId)
+STRING_FUNCTION(mid)
+STRING_FUNCTION(mode)
+STRING_FUNCTION(mux)
+STRING_FUNCTION(nack)
+STRING_FUNCTION(nackpli)
+STRING_FUNCTION(originator)
+STRING_FUNCTION(packetizationMode)
+STRING_FUNCTION(parameters)
+STRING_FUNCTION(password)
+STRING_FUNCTION(payloads)
+STRING_FUNCTION(port)
+STRING_FUNCTION(priority)
+STRING_FUNCTION(relatedAddress)
+STRING_FUNCTION(relatedPort)
+STRING_FUNCTION(rtcp)
+STRING_FUNCTION(rtcpAddress)
+STRING_FUNCTION(rtcpPort)
+STRING_FUNCTION(rtxTime)
+STRING_FUNCTION(sessionId)
+STRING_FUNCTION(sessionVersion)
+STRING_FUNCTION(setup)
+STRING_FUNCTION(ssrcs)
+STRING_FUNCTION(tcpType)
+STRING_FUNCTION(transport)
+STRING_FUNCTION(type)
+STRING_FUNCTION(ufrag)
+
+SDPProcessor::SDPProcessor(ScriptExecutionContext* context)
+ : ContextDestructionObserver(context)
+{
+}
+
+// Note that MediaEndpointSessionConfiguration is a "flatter" structure that the JSON representation. For
+// example, the JSON representation has an "ice" object which collects a set of properties under a
+// namespace. MediaEndpointSessionConfiguration has "ice"-prefixes on the corresponding properties.
+
+static RefPtr<InspectorObject> createCandidateObject(const IceCandidate& candidate)
+{
+ RefPtr<InspectorObject> candidateObject = InspectorObject::create();
+
+ candidateObject->setString(typeString(), candidate.type);
+ candidateObject->setString(foundationString(), candidate.foundation);
+ candidateObject->setInteger(componentIdString(), candidate.componentId);
+ candidateObject->setString(transportString(), candidate.transport);
+ candidateObject->setInteger(priorityString(), candidate.priority);
+ candidateObject->setString(addressString(), candidate.address);
+ candidateObject->setInteger(portString(), candidate.port);
+ if (!candidate.tcpType.isEmpty())
+ candidateObject->setString(tcpTypeString(), candidate.tcpType);
+ if (candidate.type.convertToASCIIUppercase() != "HOST") {
+ candidateObject->setString(relatedAddressString(), candidate.relatedAddress);
+ candidateObject->setInteger(relatedPortString(), candidate.relatedPort);
+ }
+
+ return candidateObject;
+}
+
+static IceCandidate createCandidate(const InspectorObject& candidateObject)
+{
+ IceCandidate candidate;
+ String stringValue;
+ unsigned intValue;
+
+ if (candidateObject.getString(typeString(), stringValue))
+ candidate.type = stringValue;
+
+ if (candidateObject.getString(foundationString(), stringValue))
+ candidate.foundation = stringValue;
+
+ if (candidateObject.getInteger(componentIdString(), intValue))
+ candidate.componentId = intValue;
+
+ if (candidateObject.getString(transportString(), stringValue))
+ candidate.transport = stringValue;
+
+ if (candidateObject.getInteger(priorityString(), intValue))
+ candidate.priority = intValue;
+
+ if (candidateObject.getString(addressString(), stringValue))
+ candidate.address = stringValue;
+
+ if (candidateObject.getInteger(portString(), intValue))
+ candidate.port = intValue;
+
+ if (candidateObject.getString(tcpTypeString(), stringValue))
+ candidate.tcpType = stringValue;
+
+ if (candidateObject.getString(relatedAddressString(), stringValue))
+ candidate.relatedAddress = stringValue;
+
+ if (candidateObject.getInteger(relatedPortString(), intValue))
+ candidate.relatedPort = intValue;
+
+ return candidate;
+}
+
+static RefPtr<MediaEndpointSessionConfiguration> configurationFromJSON(const String& json)
+{
+ RefPtr<InspectorValue> value;
+ if (!InspectorValue::parseJSON(json, value))
+ return nullptr;
+
+ RefPtr<InspectorObject> object;
+ if (!value->asObject(object))
+ return nullptr;
+
+ RefPtr<MediaEndpointSessionConfiguration> configuration = MediaEndpointSessionConfiguration::create();
+
+ String stringValue;
+ unsigned intValue;
+ unsigned long longValue;
+ bool boolValue;
+
+ RefPtr<InspectorObject> originatorObject = InspectorObject::create();
+ if (object->getObject(originatorString(), originatorObject)) {
+ if (originatorObject->getInteger(sessionIdString(), longValue))
+ configuration->setSessionId(longValue);
+ if (originatorObject->getInteger(sessionVersionString(), intValue))
+ configuration->setSessionVersion(intValue);
+ }
+
+ RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
+ object->getArray(mediaDescriptionsString(), mediaDescriptionsArray);
+
+ for (unsigned i = 0; i < mediaDescriptionsArray->length(); ++i) {
+ RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
+ mediaDescriptionsArray->get(i)->asObject(mediaDescriptionObject);
+
+ PeerMediaDescription mediaDescription;
+
+ if (mediaDescriptionObject->getString(typeString(), stringValue))
+ mediaDescription.type = stringValue;
+
+ if (mediaDescriptionObject->getInteger(portString(), intValue))
+ mediaDescription.port = intValue;
+
+ if (mediaDescriptionObject->getString(addressString(), stringValue))
+ mediaDescription.address = stringValue;
+
+ if (mediaDescriptionObject->getString(modeString(), stringValue))
+ mediaDescription.mode = stringValue;
+
+ if (mediaDescriptionObject->getString(midString(), stringValue))
+ mediaDescription.mid = stringValue;
+
+ RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
+ mediaDescriptionObject->getArray(payloadsString(), payloadsArray);
+
+ for (unsigned j = 0; j < payloadsArray->length(); ++j) {
+ RefPtr<InspectorObject> payloadsObject = InspectorObject::create();
+ payloadsArray->get(j)->asObject(payloadsObject);
+
+ MediaPayload payload;
+
+ if (payloadsObject->getInteger(typeString(), intValue))
+ payload.type = intValue;
+
+ if (payloadsObject->getString(encodingNameString(), stringValue))
+ payload.encodingName = stringValue;
+
+ if (payloadsObject->getInteger(clockRateString(), intValue))
+ payload.clockRate = intValue;
+
+ if (payloadsObject->getInteger(channelsString(), intValue))
+ payload.channels = intValue;
+
+ if (payloadsObject->getBoolean(ccmfirString(), boolValue))
+ payload.ccmfir = boolValue;
+
+ if (payloadsObject->getBoolean(nackpliString(), boolValue))
+ payload.nackpli = boolValue;
+
+ if (payloadsObject->getBoolean(nackString(), boolValue))
+ payload.nack = boolValue;
+
+ RefPtr<InspectorObject> parametersObject = InspectorObject::create();
+ if (payloadsObject->getObject(parametersString(), parametersObject)) {
+ if (parametersObject->getInteger(packetizationModeString(), intValue))
+ payload.addParameter("packetizationMode", intValue);
+
+ if (parametersObject->getInteger(aptString(), intValue))
+ payload.addParameter("apt", intValue);
+
+ if (parametersObject->getInteger(rtxTimeString(), intValue))
+ payload.addParameter("rtxTime", intValue);
+ }
+
+ mediaDescription.addPayload(WTFMove(payload));
+ }
+
+ RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(rtcpString(), rtcpObject)) {
+ if (rtcpObject->getBoolean(muxString(), boolValue))
+ mediaDescription.rtcpMux = boolValue;
+
+ if (rtcpObject->getString(rtcpAddressString(), stringValue))
+ mediaDescription.rtcpAddress = stringValue;
+
+ if (rtcpObject->getInteger(rtcpPortString(), intValue))
+ mediaDescription.rtcpPort = intValue;
+ }
+
+ if (mediaDescriptionObject->getString(mediaStreamIdString(), stringValue))
+ mediaDescription.mediaStreamId = stringValue;
+
+ if (mediaDescriptionObject->getString(mediaStreamTrackIdString(), stringValue))
+ mediaDescription.mediaStreamTrackId = stringValue;
+
+ RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(dtlsString(), dtlsObject)) {
+ if (dtlsObject->getString(setupString(), stringValue))
+ mediaDescription.dtlsSetup = stringValue;
+
+ if (dtlsObject->getString(fingerprintHashFunctionString(), stringValue))
+ mediaDescription.dtlsFingerprintHashFunction = stringValue;
+
+ if (dtlsObject->getString(fingerprintString(), stringValue))
+ mediaDescription.dtlsFingerprint = stringValue;
+ }
+
+ RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
+ mediaDescriptionObject->getArray(ssrcsString(), ssrcsArray);
+
+ for (unsigned j = 0; j < ssrcsArray->length(); ++j) {
+ ssrcsArray->get(j)->asInteger(intValue);
+ mediaDescription.addSsrc(intValue);
+ }
+
+ if (mediaDescriptionObject->getString(cnameString(), stringValue))
+ mediaDescription.cname = stringValue;
+
+ RefPtr<InspectorObject> iceObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(iceString(), iceObject)) {
+ if (iceObject->getString(ufragString(), stringValue))
+ mediaDescription.iceUfrag = stringValue;
+
+ if (iceObject->getString(passwordString(), stringValue))
+ mediaDescription.icePassword = stringValue;
+
+ RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
+ iceObject->getArray(candidatesString(), candidatesArray);
+
+ for (unsigned j = 0; j < candidatesArray->length(); ++j) {
+ RefPtr<InspectorObject> candidateObject = InspectorObject::create();
+ candidatesArray->get(j)->asObject(candidateObject);
+
+ mediaDescription.addIceCandidate(createCandidate(*candidateObject));
+ }
+ }
+
+ configuration->addMediaDescription(WTFMove(mediaDescription));
+ }
+
+ return configuration;
+}
+
+static std::optional<IceCandidate> iceCandidateFromJSON(const String& json)
+{
+ RefPtr<InspectorValue> value;
+ if (!InspectorValue::parseJSON(json, value))
+ return std::nullopt;
+
+ RefPtr<InspectorObject> candidateObject;
+ if (!value->asObject(candidateObject))
+ return std::nullopt;
+
+ return createCandidate(*candidateObject);
+}
+
+static String configurationToJSON(const MediaEndpointSessionConfiguration& configuration)
+{
+ RefPtr<InspectorObject> object = InspectorObject::create();
+
+ RefPtr<InspectorObject> originatorObject = InspectorObject::create();
+ originatorObject->setDouble(sessionIdString(), configuration.sessionId());
+ originatorObject->setInteger(sessionVersionString(), configuration.sessionVersion());
+ object->setObject(originatorString(), originatorObject);
+
+ RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
+
+ for (const auto& mediaDescription : configuration.mediaDescriptions()) {
+ RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
+
+ mediaDescriptionObject->setString(typeString(), mediaDescription.type);
+ mediaDescriptionObject->setInteger(portString(), mediaDescription.port);
+ mediaDescriptionObject->setString(addressString(), mediaDescription.address);
+ mediaDescriptionObject->setString(modeString(), mediaDescription.mode);
+ mediaDescriptionObject->setString(midString(), mediaDescription.mid);
+
+ RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
+
+ for (auto& payload : mediaDescription.payloads) {
+ RefPtr<InspectorObject> payloadObject = InspectorObject::create();
+
+ payloadObject->setInteger(typeString(), payload.type);
+ payloadObject->setString(encodingNameString(), payload.encodingName);
+ payloadObject->setInteger(clockRateString(), payload.clockRate);
+ payloadObject->setInteger(channelsString(), payload.channels);
+ payloadObject->setBoolean(ccmfirString(), payload.ccmfir);
+ payloadObject->setBoolean(nackpliString(), payload.nackpli);
+ payloadObject->setBoolean(nackString(), payload.nack);
+
+ if (!payload.parameters.isEmpty()) {
+ RefPtr<InspectorObject> parametersObject = InspectorObject::create();
+
+ for (auto& name : payload.parameters.keys())
+ parametersObject->setInteger(name, payload.parameters.get(name));
+
+ payloadObject->setObject(parametersString(), parametersObject);
+ }
+
+ payloadsArray->pushObject(payloadObject);
+ }
+ mediaDescriptionObject->setArray(payloadsString(), payloadsArray);
+
+ RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
+ rtcpObject->setBoolean(muxString(), mediaDescription.rtcpMux);
+ rtcpObject->setString(addressString(), mediaDescription.rtcpAddress);
+ rtcpObject->setInteger(portString(), mediaDescription.rtcpPort);
+ mediaDescriptionObject->setObject(rtcpString(), rtcpObject);
+
+ mediaDescriptionObject->setString(mediaStreamIdString(), mediaDescription.mediaStreamId);
+ mediaDescriptionObject->setString(mediaStreamTrackIdString(), mediaDescription.mediaStreamTrackId);
+
+ RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
+ dtlsObject->setString(setupString(), mediaDescription.dtlsSetup);
+ dtlsObject->setString(fingerprintHashFunctionString(), mediaDescription.dtlsFingerprintHashFunction);
+ dtlsObject->setString(fingerprintString(), mediaDescription.dtlsFingerprint);
+ mediaDescriptionObject->setObject(dtlsString(), dtlsObject);
+
+ RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
+
+ for (auto ssrc : mediaDescription.ssrcs)
+ ssrcsArray->pushDouble(ssrc);
+ mediaDescriptionObject->setArray(ssrcsString(), ssrcsArray);
+
+ mediaDescriptionObject->setString(cnameString(), mediaDescription.cname);
+
+ RefPtr<InspectorObject> iceObject = InspectorObject::create();
+ iceObject->setString(ufragString(), mediaDescription.iceUfrag);
+ iceObject->setString(passwordString(), mediaDescription.icePassword);
+
+ RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
+
+ for (auto& candidate : mediaDescription.iceCandidates)
+ candidatesArray->pushObject(createCandidateObject(candidate));
+
+ iceObject->setArray(candidatesString(), candidatesArray);
+ mediaDescriptionObject->setObject(iceString(), iceObject);
+
+ mediaDescriptionsArray->pushObject(mediaDescriptionObject);
+ }
+ object->setArray(mediaDescriptionsString(), mediaDescriptionsArray);
+
+ return object->toJSONString();
+}
+
+static String iceCandidateToJSON(const IceCandidate& candidate)
+{
+ return createCandidateObject(candidate)->toJSONString();
+}
+
+SDPProcessor::Result SDPProcessor::generate(const MediaEndpointSessionConfiguration& configuration, String& outSdpString) const
+{
+ String sdpString;
+ if (!callScript("generate", configurationToJSON(configuration), sdpString))
+ return Result::InternalError;
+
+ outSdpString = sdpString;
+ return Result::Success;
+}
+
+SDPProcessor::Result SDPProcessor::parse(const String& sdp, RefPtr<MediaEndpointSessionConfiguration>& outConfiguration) const
+{
+ String scriptOutput;
+ if (!callScript("parse", sdp, scriptOutput))
+ return Result::InternalError;
+
+ if (scriptOutput == "ParseError")
+ return Result::ParseError;
+
+ RefPtr<MediaEndpointSessionConfiguration> configuration = configurationFromJSON(scriptOutput);
+ if (!configuration)
+ return Result::InternalError;
+
+ outConfiguration = configuration;
+ return Result::Success;
+}
+
+SDPProcessor::Result SDPProcessor::generateCandidateLine(const IceCandidate& candidate, String& outCandidateLine) const
+{
+ String candidateLine;
+ if (!callScript("generateCandidateLine", iceCandidateToJSON(candidate), candidateLine))
+ return Result::InternalError;
+
+ outCandidateLine = candidateLine;
+ return Result::Success;
+}
+
+SDPProcessor::ParsingResult SDPProcessor::parseCandidateLine(const String& candidateLine) const
+{
+ String scriptOutput;
+ if (!callScript("parseCandidateLine", candidateLine, scriptOutput))
+ return { Result::InternalError };
+
+ if (scriptOutput == "ParseError")
+ return { Result::ParseError };
+
+ auto candidate = iceCandidateFromJSON(scriptOutput);
+ if (!candidate)
+ return { Result::InternalError };
+ return { WTFMove(candidate.value()) };
+}
+
+bool SDPProcessor::callScript(const String& functionName, const String& argument, String& outResult) const
+{
+ if (!scriptExecutionContext())
+ return false;
+
+ Document* document = downcast<Document>(scriptExecutionContext());
+ if (!document->frame())
+ return false;
+
+ if (!m_isolatedWorld)
+ m_isolatedWorld = DOMWrapperWorld::create(commonVM());
+
+ ScriptController& scriptController = document->frame()->script();
+ JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(*m_isolatedWorld));
+ JSC::VM& vm = globalObject->vm();
+ JSC::JSLockHolder lock(vm);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+ JSC::ExecState* exec = globalObject->globalExec();
+
+ JSC::JSValue probeFunctionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, "generate"));
+ if (!probeFunctionValue.isFunction()) {
+ URL scriptURL;
+ scriptController.evaluateInWorld(ScriptSourceCode(SDPProcessorScriptResource::scriptString(), scriptURL), *m_isolatedWorld);
+ if (UNLIKELY(scope.exception())) {
+ scope.clearException();
+ return false;
+ }
+ }
+
+ JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, functionName));
+ if (!functionValue.isFunction())
+ return false;
+
+ JSC::JSObject* function = functionValue.toObject(exec);
+ JSC::CallData callData;
+ JSC::CallType callType = function->methodTable()->getCallData(function, callData);
+ if (callType == JSC::CallType::None)
+ return false;
+
+ JSC::MarkedArgumentBuffer argList;
+ argList.append(JSC::jsString(exec, argument));
+
+ JSC::JSValue result = JSC::call(exec, function, callType, callData, globalObject, argList);
+ if (UNLIKELY(scope.exception())) {
+ LOG_ERROR("SDPProcessor script threw in function %s", functionName.ascii().data());
+ scope.clearException();
+ return false;
+ }
+
+ if (!result.isString())
+ return false;
+
+ outResult = asString(result)->value(exec);
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)