blob: 0cd0ea1137e831daf193cf603a41c04a00a9262f [file] [log] [blame]
// Copyright 2017 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 "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_capabilities.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h"
#include "third_party/blink/renderer/modules/peerconnection/web_rtc_stats_report_callback_resolver.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
namespace {
class ReplaceTrackRequest : public RTCVoidRequest {
public:
ReplaceTrackRequest(RTCRtpSender* sender,
MediaStreamTrack* with_track,
ScriptPromiseResolver* resolver)
: sender_(sender), with_track_(with_track), resolver_(resolver) {}
~ReplaceTrackRequest() override {}
void RequestSucceeded() override {
sender_->SetTrack(with_track_);
resolver_->Resolve();
}
void RequestFailed(const webrtc::RTCError& error) override {
ScriptState::Scope scope(resolver_->GetScriptState());
ExceptionState exception_state(resolver_->GetScriptState()->GetIsolate(),
ExceptionState::kExecutionContext,
"RTCRtpSender", "replaceTrack");
ThrowExceptionFromRTCError(error, exception_state);
resolver_->Reject(exception_state);
}
void Trace(blink::Visitor* visitor) override {
visitor->Trace(sender_);
visitor->Trace(with_track_);
visitor->Trace(resolver_);
RTCVoidRequest::Trace(visitor);
}
private:
Member<RTCRtpSender> sender_;
Member<MediaStreamTrack> with_track_;
Member<ScriptPromiseResolver> resolver_;
};
class SetParametersRequest : public RTCVoidRequestScriptPromiseResolverImpl {
public:
SetParametersRequest(ScriptPromiseResolver* resolver, RTCRtpSender* sender)
: RTCVoidRequestScriptPromiseResolverImpl(resolver,
"RTCRtpSender",
"setParameters"),
sender_(sender) {}
void RequestSucceeded() override {
sender_->ClearLastReturnedParameters();
RTCVoidRequestScriptPromiseResolverImpl::RequestSucceeded();
}
void RequestFailed(const webrtc::RTCError& error) override {
sender_->ClearLastReturnedParameters();
RTCVoidRequestScriptPromiseResolverImpl::RequestFailed(error);
}
void Trace(blink::Visitor* visitor) override {
visitor->Trace(sender_);
RTCVoidRequestScriptPromiseResolverImpl::Trace(visitor);
}
private:
Member<RTCRtpSender> sender_;
};
bool HasInvalidModification(const RTCRtpSendParameters* parameters,
const RTCRtpSendParameters* new_parameters) {
if (parameters->hasTransactionId() != new_parameters->hasTransactionId() ||
(parameters->hasTransactionId() &&
parameters->transactionId() != new_parameters->transactionId())) {
return true;
}
if (parameters->hasEncodings() != new_parameters->hasEncodings())
return true;
if (parameters->hasEncodings()) {
if (parameters->encodings().size() != new_parameters->encodings().size())
return true;
}
if (parameters->hasHeaderExtensions() !=
new_parameters->hasHeaderExtensions())
return true;
if (parameters->hasHeaderExtensions()) {
if (parameters->headerExtensions().size() !=
new_parameters->headerExtensions().size())
return true;
for (wtf_size_t i = 0; i < parameters->headerExtensions().size(); ++i) {
const auto& header_extension = parameters->headerExtensions()[i];
const auto& new_header_extension = new_parameters->headerExtensions()[i];
if (header_extension->hasUri() != new_header_extension->hasUri() ||
(header_extension->hasUri() &&
header_extension->uri() != new_header_extension->uri()) ||
header_extension->hasId() != new_header_extension->hasId() ||
(header_extension->hasId() &&
header_extension->id() != new_header_extension->id()) ||
header_extension->hasEncrypted() !=
new_header_extension->hasEncrypted() ||
(header_extension->hasEncrypted() &&
header_extension->encrypted() !=
new_header_extension->encrypted())) {
return true;
}
}
}
if (parameters->hasRtcp() != new_parameters->hasRtcp() ||
(parameters->hasRtcp() &&
((parameters->rtcp()->hasCname() != new_parameters->rtcp()->hasCname() ||
(parameters->rtcp()->hasCname() &&
parameters->rtcp()->cname() != new_parameters->rtcp()->cname())) ||
(parameters->rtcp()->hasReducedSize() !=
new_parameters->rtcp()->hasReducedSize() ||
(parameters->rtcp()->hasReducedSize() &&
parameters->rtcp()->reducedSize() !=
new_parameters->rtcp()->reducedSize()))))) {
return true;
}
if (parameters->hasCodecs() != new_parameters->hasCodecs())
return true;
if (parameters->hasCodecs()) {
if (parameters->codecs().size() != new_parameters->codecs().size())
return true;
for (wtf_size_t i = 0; i < parameters->codecs().size(); ++i) {
const auto& codec = parameters->codecs()[i];
const auto& new_codec = new_parameters->codecs()[i];
if (codec->hasPayloadType() != new_codec->hasPayloadType() ||
(codec->hasPayloadType() &&
codec->payloadType() != new_codec->payloadType()) ||
codec->hasMimeType() != new_codec->hasMimeType() ||
(codec->hasMimeType() &&
codec->mimeType() != new_codec->mimeType()) ||
codec->hasClockRate() != new_codec->hasClockRate() ||
(codec->hasClockRate() &&
codec->clockRate() != new_codec->clockRate()) ||
codec->hasChannels() != new_codec->hasChannels() ||
(codec->hasChannels() &&
codec->channels() != new_codec->channels()) ||
codec->hasSdpFmtpLine() != new_codec->hasSdpFmtpLine() ||
(codec->hasSdpFmtpLine() &&
codec->sdpFmtpLine() != new_codec->sdpFmtpLine())) {
return true;
}
}
}
return false;
}
// Relative weights for each priority as defined in RTCWEB-DATA
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel
const double kPriorityWeightVeryLow = 0.5;
const double kPriorityWeightLow = 1;
const double kPriorityWeightMedium = 2;
const double kPriorityWeightHigh = 4;
std::string PriorityFromDouble(double priority) {
// Find the middle point between 2 priority weights to match them to a
// WebRTC priority
const double very_low_upper_bound =
(kPriorityWeightVeryLow + kPriorityWeightLow) / 2;
const double low_upper_bound =
(kPriorityWeightLow + kPriorityWeightMedium) / 2;
const double medium_upper_bound =
(kPriorityWeightMedium + kPriorityWeightHigh) / 2;
if (priority < webrtc::kDefaultBitratePriority * very_low_upper_bound) {
return "very-low";
}
if (priority < webrtc::kDefaultBitratePriority * low_upper_bound) {
return "low";
}
if (priority < webrtc::kDefaultBitratePriority * medium_upper_bound) {
return "medium";
}
return "high";
}
double PriorityToDouble(const WTF::String& priority) {
double result = 1;
if (priority == "very-low") {
result = webrtc::kDefaultBitratePriority * kPriorityWeightVeryLow;
} else if (priority == "low") {
result = webrtc::kDefaultBitratePriority * kPriorityWeightLow;
} else if (priority == "medium") {
result = webrtc::kDefaultBitratePriority * kPriorityWeightMedium;
} else if (priority == "high") {
result = webrtc::kDefaultBitratePriority * kPriorityWeightHigh;
} else {
NOTREACHED();
}
return result;
}
std::tuple<std::vector<webrtc::RtpEncodingParameters>,
webrtc::DegradationPreference>
ToRtpParameters(const RTCRtpSendParameters* parameters) {
std::vector<webrtc::RtpEncodingParameters> encodings;
if (parameters->hasEncodings()) {
encodings.reserve(parameters->encodings().size());
for (const auto& encoding : parameters->encodings()) {
encodings.push_back(ToRtpEncodingParameters(encoding));
}
}
webrtc::DegradationPreference degradation_preference =
webrtc::DegradationPreference::BALANCED;
return std::make_tuple(encodings, degradation_preference);
}
} // namespace
webrtc::RtpEncodingParameters ToRtpEncodingParameters(
const RTCRtpEncodingParameters* encoding) {
// TODO(orphis): Forward missing fields from the WebRTC library:
// codecPayloadType, dtx, ptime, maxFramerate, scaleResolutionDownBy,
// rid
webrtc::RtpEncodingParameters webrtc_encoding;
webrtc_encoding.active = encoding->active();
webrtc_encoding.bitrate_priority = PriorityToDouble(encoding->priority());
if (encoding->hasMaxBitrate())
webrtc_encoding.max_bitrate_bps = clampTo<int>(encoding->maxBitrate());
return webrtc_encoding;
}
RTCRtpSender::RTCRtpSender(RTCPeerConnection* pc,
std::unique_ptr<WebRTCRtpSender> sender,
String kind,
MediaStreamTrack* track,
MediaStreamVector streams)
: pc_(pc),
sender_(std::move(sender)),
kind_(std::move(kind)),
track_(track),
streams_(std::move(streams)) {
DCHECK(pc_);
DCHECK(sender_);
DCHECK(!track || kind_ == track->kind());
}
MediaStreamTrack* RTCRtpSender::track() {
return track_;
}
ScriptPromise RTCRtpSender::replaceTrack(ScriptState* script_state,
MediaStreamTrack* with_track) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
if (pc_->IsClosed()) {
resolver->Reject(DOMException::Create(DOMExceptionCode::kInvalidStateError,
"The peer connection is closed."));
return promise;
}
WebMediaStreamTrack web_track;
if (with_track) {
pc_->RegisterTrack(with_track);
web_track = with_track->Component();
}
ReplaceTrackRequest* request =
new ReplaceTrackRequest(this, with_track, resolver);
sender_->ReplaceTrack(web_track, request);
return promise;
}
RTCRtpSendParameters* RTCRtpSender::getParameters() {
RTCRtpSendParameters* parameters = RTCRtpSendParameters::Create();
// TODO(orphis): Forward missing fields from the WebRTC library:
// degradationPreference
std::unique_ptr<webrtc::RtpParameters> webrtc_parameters =
sender_->GetParameters();
parameters->setTransactionId(webrtc_parameters->transaction_id.c_str());
RTCRtcpParameters* rtcp = RTCRtcpParameters::Create();
rtcp->setCname(webrtc_parameters->rtcp.cname.c_str());
rtcp->setReducedSize(webrtc_parameters->rtcp.reduced_size);
parameters->setRtcp(rtcp);
HeapVector<Member<RTCRtpEncodingParameters>> encodings;
encodings.ReserveCapacity(
SafeCast<wtf_size_t>(webrtc_parameters->encodings.size()));
for (const auto& web_encoding : webrtc_parameters->encodings) {
// TODO(orphis): Forward missing fields from the WebRTC library:
// codecPayloadType, dtx, ptime, maxFramerate, scaleResolutionDownBy, rid
RTCRtpEncodingParameters* encoding = RTCRtpEncodingParameters::Create();
encoding->setActive(web_encoding.active);
if (web_encoding.max_bitrate_bps)
encoding->setMaxBitrate(web_encoding.max_bitrate_bps.value());
encoding->setPriority(
PriorityFromDouble(web_encoding.bitrate_priority).c_str());
encodings.push_back(encoding);
}
parameters->setEncodings(encodings);
HeapVector<Member<RTCRtpHeaderExtensionParameters>> headers;
headers.ReserveCapacity(
SafeCast<wtf_size_t>(webrtc_parameters->header_extensions.size()));
for (const auto& web_header : webrtc_parameters->header_extensions) {
auto* header = RTCRtpHeaderExtensionParameters::Create();
header->setUri(web_header.uri.c_str());
header->setId(web_header.id);
header->setEncrypted(web_header.encrypt);
headers.push_back(header);
}
parameters->setHeaderExtensions(headers);
HeapVector<Member<RTCRtpCodecParameters>> codecs;
codecs.ReserveCapacity(
SafeCast<wtf_size_t>(webrtc_parameters->codecs.size()));
for (const auto& web_codec : webrtc_parameters->codecs) {
auto* codec = RTCRtpCodecParameters::Create();
codec->setPayloadType(web_codec.payload_type);
codec->setMimeType(WTF::String::FromUTF8(web_codec.mime_type().c_str()));
if (web_codec.clock_rate)
codec->setClockRate(web_codec.clock_rate.value());
if (web_codec.num_channels)
codec->setChannels(web_codec.num_channels.value());
if (web_codec.parameters.size()) {
std::string sdp_fmtp_line;
for (const auto& parameter : web_codec.parameters) {
if (sdp_fmtp_line.size())
sdp_fmtp_line += ";";
sdp_fmtp_line += parameter.first + "=" + parameter.second;
}
codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
}
codecs.push_back(codec);
}
parameters->setCodecs(codecs);
last_returned_parameters_ = parameters;
return parameters;
}
ScriptPromise RTCRtpSender::setParameters(
ScriptState* script_state,
const RTCRtpSendParameters* parameters) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
if (!last_returned_parameters_) {
resolver->Reject(DOMException::Create(
DOMExceptionCode::kInvalidStateError,
"getParameters() needs to be called before setParameters()."));
return promise;
}
// The specification mentions that some fields in the dictionary should not
// be modified. Some of those checks are done in the lower WebRTC layer, but
// there is no perfect 1-1 mapping between the Javascript layer and native.
// So we save the last returned dictionary and enforce the check at this
// level instead.
if (HasInvalidModification(last_returned_parameters_, parameters)) {
resolver->Reject(
DOMException::Create(DOMExceptionCode::kInvalidModificationError,
"Read-only field modified in setParameters()."));
return promise;
}
// The only values that can be set by setParameters are in the encodings
// field and the degradationPreference field. We just forward those to the
// native layer without having to transform all the other read-only
// parameters.
std::vector<webrtc::RtpEncodingParameters> encodings;
webrtc::DegradationPreference degradation_preference;
std::tie(encodings, degradation_preference) = ToRtpParameters(parameters);
auto* request = new SetParametersRequest(resolver, this);
sender_->SetParameters(std::move(encodings), degradation_preference, request);
return promise;
}
void RTCRtpSender::ClearLastReturnedParameters() {
last_returned_parameters_ = nullptr;
}
ScriptPromise RTCRtpSender::getStats(ScriptState* script_state) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
sender_->GetStats(WebRTCStatsReportCallbackResolver::Create(resolver),
GetRTCStatsFilter(script_state));
return promise;
}
WebRTCRtpSender* RTCRtpSender::web_sender() {
return sender_.get();
}
void RTCRtpSender::SetTrack(MediaStreamTrack* track) {
track_ = track;
if (track) {
if (kind_.IsNull()) {
kind_ = track->kind();
} else if (kind_ != track->kind()) {
LOG(ERROR) << "Trying to set track to a different kind: Old " << kind_
<< " new " << track->kind();
NOTREACHED();
}
}
}
MediaStreamVector RTCRtpSender::streams() const {
return streams_;
}
void RTCRtpSender::set_streams(MediaStreamVector streams) {
streams_ = std::move(streams);
}
RTCDTMFSender* RTCRtpSender::dtmf() {
// Lazy initialization of dtmf_ to avoid overhead when not used.
if (!dtmf_ && kind_ == "audio") {
auto handler = sender_->GetDtmfSender();
if (!handler) {
LOG(ERROR) << "Unable to create DTMF sender attribute on an audio sender";
return nullptr;
}
dtmf_ =
RTCDTMFSender::Create(pc_->GetExecutionContext(), std::move(handler));
}
return dtmf_;
}
void RTCRtpSender::Trace(blink::Visitor* visitor) {
visitor->Trace(pc_);
visitor->Trace(track_);
visitor->Trace(dtmf_);
visitor->Trace(streams_);
visitor->Trace(last_returned_parameters_);
ScriptWrappable::Trace(visitor);
}
RTCRtpCapabilities* RTCRtpSender::getCapabilities(const String& kind) {
if (kind != "audio" && kind != "video")
return nullptr;
RTCRtpCapabilities* capabilities = RTCRtpCapabilities::Create();
capabilities->setCodecs(HeapVector<Member<RTCRtpCodecCapability>>());
capabilities->setHeaderExtensions(
HeapVector<Member<RTCRtpHeaderExtensionCapability>>());
std::unique_ptr<webrtc::RtpCapabilities> rtc_capabilities =
blink::Platform::Current()->GetRtpSenderCapabilities(kind);
HeapVector<Member<RTCRtpCodecCapability>> codecs;
codecs.ReserveInitialCapacity(
SafeCast<wtf_size_t>(rtc_capabilities->codecs.size()));
for (const auto& rtc_codec : rtc_capabilities->codecs) {
auto* codec = RTCRtpCodecCapability::Create();
codec->setMimeType(WTF::String::FromUTF8(rtc_codec.mime_type().c_str()));
if (rtc_codec.clock_rate)
codec->setClockRate(rtc_codec.clock_rate.value());
if (rtc_codec.num_channels)
codec->setChannels(rtc_codec.num_channels.value());
if (rtc_codec.parameters.size()) {
std::string sdp_fmtp_line;
for (const auto& parameter : rtc_codec.parameters) {
if (sdp_fmtp_line.size())
sdp_fmtp_line += ";";
sdp_fmtp_line += parameter.first + "=" + parameter.second;
}
codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
}
codecs.push_back(codec);
}
capabilities->setCodecs(codecs);
HeapVector<Member<RTCRtpHeaderExtensionCapability>> header_extensions;
header_extensions.ReserveInitialCapacity(
SafeCast<wtf_size_t>(rtc_capabilities->header_extensions.size()));
for (const auto& rtc_header_extension : rtc_capabilities->header_extensions) {
auto* header_extension = RTCRtpHeaderExtensionCapability::Create();
header_extension->setUri(
WTF::String::FromUTF8(rtc_header_extension.uri.c_str()));
header_extensions.push_back(header_extension);
}
capabilities->setHeaderExtensions(header_extensions);
return capabilities;
}
} // namespace blink