blob: 45e4d17363ff1b6407e3c099c0fc7f4282fa68b0 [file] [log] [blame]
/*
* Copyright (C) 2018 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "LibWebRTCUtils.h"
#if USE(LIBWEBRTC)
#include "LibWebRTCMacros.h"
#include "RTCDtlsTransportState.h"
#include "RTCError.h"
#include "RTCIceCandidate.h"
#include "RTCPeerConnection.h"
#include "RTCRtpSendParameters.h"
#include <wtf/text/WTFString.h>
ALLOW_UNUSED_PARAMETERS_BEGIN
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
#include <webrtc/api/rtp_parameters.h>
#include <webrtc/api/rtp_transceiver_interface.h>
#include <webrtc/pc/webrtc_sdp.h>
ALLOW_DEPRECATED_DECLARATIONS_END
ALLOW_UNUSED_PARAMETERS_END
namespace WebCore {
webrtc::Priority fromRTCPriorityType(RTCPriorityType priority)
{
switch (priority) {
case RTCPriorityType::VeryLow:
return webrtc::Priority::kVeryLow;
case RTCPriorityType::Low:
return webrtc::Priority::kLow;
case RTCPriorityType::Medium:
return webrtc::Priority::kMedium;
case RTCPriorityType::High:
return webrtc::Priority::kHigh;
}
RELEASE_ASSERT_NOT_REACHED();
}
RTCPriorityType toRTCPriorityType(webrtc::Priority priority)
{
switch (priority) {
case webrtc::Priority::kVeryLow:
return RTCPriorityType::VeryLow;
case webrtc::Priority::kLow:
return RTCPriorityType::Low;
case webrtc::Priority::kMedium:
return RTCPriorityType::Medium;
case webrtc::Priority::kHigh:
return RTCPriorityType::High;
}
RELEASE_ASSERT_NOT_REACHED();
}
static inline double toWebRTCBitRatePriority(RTCPriorityType priority)
{
switch (priority) {
case RTCPriorityType::VeryLow:
return 0.5;
case RTCPriorityType::Low:
return 1;
case RTCPriorityType::Medium:
return 2;
case RTCPriorityType::High:
return 4;
}
RELEASE_ASSERT_NOT_REACHED();
}
static inline RTCPriorityType fromWebRTCBitRatePriority(double priority)
{
if (priority < 0.7)
return RTCPriorityType::VeryLow;
if (priority < 1.5)
return RTCPriorityType::Low;
if (priority < 2.5)
return RTCPriorityType::Medium;
return RTCPriorityType::High;
}
static inline RTCRtpEncodingParameters toRTCEncodingParameters(const webrtc::RtpEncodingParameters& rtcParameters)
{
RTCRtpEncodingParameters parameters;
if (rtcParameters.ssrc)
parameters.ssrc = *rtcParameters.ssrc;
parameters.active = rtcParameters.active;
if (rtcParameters.max_bitrate_bps)
parameters.maxBitrate = *rtcParameters.max_bitrate_bps;
if (rtcParameters.max_framerate)
parameters.maxFramerate = *rtcParameters.max_framerate;
parameters.rid = fromStdString(rtcParameters.rid);
if (rtcParameters.scale_resolution_down_by)
parameters.scaleResolutionDownBy = *rtcParameters.scale_resolution_down_by;
parameters.priority = fromWebRTCBitRatePriority(rtcParameters.bitrate_priority);
parameters.networkPriority = toRTCPriorityType(rtcParameters.network_priority);
return parameters;
}
static inline webrtc::RtpEncodingParameters fromRTCEncodingParameters(const RTCRtpEncodingParameters& parameters)
{
webrtc::RtpEncodingParameters rtcParameters;
if (parameters.ssrc)
rtcParameters.ssrc = parameters.ssrc;
rtcParameters.active = parameters.active;
if (parameters.maxBitrate)
rtcParameters.max_bitrate_bps = parameters.maxBitrate;
if (parameters.maxFramerate)
rtcParameters.max_framerate = parameters.maxFramerate;
rtcParameters.rid = parameters.rid.utf8().data();
if (parameters.scaleResolutionDownBy)
rtcParameters.scale_resolution_down_by = parameters.scaleResolutionDownBy;
rtcParameters.bitrate_priority = toWebRTCBitRatePriority(parameters.priority);
if (parameters.networkPriority)
rtcParameters.network_priority = fromRTCPriorityType(*parameters.networkPriority);
return rtcParameters;
}
static inline RTCRtpHeaderExtensionParameters toRTCHeaderExtensionParameters(const webrtc::RtpExtension& rtcParameters)
{
RTCRtpHeaderExtensionParameters parameters;
parameters.uri = fromStdString(rtcParameters.uri);
parameters.id = rtcParameters.id;
return parameters;
}
static inline webrtc::RtpExtension fromRTCHeaderExtensionParameters(const RTCRtpHeaderExtensionParameters& parameters)
{
webrtc::RtpExtension rtcParameters;
rtcParameters.uri = parameters.uri.utf8().data();
rtcParameters.id = parameters.id;
return rtcParameters;
}
static inline RTCRtpCodecParameters toRTCCodecParameters(const webrtc::RtpCodecParameters& rtcParameters)
{
RTCRtpCodecParameters parameters;
parameters.payloadType = rtcParameters.payload_type;
parameters.mimeType = fromStdString(rtcParameters.mime_type());
if (rtcParameters.clock_rate)
parameters.clockRate = *rtcParameters.clock_rate;
if (rtcParameters.num_channels)
parameters.channels = *rtcParameters.num_channels;
StringBuilder sdpFmtpLineBuilder;
sdpFmtpLineBuilder.append("a=fmtp:", parameters.payloadType, ' ');
bool isFirst = true;
for (auto& keyValue : rtcParameters.parameters) {
if (!isFirst)
sdpFmtpLineBuilder.append(';');
else
isFirst = false;
sdpFmtpLineBuilder.append(keyValue.first.c_str(), '=', keyValue.second.c_str());
}
parameters.sdpFmtpLine = sdpFmtpLineBuilder.toString();
return parameters;
}
RTCRtpParameters toRTCRtpParameters(const webrtc::RtpParameters& rtcParameters)
{
RTCRtpParameters parameters;
for (auto& extension : rtcParameters.header_extensions)
parameters.headerExtensions.append(toRTCHeaderExtensionParameters(extension));
for (auto& codec : rtcParameters.codecs)
parameters.codecs.append(toRTCCodecParameters(codec));
parameters.rtcp.reducedSize = rtcParameters.rtcp.reduced_size;
if (rtcParameters.rtcp.cname.length())
parameters.rtcp.cname = fromStdString(rtcParameters.rtcp.cname);
return parameters;
}
RTCRtpSendParameters toRTCRtpSendParameters(const webrtc::RtpParameters& rtcParameters)
{
RTCRtpSendParameters parameters { toRTCRtpParameters(rtcParameters) };
parameters.transactionId = fromStdString(rtcParameters.transaction_id);
for (auto& rtcEncoding : rtcParameters.encodings)
parameters.encodings.append(toRTCEncodingParameters(rtcEncoding));
if (rtcParameters.degradation_preference) {
switch (*rtcParameters.degradation_preference) {
// FIXME: Support DegradationPreference::DISABLED.
case webrtc::DegradationPreference::DISABLED:
case webrtc::DegradationPreference::MAINTAIN_FRAMERATE:
parameters.degradationPreference = RTCDegradationPreference::MaintainFramerate;
break;
case webrtc::DegradationPreference::MAINTAIN_RESOLUTION:
parameters.degradationPreference = RTCDegradationPreference::MaintainResolution;
break;
case webrtc::DegradationPreference::BALANCED:
parameters.degradationPreference = RTCDegradationPreference::Balanced;
break;
};
}
return parameters;
}
void updateRTCRtpSendParameters(const RTCRtpSendParameters& parameters, webrtc::RtpParameters& rtcParameters)
{
rtcParameters.transaction_id = parameters.transactionId.utf8().data();
if (parameters.encodings.size() != rtcParameters.encodings.size()) {
// If encodings size is different, setting parameters will fail. Let's make it so.
rtcParameters.encodings.clear();
return;
}
// We copy all current encodings parameters and only update parameters that can actually be usefully updated.
for (size_t i = 0; i < parameters.encodings.size(); ++i) {
rtcParameters.encodings[i].active = parameters.encodings[i].active;
if (parameters.encodings[i].maxBitrate)
rtcParameters.encodings[i].max_bitrate_bps = parameters.encodings[i].maxBitrate;
if (parameters.encodings[i].maxFramerate)
rtcParameters.encodings[i].max_framerate = parameters.encodings[i].maxFramerate;
if (parameters.encodings[i].scaleResolutionDownBy)
rtcParameters.encodings[i].scale_resolution_down_by = parameters.encodings[i].scaleResolutionDownBy;
rtcParameters.encodings[i].bitrate_priority = toWebRTCBitRatePriority(parameters.encodings[i].priority);
if (parameters.encodings[i].networkPriority)
rtcParameters.encodings[i].network_priority = fromRTCPriorityType(*parameters.encodings[i].networkPriority);
}
rtcParameters.header_extensions.clear();
for (auto& extension : parameters.headerExtensions)
rtcParameters.header_extensions.push_back(fromRTCHeaderExtensionParameters(extension));
// Codecs parameters are readonly
switch (parameters.degradationPreference) {
case RTCDegradationPreference::MaintainFramerate:
rtcParameters.degradation_preference = webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
break;
case RTCDegradationPreference::MaintainResolution:
rtcParameters.degradation_preference = webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
break;
case RTCDegradationPreference::Balanced:
rtcParameters.degradation_preference = webrtc::DegradationPreference::BALANCED;
break;
}
if (parameters.rtcp.reducedSize)
rtcParameters.rtcp.reduced_size = *parameters.rtcp.reducedSize;
if (!parameters.rtcp.cname.isNull())
rtcParameters.rtcp.cname = parameters.rtcp.cname.utf8().data();
}
RTCRtpTransceiverDirection toRTCRtpTransceiverDirection(webrtc::RtpTransceiverDirection rtcDirection)
{
switch (rtcDirection) {
case webrtc::RtpTransceiverDirection::kSendRecv:
return RTCRtpTransceiverDirection::Sendrecv;
case webrtc::RtpTransceiverDirection::kSendOnly:
return RTCRtpTransceiverDirection::Sendonly;
case webrtc::RtpTransceiverDirection::kRecvOnly:
return RTCRtpTransceiverDirection::Recvonly;
case webrtc::RtpTransceiverDirection::kInactive:
case webrtc::RtpTransceiverDirection::kStopped:
return RTCRtpTransceiverDirection::Inactive;
};
RELEASE_ASSERT_NOT_REACHED();
}
webrtc::RtpTransceiverDirection fromRTCRtpTransceiverDirection(RTCRtpTransceiverDirection direction)
{
switch (direction) {
case RTCRtpTransceiverDirection::Sendrecv:
return webrtc::RtpTransceiverDirection::kSendRecv;
case RTCRtpTransceiverDirection::Sendonly:
return webrtc::RtpTransceiverDirection::kSendOnly;
case RTCRtpTransceiverDirection::Recvonly:
return webrtc::RtpTransceiverDirection::kRecvOnly;
case RTCRtpTransceiverDirection::Inactive:
return webrtc::RtpTransceiverDirection::kInactive;
};
RELEASE_ASSERT_NOT_REACHED();
}
webrtc::RtpTransceiverInit fromRtpTransceiverInit(const RTCRtpTransceiverInit& init, cricket::MediaType type)
{
webrtc::RtpTransceiverInit rtcInit;
rtcInit.direction = fromRTCRtpTransceiverDirection(init.direction);
for (auto& stream : init.streams)
rtcInit.stream_ids.push_back(stream->id().utf8().data());
if (type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
if (!init.sendEncodings.isEmpty())
rtcInit.send_encodings.push_back(fromRTCEncodingParameters(init.sendEncodings[0]));
} else {
for (auto& encoding : init.sendEncodings)
rtcInit.send_encodings.push_back(fromRTCEncodingParameters(encoding));
}
return rtcInit;
}
ExceptionCode toExceptionCode(webrtc::RTCErrorType type)
{
switch (type) {
case webrtc::RTCErrorType::INVALID_PARAMETER:
return InvalidAccessError;
case webrtc::RTCErrorType::INVALID_RANGE:
return RangeError;
case webrtc::RTCErrorType::SYNTAX_ERROR:
return SyntaxError;
case webrtc::RTCErrorType::INVALID_STATE:
return InvalidStateError;
case webrtc::RTCErrorType::INVALID_MODIFICATION:
return InvalidModificationError;
case webrtc::RTCErrorType::NETWORK_ERROR:
return NetworkError;
default:
return OperationError;
}
}
Exception toException(const webrtc::RTCError& error)
{
ASSERT(!error.ok());
return Exception { toExceptionCode(error.type()), error.message() };
}
static inline RTCIceComponent toRTCIceComponent(int component)
{
return component == cricket::ICE_CANDIDATE_COMPONENT_RTP ? RTCIceComponent::Rtp : RTCIceComponent::Rtcp;
}
static inline std::optional<RTCIceProtocol> toRTCIceProtocol(const std::string& protocol)
{
if (protocol == "")
return { };
if (protocol == "udp")
return RTCIceProtocol::Udp;
ASSERT(protocol == "tcp" || protocol == "ssltcp");
return RTCIceProtocol::Tcp;
}
static inline std::optional<RTCIceTcpCandidateType> toRTCIceTcpCandidateType(const std::string& type)
{
if (type == "")
return { };
if (type == "active")
return RTCIceTcpCandidateType::Active;
if (type == "passive")
return RTCIceTcpCandidateType::Passive;
ASSERT(type == "so");
return RTCIceTcpCandidateType::So;
}
static inline std::optional<RTCIceCandidateType> toRTCIceCandidateType(const std::string& type)
{
if (type == "")
return { };
if (type == "local")
return RTCIceCandidateType::Host;
if (type == "stun")
return RTCIceCandidateType::Srflx;
if (type == "prflx")
return RTCIceCandidateType::Prflx;
ASSERT(type == "relay");
return RTCIceCandidateType::Relay;
}
std::optional<RTCIceCandidate::Fields> parseIceCandidateSDP(const String& sdp)
{
cricket::Candidate candidate;
if (!webrtc::ParseCandidate(sdp.utf8().data(), &candidate, nullptr, true))
return { };
RTCIceCandidate::Fields fields;
fields.foundation = fromStdString(candidate.foundation());
fields.component = toRTCIceComponent(candidate.component());
fields.priority = candidate.priority();
fields.protocol = toRTCIceProtocol(candidate.protocol());
if (!candidate.address().IsNil()) {
fields.address = fromStdString(candidate.address().HostAsURIString());
fields.port = candidate.address().port();
}
fields.type = toRTCIceCandidateType(candidate.type());
fields.tcpType = toRTCIceTcpCandidateType(candidate.tcptype());
if (!candidate.related_address().IsNil()) {
fields.relatedAddress = fromStdString(candidate.related_address().HostAsURIString());
fields.relatedPort = candidate.related_address().port();
}
fields.usernameFragment = fromStdString(candidate.username());
return fields;
}
static std::optional<RTCErrorDetailType> toRTCErrorDetailType(webrtc::RTCErrorDetailType type)
{
switch (type) {
case webrtc::RTCErrorDetailType::DATA_CHANNEL_FAILURE:
return RTCErrorDetailType::DataChannelFailure;
case webrtc::RTCErrorDetailType::DTLS_FAILURE:
return RTCErrorDetailType::DtlsFailure;
case webrtc::RTCErrorDetailType::FINGERPRINT_FAILURE:
return RTCErrorDetailType::FingerprintFailure;
case webrtc::RTCErrorDetailType::SCTP_FAILURE:
return RTCErrorDetailType::SctpFailure;
case webrtc::RTCErrorDetailType::SDP_SYNTAX_ERROR:
return RTCErrorDetailType::SdpSyntaxError;
case webrtc::RTCErrorDetailType::HARDWARE_ENCODER_NOT_AVAILABLE:
case webrtc::RTCErrorDetailType::HARDWARE_ENCODER_ERROR:
case webrtc::RTCErrorDetailType::NONE:
return { };
};
}
RefPtr<RTCError> toRTCError(const webrtc::RTCError& rtcError)
{
auto detail = toRTCErrorDetailType(rtcError.error_detail());
if (!detail)
return nullptr;
return RTCError::create(*detail, rtcError.message());
}
} // namespace WebCore
#endif // USE(LIBWEBRTC)