blob: c54b671bfda084bc3d1f56b342a344298877435d [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 <memory>
#include <string>
#include <tuple>
#include <utility>
#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_insertable_streams.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtcp_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_capabilities.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_codec_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_capability.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_parameters.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/streams/readable_stream.h"
#include "third_party/blink/renderer/core/streams/writable_stream.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
#include "third_party/blink/renderer/modules/peerconnection/identifiability_metrics.h"
#include "third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_source.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_source.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_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/heap/heap.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.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(Visitor* visitor) const 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(Visitor* visitor) const 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;
for (wtf_size_t i = 0; i < parameters->encodings().size(); ++i) {
const auto& encoding = parameters->encodings()[i];
const auto& new_encoding = new_parameters->encodings()[i];
if (encoding->hasRid() != new_encoding->hasRid() ||
(encoding->hasRid() && encoding->rid() != new_encoding->rid())) {
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::string PriorityFromEnum(webrtc::Priority priority) {
switch (priority) {
case webrtc::Priority::kVeryLow:
return "very-low";
case webrtc::Priority::kLow:
return "low";
case webrtc::Priority::kMedium:
return "medium";
case webrtc::Priority::kHigh:
return "high";
}
}
webrtc::Priority PriorityToEnum(const WTF::String& priority) {
webrtc::Priority result = webrtc::Priority::kLow;
if (priority == "very-low") {
result = webrtc::Priority::kVeryLow;
} else if (priority == "low") {
result = webrtc::Priority::kLow;
} else if (priority == "medium") {
result = webrtc::Priority::kMedium;
} else if (priority == "high") {
result = webrtc::Priority::kHigh;
} else {
NOTREACHED();
}
return result;
}
std::tuple<Vector<webrtc::RtpEncodingParameters>,
absl::optional<webrtc::DegradationPreference>>
ToRtpParameters(ExecutionContext* context,
const RTCRtpSendParameters* parameters) {
Vector<webrtc::RtpEncodingParameters> encodings;
if (parameters->hasEncodings()) {
encodings.ReserveCapacity(parameters->encodings().size());
for (const auto& encoding : parameters->encodings()) {
encodings.push_back(ToRtpEncodingParameters(context, encoding));
}
}
absl::optional<webrtc::DegradationPreference> degradation_preference;
if (parameters->hasDegradationPreference()) {
if (parameters->degradationPreference() == "balanced") {
degradation_preference = webrtc::DegradationPreference::BALANCED;
} else if (parameters->degradationPreference() == "maintain-framerate") {
degradation_preference =
webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
} else if (parameters->degradationPreference() == "maintain-resolution") {
degradation_preference =
webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
} else {
NOTREACHED();
}
}
return std::make_tuple(encodings, degradation_preference);
}
} // namespace
webrtc::RtpEncodingParameters ToRtpEncodingParameters(
ExecutionContext* context,
const RTCRtpEncodingParameters* encoding) {
webrtc::RtpEncodingParameters webrtc_encoding;
if (encoding->hasRid()) {
webrtc_encoding.rid = encoding->rid().Utf8();
}
webrtc_encoding.active = encoding->active();
webrtc_encoding.bitrate_priority = PriorityToDouble(encoding->priority());
webrtc_encoding.network_priority =
PriorityToEnum(encoding->networkPriority());
if (encoding->hasMaxBitrate()) {
webrtc_encoding.max_bitrate_bps = clampTo<int>(encoding->maxBitrate());
}
if (encoding->hasScaleResolutionDownBy()) {
webrtc_encoding.scale_resolution_down_by =
encoding->scaleResolutionDownBy();
}
if (encoding->hasMaxFramerate()) {
webrtc_encoding.max_framerate = encoding->maxFramerate();
}
// https://w3c.github.io/webrtc-svc/
if (encoding->hasScalabilityMode()) {
if (encoding->scalabilityMode() == "L1T2") {
webrtc_encoding.num_temporal_layers = 2;
} else if (encoding->scalabilityMode() == "L1T3") {
webrtc_encoding.num_temporal_layers = 3;
}
}
if (encoding->adaptivePtime()) {
UseCounter::Count(context, WebFeature::kRTCAdaptivePtime);
}
webrtc_encoding.adaptive_ptime = encoding->adaptivePtime();
return webrtc_encoding;
}
RTCRtpHeaderExtensionParameters* ToRtpHeaderExtensionParameters(
const webrtc::RtpExtension& webrtc_header) {
RTCRtpHeaderExtensionParameters* header =
RTCRtpHeaderExtensionParameters::Create();
header->setUri(webrtc_header.uri.c_str());
header->setId(webrtc_header.id);
header->setEncrypted(webrtc_header.encrypt);
return header;
}
RTCRtpCodecParameters* ToRtpCodecParameters(
const webrtc::RtpCodecParameters& webrtc_codec) {
RTCRtpCodecParameters* codec = RTCRtpCodecParameters::Create();
codec->setPayloadType(webrtc_codec.payload_type);
codec->setMimeType(WTF::String::FromUTF8(webrtc_codec.mime_type()));
if (webrtc_codec.clock_rate)
codec->setClockRate(webrtc_codec.clock_rate.value());
if (webrtc_codec.num_channels)
codec->setChannels(webrtc_codec.num_channels.value());
if (!webrtc_codec.parameters.empty()) {
std::string sdp_fmtp_line;
for (const auto& parameter : webrtc_codec.parameters) {
if (!sdp_fmtp_line.empty())
sdp_fmtp_line += ";";
sdp_fmtp_line += parameter.first + "=" + parameter.second;
}
codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
}
return codec;
}
RTCRtpSender::RTCRtpSender(RTCPeerConnection* pc,
std::unique_ptr<RTCRtpSenderPlatform> sender,
String kind,
MediaStreamTrack* track,
MediaStreamVector streams,
bool force_encoded_audio_insertable_streams,
bool force_encoded_video_insertable_streams)
: pc_(pc),
sender_(std::move(sender)),
kind_(std::move(kind)),
track_(track),
streams_(std::move(streams)),
force_encoded_audio_insertable_streams_(
force_encoded_audio_insertable_streams && kind_ == "audio"),
force_encoded_video_insertable_streams_(
force_encoded_video_insertable_streams && kind_ == "video") {
DCHECK(pc_);
DCHECK(sender_);
DCHECK(!track || kind_ == track->kind());
DCHECK(!force_encoded_audio_insertable_streams_ ||
!force_encoded_video_insertable_streams_);
if (force_encoded_audio_insertable_streams_)
RegisterEncodedAudioStreamCallback();
if (force_encoded_video_insertable_streams_)
RegisterEncodedVideoStreamCallback();
}
MediaStreamTrack* RTCRtpSender::track() {
return track_;
}
RTCDtlsTransport* RTCRtpSender::transport() {
return transport_;
}
RTCDtlsTransport* RTCRtpSender::rtcpTransport() {
// Chrome does not support turning off RTCP-mux.
return nullptr;
}
ScriptPromise RTCRtpSender::replaceTrack(ScriptState* script_state,
MediaStreamTrack* with_track) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
if (pc_->IsClosed()) {
resolver->Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError,
"The peer connection is closed."));
return promise;
}
MediaStreamComponent* component = nullptr;
if (with_track) {
pc_->RegisterTrack(with_track);
component = with_track->Component();
}
ReplaceTrackRequest* request =
MakeGarbageCollected<ReplaceTrackRequest>(this, with_track, resolver);
sender_->ReplaceTrack(component, request);
return promise;
}
RTCRtpSendParameters* RTCRtpSender::getParameters() {
RTCRtpSendParameters* parameters = RTCRtpSendParameters::Create();
std::unique_ptr<webrtc::RtpParameters> webrtc_parameters =
sender_->GetParameters();
parameters->setTransactionId(webrtc_parameters->transaction_id.c_str());
if (webrtc_parameters->degradation_preference.has_value()) {
WTF::String degradation_preference_str;
switch (webrtc_parameters->degradation_preference.value()) {
case webrtc::DegradationPreference::MAINTAIN_FRAMERATE:
degradation_preference_str = "maintain-framerate";
break;
case webrtc::DegradationPreference::MAINTAIN_RESOLUTION:
degradation_preference_str = "maintain-resolution";
break;
case webrtc::DegradationPreference::BALANCED:
degradation_preference_str = "balanced";
break;
default:
NOTREACHED();
}
parameters->setDegradationPreference(degradation_preference_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& webrtc_encoding : webrtc_parameters->encodings) {
RTCRtpEncodingParameters* encoding = RTCRtpEncodingParameters::Create();
if (!webrtc_encoding.rid.empty()) {
encoding->setRid(String::FromUTF8(webrtc_encoding.rid));
}
encoding->setActive(webrtc_encoding.active);
if (webrtc_encoding.max_bitrate_bps) {
encoding->setMaxBitrate(webrtc_encoding.max_bitrate_bps.value());
}
if (webrtc_encoding.scale_resolution_down_by) {
encoding->setScaleResolutionDownBy(
webrtc_encoding.scale_resolution_down_by.value());
}
if (webrtc_encoding.max_framerate) {
encoding->setMaxFramerate(webrtc_encoding.max_framerate.value());
}
encoding->setPriority(
PriorityFromDouble(webrtc_encoding.bitrate_priority).c_str());
encoding->setNetworkPriority(
PriorityFromEnum(webrtc_encoding.network_priority).c_str());
if (webrtc_encoding.num_temporal_layers) {
if (*webrtc_encoding.num_temporal_layers == 2) {
encoding->setScalabilityMode("L1T2");
} else if (*webrtc_encoding.num_temporal_layers == 3) {
encoding->setScalabilityMode("L1T3");
} else {
LOG(ERROR) << "Not understood value of num_temporal_layers: "
<< *webrtc_encoding.num_temporal_layers;
}
}
encoding->setAdaptivePtime(webrtc_encoding.adaptive_ptime);
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& webrtc_header : webrtc_parameters->header_extensions) {
headers.push_back(ToRtpHeaderExtensionParameters(webrtc_header));
}
parameters->setHeaderExtensions(headers);
HeapVector<Member<RTCRtpCodecParameters>> codecs;
codecs.ReserveCapacity(
SafeCast<wtf_size_t>(webrtc_parameters->codecs.size()));
for (const auto& webrtc_codec : webrtc_parameters->codecs) {
codecs.push_back(ToRtpCodecParameters(webrtc_codec));
}
parameters->setCodecs(codecs);
last_returned_parameters_ = parameters;
return parameters;
}
ScriptPromise RTCRtpSender::setParameters(
ScriptState* script_state,
const RTCRtpSendParameters* parameters) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
if (!last_returned_parameters_) {
resolver->Reject(MakeGarbageCollected<DOMException>(
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(MakeGarbageCollected<DOMException>(
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.
Vector<webrtc::RtpEncodingParameters> encodings;
absl::optional<webrtc::DegradationPreference> degradation_preference;
std::tie(encodings, degradation_preference) =
ToRtpParameters(pc_->GetExecutionContext(), parameters);
auto* request = MakeGarbageCollected<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) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
sender_->GetStats(
WTF::Bind(WebRTCStatsReportCallbackResolver, WrapPersistent(resolver)),
GetExposedGroupIds(script_state));
return promise;
}
RTCRtpSenderPlatform* 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);
}
void RTCRtpSender::set_transceiver(RTCRtpTransceiver* transceiver) {
transceiver_ = transceiver;
}
void RTCRtpSender::set_transport(RTCDtlsTransport* transport) {
transport_ = transport;
}
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::setStreams(HeapVector<Member<MediaStream>> streams,
ExceptionState& exception_state) {
if (pc_->IsClosed()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"The RTCPeerConnection's signalingState is 'closed'.");
return;
}
if (pc_->sdp_semantics() != webrtc::SdpSemantics::kUnifiedPlan) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
kOnlySupportedInUnifiedPlanMessage);
return;
}
Vector<String> stream_ids;
for (auto stream : streams)
stream_ids.emplace_back(stream->id());
sender_->SetStreams(stream_ids);
}
RTCInsertableStreams* RTCRtpSender::createEncodedStreams(
ScriptState* script_state,
ExceptionState& exception_state) {
if (kind_ == "audio")
return createEncodedAudioStreams(script_state, exception_state);
DCHECK_EQ(kind_, "video");
return createEncodedVideoStreams(script_state, exception_state);
}
RTCInsertableStreams* RTCRtpSender::createEncodedAudioStreams(
ScriptState* script_state,
ExceptionState& exception_state) {
if (!force_encoded_audio_insertable_streams_) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Encoded audio streams not requested at PC initialization");
return nullptr;
}
if (encoded_video_streams_) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Encoded audio streams already created");
return nullptr;
}
InitializeEncodedAudioStreams(script_state);
return encoded_audio_streams_;
}
RTCInsertableStreams* RTCRtpSender::createEncodedVideoStreams(
ScriptState* script_state,
ExceptionState& exception_state) {
if (!force_encoded_video_insertable_streams_) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Encoded video streams not requested at PC initialization");
return nullptr;
}
if (encoded_video_streams_) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Encoded video streams already created");
return nullptr;
}
InitializeEncodedVideoStreams(script_state);
return encoded_video_streams_;
}
void RTCRtpSender::Trace(Visitor* visitor) const {
visitor->Trace(pc_);
visitor->Trace(track_);
visitor->Trace(transport_);
visitor->Trace(dtmf_);
visitor->Trace(streams_);
visitor->Trace(last_returned_parameters_);
visitor->Trace(transceiver_);
visitor->Trace(audio_from_encoder_underlying_source_);
visitor->Trace(audio_to_packetizer_underlying_sink_);
visitor->Trace(encoded_video_streams_);
visitor->Trace(encoded_audio_streams_);
visitor->Trace(video_from_encoder_underlying_source_);
visitor->Trace(video_to_packetizer_underlying_sink_);
visitor->Trace(encoded_video_streams_);
ScriptWrappable::Trace(visitor);
}
RTCRtpCapabilities* RTCRtpSender::getCapabilities(ScriptState* state,
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 =
PeerConnectionDependencyFactory::GetInstance()->GetSenderCapabilities(
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()));
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.empty()) {
std::string sdp_fmtp_line;
for (const auto& parameter : rtc_codec.parameters) {
if (!sdp_fmtp_line.empty())
sdp_fmtp_line += ";";
sdp_fmtp_line += parameter.first + "=" + parameter.second;
}
codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
}
if (rtc_codec.mime_type() == "video/VP8" ||
rtc_codec.mime_type() == "video/VP9") {
Vector<String> modes;
modes.push_back("L1T2");
modes.push_back("L1T3");
codec->setScalabilityModes(modes);
}
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));
header_extensions.push_back(header_extension);
}
capabilities->setHeaderExtensions(header_extensions);
if (IdentifiabilityStudySettings::Get()->IsTypeAllowed(
IdentifiableSurface::Type::kRtcRtpSenderGetCapabilities)) {
IdentifiableTokenBuilder builder;
IdentifiabilityAddRTCRtpCapabilitiesToBuilder(builder, *capabilities);
IdentifiabilityMetricBuilder(ExecutionContext::From(state)->UkmSourceID())
.Set(IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kRtcRtpSenderGetCapabilities,
IdentifiabilityBenignStringToken(kind)),
builder.GetToken())
.Record(ExecutionContext::From(state)->UkmRecorder());
}
return capabilities;
}
void RTCRtpSender::RegisterEncodedAudioStreamCallback() {
DCHECK(!web_sender()
->GetEncodedAudioStreamTransformer()
->HasTransformerCallback());
DCHECK_EQ(kind_, "audio");
web_sender()->GetEncodedAudioStreamTransformer()->SetTransformerCallback(
WTF::BindRepeating(&RTCRtpSender::OnAudioFrameFromEncoder,
WrapWeakPersistent(this)));
}
void RTCRtpSender::UnregisterEncodedAudioStreamCallback() {
web_sender()->GetEncodedAudioStreamTransformer()->ResetTransformerCallback();
}
void RTCRtpSender::InitializeEncodedAudioStreams(ScriptState* script_state) {
DCHECK(!audio_from_encoder_underlying_source_);
DCHECK(!audio_to_packetizer_underlying_sink_);
DCHECK(!encoded_audio_streams_);
DCHECK(force_encoded_audio_insertable_streams_);
encoded_audio_streams_ = RTCInsertableStreams::Create();
// Set up readable stream.
audio_from_encoder_underlying_source_ =
MakeGarbageCollected<RTCEncodedAudioUnderlyingSource>(
script_state,
WTF::Bind(&RTCRtpSender::UnregisterEncodedAudioStreamCallback,
WrapWeakPersistent(this)),
/*is_receiver=*/false);
// The high water mark for the readable stream is set to 0 so that frames are
// removed from the queue right away, without introducing any buffering.
ReadableStream* readable_stream =
ReadableStream::CreateWithCountQueueingStrategy(
script_state, audio_from_encoder_underlying_source_,
/*high_water_mark=*/0);
encoded_audio_streams_->setReadableStream(readable_stream);
encoded_audio_streams_->setReadable(readable_stream);
// Set up writable stream.
audio_to_packetizer_underlying_sink_ =
MakeGarbageCollected<RTCEncodedAudioUnderlyingSink>(
script_state,
WTF::BindRepeating(
[](RTCRtpSender* sender) -> RTCEncodedAudioStreamTransformer* {
return sender ? sender->web_sender()
->GetEncodedAudioStreamTransformer()
: nullptr;
},
WrapWeakPersistent(this)));
// The high water mark for the stream is set to 1 so that the stream is
// ready to write, but without queuing frames.
WritableStream* writable_stream =
WritableStream::CreateWithCountQueueingStrategy(
script_state, audio_to_packetizer_underlying_sink_,
/*high_water_mark=*/1);
encoded_audio_streams_->setWritableStream(writable_stream);
encoded_audio_streams_->setWritable(writable_stream);
}
void RTCRtpSender::OnAudioFrameFromEncoder(
std::unique_ptr<webrtc::TransformableFrameInterface> frame) {
if (audio_from_encoder_underlying_source_) {
audio_from_encoder_underlying_source_->OnFrameFromSource(std::move(frame));
}
}
void RTCRtpSender::RegisterEncodedVideoStreamCallback() {
DCHECK(!web_sender()
->GetEncodedVideoStreamTransformer()
->HasTransformerCallback());
DCHECK_EQ(kind_, "video");
web_sender()->GetEncodedVideoStreamTransformer()->SetTransformerCallback(
WTF::BindRepeating(&RTCRtpSender::OnVideoFrameFromEncoder,
WrapWeakPersistent(this)));
}
void RTCRtpSender::UnregisterEncodedVideoStreamCallback() {
web_sender()->GetEncodedVideoStreamTransformer()->ResetTransformerCallback();
}
void RTCRtpSender::InitializeEncodedVideoStreams(ScriptState* script_state) {
DCHECK(!video_from_encoder_underlying_source_);
DCHECK(!video_to_packetizer_underlying_sink_);
DCHECK(!encoded_video_streams_);
DCHECK(force_encoded_video_insertable_streams_);
encoded_video_streams_ = RTCInsertableStreams::Create();
// Set up readable stream.
video_from_encoder_underlying_source_ =
MakeGarbageCollected<RTCEncodedVideoUnderlyingSource>(
script_state,
WTF::Bind(&RTCRtpSender::UnregisterEncodedVideoStreamCallback,
WrapWeakPersistent(this)));
// The high water mark for the readable stream is set to 0 so that frames are
// removed from the queue right away, without introducing any buffering.
ReadableStream* readable_stream =
ReadableStream::CreateWithCountQueueingStrategy(
script_state, video_from_encoder_underlying_source_,
/*high_water_mark=*/0);
encoded_video_streams_->setReadableStream(readable_stream);
encoded_video_streams_->setReadable(readable_stream);
// Set up writable stream.
video_to_packetizer_underlying_sink_ =
MakeGarbageCollected<RTCEncodedVideoUnderlyingSink>(
script_state,
WTF::BindRepeating(
[](RTCRtpSender* sender) -> RTCEncodedVideoStreamTransformer* {
return sender ? sender->web_sender()
->GetEncodedVideoStreamTransformer()
: nullptr;
},
WrapWeakPersistent(this)),
webrtc::TransformableFrameInterface::Direction::kSender);
// The high water mark for the stream is set to 1 so that the stream is
// ready to write, but without queuing frames.
WritableStream* writable_stream =
WritableStream::CreateWithCountQueueingStrategy(
script_state, video_to_packetizer_underlying_sink_,
/*high_water_mark=*/1);
encoded_video_streams_->setWritableStream(writable_stream);
encoded_video_streams_->setWritable(writable_stream);
}
void RTCRtpSender::OnVideoFrameFromEncoder(
std::unique_ptr<webrtc::TransformableVideoFrameInterface> frame) {
if (video_from_encoder_underlying_source_) {
video_from_encoder_underlying_source_->OnFrameFromSource(std::move(frame));
}
}
} // namespace blink