blob: ddc698d1552efdf33a515fd971534844218f1966 [file] [log] [blame]
// Copyright 2018 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_transceiver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_capability.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_receiver.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
namespace {
String TransceiverDirectionToString(
const webrtc::RtpTransceiverDirection& direction) {
switch (direction) {
case webrtc::RtpTransceiverDirection::kSendRecv:
return "sendrecv";
case webrtc::RtpTransceiverDirection::kSendOnly:
return "sendonly";
case webrtc::RtpTransceiverDirection::kRecvOnly:
return "recvonly";
case webrtc::RtpTransceiverDirection::kInactive:
return "inactive";
case webrtc::RtpTransceiverDirection::kStopped:
return "stopped";
default:
NOTREACHED();
return String();
}
}
String OptionalTransceiverDirectionToString(
const base::Optional<webrtc::RtpTransceiverDirection>& direction) {
return direction ? TransceiverDirectionToString(*direction)
: String(); // null
}
bool TransceiverDirectionFromString(
const String& direction_string,
base::Optional<webrtc::RtpTransceiverDirection>* direction_out) {
if (!direction_string) {
*direction_out = base::nullopt;
return true;
}
if (direction_string == "sendrecv") {
*direction_out = webrtc::RtpTransceiverDirection::kSendRecv;
return true;
}
if (direction_string == "sendonly") {
*direction_out = webrtc::RtpTransceiverDirection::kSendOnly;
return true;
}
if (direction_string == "recvonly") {
*direction_out = webrtc::RtpTransceiverDirection::kRecvOnly;
return true;
}
if (direction_string == "inactive") {
*direction_out = webrtc::RtpTransceiverDirection::kInactive;
return true;
}
return false;
}
bool OptionalTransceiverDirectionFromStringWithStopped(
const String& direction_string,
absl::optional<webrtc::RtpTransceiverDirection>* direction_out) {
if (direction_string == "stopped") {
*direction_out = webrtc::RtpTransceiverDirection::kStopped;
return true;
}
base::Optional<webrtc::RtpTransceiverDirection> base_direction;
bool result =
TransceiverDirectionFromString(direction_string, &base_direction);
if (base_direction)
*direction_out = *base_direction;
return result;
}
} // namespace
webrtc::RtpTransceiverInit ToRtpTransceiverInit(
ExecutionContext* context,
const RTCRtpTransceiverInit* init) {
webrtc::RtpTransceiverInit webrtc_init;
base::Optional<webrtc::RtpTransceiverDirection> direction;
if (init->hasDirection() &&
TransceiverDirectionFromString(init->direction(), &direction) &&
direction) {
webrtc_init.direction = *direction;
}
DCHECK(init->hasStreams());
for (const auto& stream : init->streams()) {
webrtc_init.stream_ids.push_back(stream->id().Utf8());
}
DCHECK(init->hasSendEncodings());
for (const auto& encoding : init->sendEncodings()) {
webrtc_init.send_encodings.push_back(
ToRtpEncodingParameters(context, encoding));
}
return webrtc_init;
}
RTCRtpTransceiver::RTCRtpTransceiver(
RTCPeerConnection* pc,
std::unique_ptr<RTCRtpTransceiverPlatform> platform_transceiver,
RTCRtpSender* sender,
RTCRtpReceiver* receiver)
: pc_(pc),
platform_transceiver_(std::move(platform_transceiver)),
sender_(sender),
receiver_(receiver),
fired_direction_(base::nullopt) {
DCHECK(pc_);
DCHECK(platform_transceiver_);
DCHECK(sender_);
DCHECK(receiver_);
UpdateMembers();
sender_->set_transceiver(this);
receiver_->set_transceiver(this);
}
String RTCRtpTransceiver::mid() const {
return platform_transceiver_->Mid();
}
RTCRtpSender* RTCRtpTransceiver::sender() const {
return sender_;
}
RTCRtpReceiver* RTCRtpTransceiver::receiver() const {
return receiver_;
}
bool RTCRtpTransceiver::stopped() const {
return stopped_;
}
String RTCRtpTransceiver::direction() const {
return direction_;
}
void RTCRtpTransceiver::setDirection(String direction,
ExceptionState& exception_state) {
base::Optional<webrtc::RtpTransceiverDirection> webrtc_direction;
if (!TransceiverDirectionFromString(direction, &webrtc_direction) ||
!webrtc_direction) {
exception_state.ThrowTypeError("Invalid RTCRtpTransceiverDirection.");
return;
}
if (pc_->IsClosed()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"The peer connection is closed.");
return;
}
if (stopped_) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"The transceiver is stopped.");
return;
}
webrtc::RTCError error =
platform_transceiver_->SetDirection(*webrtc_direction);
if (!error.ok()) {
ThrowExceptionFromRTCError(error, exception_state);
return;
}
UpdateMembers();
}
String RTCRtpTransceiver::currentDirection() const {
return current_direction_;
}
void RTCRtpTransceiver::UpdateMembers() {
stopped_ = platform_transceiver_->Stopped();
direction_ = TransceiverDirectionToString(platform_transceiver_->Direction());
current_direction_ = OptionalTransceiverDirectionToString(
platform_transceiver_->CurrentDirection());
fired_direction_ = platform_transceiver_->FiredDirection();
}
void RTCRtpTransceiver::OnPeerConnectionClosed() {
receiver_->track()->Component()->Source()->SetReadyState(
MediaStreamSource::kReadyStateMuted);
stopped_ = true;
current_direction_ = String(); // null
}
RTCRtpTransceiverPlatform* RTCRtpTransceiver::platform_transceiver() const {
return platform_transceiver_.get();
}
base::Optional<webrtc::RtpTransceiverDirection>
RTCRtpTransceiver::fired_direction() const {
return fired_direction_;
}
bool RTCRtpTransceiver::DirectionHasSend() const {
auto direction = platform_transceiver_->Direction();
return direction == webrtc::RtpTransceiverDirection::kSendRecv ||
direction == webrtc::RtpTransceiverDirection::kSendOnly;
}
bool RTCRtpTransceiver::DirectionHasRecv() const {
auto direction = platform_transceiver_->Direction();
return direction == webrtc::RtpTransceiverDirection::kSendRecv ||
direction == webrtc::RtpTransceiverDirection::kRecvOnly;
}
bool RTCRtpTransceiver::FiredDirectionHasRecv() const {
return fired_direction_ &&
(*fired_direction_ == webrtc::RtpTransceiverDirection::kSendRecv ||
*fired_direction_ == webrtc::RtpTransceiverDirection::kRecvOnly);
}
void RTCRtpTransceiver::stop(ExceptionState& exception_state) {
webrtc::RTCError error = platform_transceiver_->Stop();
if (!error.ok()) {
ThrowExceptionFromRTCError(error, exception_state);
return;
}
stopped_ = true;
UpdateMembers();
}
void RTCRtpTransceiver::setCodecPreferences(
const HeapVector<Member<RTCRtpCodecCapability>>& codecs,
ExceptionState& exception_state) {
Vector<webrtc::RtpCodecCapability> codec_preferences;
codec_preferences.ReserveCapacity(codecs.size());
for (const auto& codec : codecs) {
codec_preferences.emplace_back();
auto& webrtc_codec = codec_preferences.back();
auto slash_position = codec->mimeType().find('/');
if (slash_position == WTF::kNotFound) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError, "Invalid codec");
return;
}
auto type = codec->mimeType().Left(slash_position);
if (type == "video") {
webrtc_codec.kind = cricket::MEDIA_TYPE_VIDEO;
} else if (type == "audio") {
webrtc_codec.kind = cricket::MEDIA_TYPE_AUDIO;
} else {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError, "Invalid codec");
return;
}
webrtc_codec.name = codec->mimeType().Substring(slash_position + 1).Ascii();
webrtc_codec.clock_rate = codec->clockRate();
if (codec->hasChannels()) {
webrtc_codec.num_channels = codec->channels();
}
if (codec->hasSdpFmtpLine()) {
WTF::Vector<WTF::String> parameters;
codec->sdpFmtpLine().Split(';', parameters);
for (const auto& parameter : parameters) {
auto equal_position = parameter.find('=');
if (equal_position == WTF::kNotFound) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError, "Invalid codec");
return;
}
auto parameter_name = parameter.Left(equal_position);
auto parameter_value = parameter.Substring(equal_position + 1);
webrtc_codec.parameters.emplace(parameter_name.Ascii(),
parameter_value.Ascii());
}
}
}
auto result = platform_transceiver_->SetCodecPreferences(codec_preferences);
if (!result.ok()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError, result.message());
}
}
void RTCRtpTransceiver::setOfferedRtpHeaderExtensions(
const HeapVector<Member<RTCRtpHeaderExtensionCapability>>&
header_extensions_to_offer,
ExceptionState& exception_state) {
Vector<webrtc::RtpHeaderExtensionCapability> webrtc_hdr_exts;
auto webrtc_offered_exts = platform_transceiver_->HeaderExtensionsToOffer();
int id = 1;
for (const auto& hdr_ext : header_extensions_to_offer) {
// Handle invalid requests for mandatory extensions as per
// https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
// Step 2.1 (not handled on the WebRTC level).
if (hdr_ext->uri().IsEmpty()) {
exception_state.ThrowTypeError("The extension URL cannot be empty.");
return;
}
absl::optional<webrtc::RtpTransceiverDirection> direction;
if (!OptionalTransceiverDirectionFromStringWithStopped(hdr_ext->direction(),
&direction) ||
!direction) {
exception_state.ThrowTypeError("Invalid RTCRtpTransceiverDirection.");
return;
}
const int id_to_store = direction ? id++ : 0;
webrtc_hdr_exts.emplace_back(hdr_ext->uri().Ascii(), id_to_store,
*direction);
}
webrtc::RTCError status =
platform_transceiver_->SetOfferedRtpHeaderExtensions(
std::move(webrtc_hdr_exts));
if (status.type() == webrtc::RTCErrorType::UNSUPPORTED_PARAMETER) {
// TODO(crbug.com/1051821): support DOMExceptionCode::kNotSupportedError in
// rtc_error_util.h/cc and get rid of this manually handled case.
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
status.message());
return;
} else if (status.type() != webrtc::RTCErrorType::NONE) {
ThrowExceptionFromRTCError(status, exception_state);
return;
}
}
HeapVector<Member<RTCRtpHeaderExtensionCapability>>
RTCRtpTransceiver::headerExtensionsToOffer() const {
auto webrtc_exts = platform_transceiver_->HeaderExtensionsToOffer();
HeapVector<Member<RTCRtpHeaderExtensionCapability>> exts;
for (const auto& webrtc_ext : webrtc_exts) {
auto* ext = MakeGarbageCollected<RTCRtpHeaderExtensionCapability>();
ext->setDirection(TransceiverDirectionToString(webrtc_ext.direction));
ext->setUri(webrtc_ext.uri.c_str());
exts.push_back(ext);
}
return exts;
}
HeapVector<Member<RTCRtpHeaderExtensionCapability>>
RTCRtpTransceiver::headerExtensionsNegotiated() const {
auto webrtc_exts = platform_transceiver_->HeaderExtensionsNegotiated();
HeapVector<Member<RTCRtpHeaderExtensionCapability>> exts;
for (const auto& webrtc_ext : webrtc_exts) {
auto* ext = MakeGarbageCollected<RTCRtpHeaderExtensionCapability>();
ext->setDirection(TransceiverDirectionToString(webrtc_ext.direction));
ext->setUri(webrtc_ext.uri.c_str());
exts.push_back(ext);
}
return exts;
}
void RTCRtpTransceiver::Trace(Visitor* visitor) const {
visitor->Trace(pc_);
visitor->Trace(sender_);
visitor->Trace(receiver_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink