webrtc_m130/ortc/rtptransportcontrolleradapter.cc
Zhi Huang e830e683c4 Use new TransportController implementation in PeerConnection.
The TransportController will be replaced by the JsepTransportController
and JsepTransport will be replace be JsepTransport2.

The JsepTransportController will take the entire SessionDescription
and handle the RtcpMux, Sdes and BUNDLE internally.

The ownership model is also changed. The P2P layer transports are not
ref-counted and will be owned by the JsepTransport2.

In ORTC aspect, RtpTransportAdapter is now a wrapper over RtpTransport
or SrtpTransport and it implements the public and internal interface
by calling the transport underneath.

Bug: webrtc:8587
Change-Id: Ia7fa61288a566f211f8560072ea0eecaf19e48df
Reviewed-on: https://webrtc-review.googlesource.com/59586
Commit-Queue: Zhi Huang <zhihuang@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22693}
2018-03-30 18:41:19 +00:00

978 lines
37 KiB
C++

/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "ortc/rtptransportcontrolleradapter.h"
#include <algorithm> // For "remove", "find".
#include <set>
#include <sstream>
#include <unordered_map>
#include <utility> // For std::move.
#include "api/proxy.h"
#include "media/base/mediaconstants.h"
#include "ortc/ortcrtpreceiveradapter.h"
#include "ortc/ortcrtpsenderadapter.h"
#include "ortc/rtpparametersconversion.h"
#include "ortc/rtptransportadapter.h"
#include "pc/rtpmediautils.h"
#include "rtc_base/checks.h"
#include "rtc_base/ptr_util.h"
namespace webrtc {
// Note: It's assumed that each individual list doesn't have conflicts, since
// they should have been detected already by rtpparametersconversion.cc. This
// only needs to detect conflicts *between* A and B.
template <typename C1, typename C2>
static RTCError CheckForIdConflicts(
const std::vector<C1>& codecs_a,
const cricket::RtpHeaderExtensions& extensions_a,
const cricket::StreamParamsVec& streams_a,
const std::vector<C2>& codecs_b,
const cricket::RtpHeaderExtensions& extensions_b,
const cricket::StreamParamsVec& streams_b) {
std::ostringstream oss;
// Since it's assumed that C1 and C2 are different types, codecs_a and
// codecs_b should never contain the same payload type, and thus we can just
// use a set.
std::set<int> seen_payload_types;
for (const C1& codec : codecs_a) {
seen_payload_types.insert(codec.id);
}
for (const C2& codec : codecs_b) {
if (!seen_payload_types.insert(codec.id).second) {
oss << "Same payload type used for audio and video codecs: " << codec.id;
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
}
}
// Audio and video *may* use the same header extensions, so use a map.
std::unordered_map<int, std::string> seen_extensions;
for (const webrtc::RtpExtension& extension : extensions_a) {
seen_extensions[extension.id] = extension.uri;
}
for (const webrtc::RtpExtension& extension : extensions_b) {
if (seen_extensions.find(extension.id) != seen_extensions.end() &&
seen_extensions.at(extension.id) != extension.uri) {
oss << "Same ID used for different RTP header extensions: "
<< extension.id;
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
}
}
std::set<uint32_t> seen_ssrcs;
for (const cricket::StreamParams& stream : streams_a) {
seen_ssrcs.insert(stream.ssrcs.begin(), stream.ssrcs.end());
}
for (const cricket::StreamParams& stream : streams_b) {
for (uint32_t ssrc : stream.ssrcs) {
if (!seen_ssrcs.insert(ssrc).second) {
oss << "Same SSRC used for audio and video senders: " << ssrc;
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
}
}
}
return RTCError::OK();
}
BEGIN_OWNED_PROXY_MAP(RtpTransportController)
PROXY_SIGNALING_THREAD_DESTRUCTOR()
PROXY_CONSTMETHOD0(std::vector<RtpTransportInterface*>, GetTransports)
protected:
RtpTransportControllerAdapter* GetInternal() override {
return internal();
}
END_PROXY_MAP()
// static
std::unique_ptr<RtpTransportControllerInterface>
RtpTransportControllerAdapter::CreateProxied(
const cricket::MediaConfig& config,
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread) {
std::unique_ptr<RtpTransportControllerAdapter> wrapped(
new RtpTransportControllerAdapter(config, channel_manager, event_log,
signaling_thread, worker_thread,
network_thread));
return RtpTransportControllerProxyWithInternal<
RtpTransportControllerAdapter>::Create(signaling_thread, worker_thread,
std::move(wrapped));
}
RtpTransportControllerAdapter::~RtpTransportControllerAdapter() {
RTC_DCHECK_RUN_ON(signaling_thread_);
if (!transport_proxies_.empty()) {
RTC_LOG(LS_ERROR)
<< "Destroying RtpTransportControllerAdapter while RtpTransports "
"are still using it; this is unsafe.";
}
if (voice_channel_) {
// This would mean audio RTP senders/receivers that are using us haven't
// been destroyed. This isn't safe (see error log above).
DestroyVoiceChannel();
}
if (voice_channel_) {
// This would mean video RTP senders/receivers that are using us haven't
// been destroyed. This isn't safe (see error log above).
DestroyVideoChannel();
}
// Call must be destroyed on the worker thread.
worker_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&RtpTransportControllerAdapter::Close_w, this));
}
RTCErrorOr<std::unique_ptr<RtpTransportInterface>>
RtpTransportControllerAdapter::CreateProxiedRtpTransport(
const RtpTransportParameters& parameters,
PacketTransportInterface* rtp,
PacketTransportInterface* rtcp) {
if (!transport_proxies_.empty() && (parameters.keepalive != keepalive_)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Cannot create RtpTransport with different keep-alive "
"from the RtpTransports already associated with this "
"transport controller.");
}
auto result = RtpTransportAdapter::CreateProxied(parameters, rtp, rtcp, this);
if (result.ok()) {
transport_proxies_.push_back(result.value().get());
transport_proxies_.back()->GetInternal()->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed);
}
return result;
}
RTCErrorOr<std::unique_ptr<SrtpTransportInterface>>
RtpTransportControllerAdapter::CreateProxiedSrtpTransport(
const RtpTransportParameters& parameters,
PacketTransportInterface* rtp,
PacketTransportInterface* rtcp) {
auto result =
RtpTransportAdapter::CreateSrtpProxied(parameters, rtp, rtcp, this);
if (result.ok()) {
transport_proxies_.push_back(result.value().get());
transport_proxies_.back()->GetInternal()->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed);
}
return result;
}
RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>
RtpTransportControllerAdapter::CreateProxiedRtpSender(
cricket::MediaType kind,
RtpTransportInterface* transport_proxy) {
RTC_DCHECK(transport_proxy);
RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
transport_proxy) != transport_proxies_.end());
std::unique_ptr<OrtcRtpSenderAdapter> new_sender(
new OrtcRtpSenderAdapter(kind, transport_proxy, this));
RTCError err;
switch (kind) {
case cricket::MEDIA_TYPE_AUDIO:
err = AttachAudioSender(new_sender.get(), transport_proxy->GetInternal());
break;
case cricket::MEDIA_TYPE_VIDEO:
err = AttachVideoSender(new_sender.get(), transport_proxy->GetInternal());
break;
case cricket::MEDIA_TYPE_DATA:
RTC_NOTREACHED();
}
if (!err.ok()) {
return std::move(err);
}
return OrtcRtpSenderAdapter::CreateProxy(std::move(new_sender));
}
RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
RtpTransportControllerAdapter::CreateProxiedRtpReceiver(
cricket::MediaType kind,
RtpTransportInterface* transport_proxy) {
RTC_DCHECK(transport_proxy);
RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
transport_proxy) != transport_proxies_.end());
std::unique_ptr<OrtcRtpReceiverAdapter> new_receiver(
new OrtcRtpReceiverAdapter(kind, transport_proxy, this));
RTCError err;
switch (kind) {
case cricket::MEDIA_TYPE_AUDIO:
err = AttachAudioReceiver(new_receiver.get(),
transport_proxy->GetInternal());
break;
case cricket::MEDIA_TYPE_VIDEO:
err = AttachVideoReceiver(new_receiver.get(),
transport_proxy->GetInternal());
break;
case cricket::MEDIA_TYPE_DATA:
RTC_NOTREACHED();
}
if (!err.ok()) {
return std::move(err);
}
return OrtcRtpReceiverAdapter::CreateProxy(std::move(new_receiver));
}
std::vector<RtpTransportInterface*>
RtpTransportControllerAdapter::GetTransports() const {
RTC_DCHECK_RUN_ON(signaling_thread_);
return transport_proxies_;
}
RTCError RtpTransportControllerAdapter::SetRtpTransportParameters(
const RtpTransportParameters& parameters,
RtpTransportInterface* inner_transport) {
if ((video_channel_ != nullptr || voice_channel_ != nullptr) &&
(parameters.keepalive != keepalive_)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Cannot change keep-alive settings after creating "
"media streams or additional transports for the same "
"transport controller.");
}
// Call must be configured on the worker thread.
worker_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&RtpTransportControllerAdapter::SetRtpTransportParameters_w,
this, parameters));
do {
if (inner_transport == inner_audio_transport_) {
CopyRtcpParametersToDescriptions(parameters.rtcp,
&local_audio_description_,
&remote_audio_description_);
if (!voice_channel_->SetLocalContent(&local_audio_description_,
SdpType::kOffer, nullptr)) {
break;
}
if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
SdpType::kAnswer, nullptr)) {
break;
}
} else if (inner_transport == inner_video_transport_) {
CopyRtcpParametersToDescriptions(parameters.rtcp,
&local_video_description_,
&remote_video_description_);
if (!video_channel_->SetLocalContent(&local_video_description_,
SdpType::kOffer, nullptr)) {
break;
}
if (!video_channel_->SetRemoteContent(&remote_video_description_,
SdpType::kAnswer, nullptr)) {
break;
}
}
return RTCError::OK();
} while (false);
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply new RTCP parameters.");
}
void RtpTransportControllerAdapter::SetRtpTransportParameters_w(
const RtpTransportParameters& parameters) {
call_send_rtp_transport_controller_->SetKeepAliveConfig(parameters.keepalive);
}
RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioSenderParameters(
const RtpParameters& parameters,
uint32_t* primary_ssrc) {
RTC_DCHECK(voice_channel_);
RTC_DCHECK(have_audio_sender_);
auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
if (!codecs_result.ok()) {
return codecs_result.MoveError();
}
auto extensions_result =
ToCricketRtpHeaderExtensions(parameters.header_extensions);
if (!extensions_result.ok()) {
return extensions_result.MoveError();
}
auto stream_params_result = MakeSendStreamParamsVec(
parameters.encodings, inner_audio_transport_->GetParameters().rtcp.cname,
local_audio_description_);
if (!stream_params_result.ok()) {
return stream_params_result.MoveError();
}
// Check that audio/video sender aren't using the same IDs to refer to
// different things, if they share the same transport.
if (inner_audio_transport_ == inner_video_transport_) {
RTCError err = CheckForIdConflicts(
codecs_result.value(), extensions_result.value(),
stream_params_result.value(), remote_video_description_.codecs(),
remote_video_description_.rtp_header_extensions(),
local_video_description_.streams());
if (!err.ok()) {
return err;
}
}
bool local_send = false;
int bandwidth = cricket::kAutoBandwidth;
if (parameters.encodings.size() == 1u) {
if (parameters.encodings[0].max_bitrate_bps) {
bandwidth = *parameters.encodings[0].max_bitrate_bps;
}
local_send = parameters.encodings[0].active;
}
const bool local_recv =
RtpTransceiverDirectionHasRecv(local_audio_description_.direction());
const auto local_direction =
RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
if (primary_ssrc && !stream_params_result.value().empty()) {
*primary_ssrc = stream_params_result.value()[0].first_ssrc();
}
// Validation is done, so we can attempt applying the descriptions. Sent
// codecs and header extensions go in remote description, streams go in
// local.
//
// If there are no codecs or encodings, just leave the previous set of
// codecs. The media engine doesn't like an empty set of codecs.
if (local_audio_description_.streams().empty() &&
remote_audio_description_.codecs().empty()) {
} else {
remote_audio_description_.set_codecs(codecs_result.MoveValue());
}
remote_audio_description_.set_rtp_header_extensions(
extensions_result.MoveValue());
remote_audio_description_.set_bandwidth(bandwidth);
local_audio_description_.mutable_streams() = stream_params_result.MoveValue();
// Direction set based on encoding "active" flag.
local_audio_description_.set_direction(local_direction);
remote_audio_description_.set_direction(
RtpTransceiverDirectionReversed(local_direction));
// Set remote content first, to ensure the stream is created with the correct
// codec.
if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
SdpType::kOffer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply remote parameters to media channel.");
}
if (!voice_channel_->SetLocalContent(&local_audio_description_,
SdpType::kAnswer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply local parameters to media channel.");
}
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoSenderParameters(
const RtpParameters& parameters,
uint32_t* primary_ssrc) {
RTC_DCHECK(video_channel_);
RTC_DCHECK(have_video_sender_);
auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
if (!codecs_result.ok()) {
return codecs_result.MoveError();
}
auto extensions_result =
ToCricketRtpHeaderExtensions(parameters.header_extensions);
if (!extensions_result.ok()) {
return extensions_result.MoveError();
}
auto stream_params_result = MakeSendStreamParamsVec(
parameters.encodings, inner_video_transport_->GetParameters().rtcp.cname,
local_video_description_);
if (!stream_params_result.ok()) {
return stream_params_result.MoveError();
}
// Check that audio/video sender aren't using the same IDs to refer to
// different things, if they share the same transport.
if (inner_audio_transport_ == inner_video_transport_) {
RTCError err = CheckForIdConflicts(
codecs_result.value(), extensions_result.value(),
stream_params_result.value(), remote_audio_description_.codecs(),
remote_audio_description_.rtp_header_extensions(),
local_audio_description_.streams());
if (!err.ok()) {
return err;
}
}
bool local_send = false;
int bandwidth = cricket::kAutoBandwidth;
if (parameters.encodings.size() == 1u) {
if (parameters.encodings[0].max_bitrate_bps) {
bandwidth = *parameters.encodings[0].max_bitrate_bps;
}
local_send = parameters.encodings[0].active;
}
const bool local_recv =
RtpTransceiverDirectionHasRecv(local_audio_description_.direction());
const auto local_direction =
RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
if (primary_ssrc && !stream_params_result.value().empty()) {
*primary_ssrc = stream_params_result.value()[0].first_ssrc();
}
// Validation is done, so we can attempt applying the descriptions. Sent
// codecs and header extensions go in remote description, streams go in
// local.
//
// If there are no codecs or encodings, just leave the previous set of
// codecs. The media engine doesn't like an empty set of codecs.
if (local_video_description_.streams().empty() &&
remote_video_description_.codecs().empty()) {
} else {
remote_video_description_.set_codecs(codecs_result.MoveValue());
}
remote_video_description_.set_rtp_header_extensions(
extensions_result.MoveValue());
remote_video_description_.set_bandwidth(bandwidth);
local_video_description_.mutable_streams() = stream_params_result.MoveValue();
// Direction set based on encoding "active" flag.
local_video_description_.set_direction(local_direction);
remote_video_description_.set_direction(
RtpTransceiverDirectionReversed(local_direction));
// Set remote content first, to ensure the stream is created with the correct
// codec.
if (!video_channel_->SetRemoteContent(&remote_video_description_,
SdpType::kOffer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply remote parameters to media channel.");
}
if (!video_channel_->SetLocalContent(&local_video_description_,
SdpType::kAnswer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply local parameters to media channel.");
}
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioReceiverParameters(
const RtpParameters& parameters) {
RTC_DCHECK(voice_channel_);
RTC_DCHECK(have_audio_receiver_);
auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
if (!codecs_result.ok()) {
return codecs_result.MoveError();
}
auto extensions_result =
ToCricketRtpHeaderExtensions(parameters.header_extensions);
if (!extensions_result.ok()) {
return extensions_result.MoveError();
}
auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
if (!stream_params_result.ok()) {
return stream_params_result.MoveError();
}
// Check that audio/video receive aren't using the same IDs to refer to
// different things, if they share the same transport.
if (inner_audio_transport_ == inner_video_transport_) {
RTCError err = CheckForIdConflicts(
codecs_result.value(), extensions_result.value(),
stream_params_result.value(), local_video_description_.codecs(),
local_video_description_.rtp_header_extensions(),
remote_video_description_.streams());
if (!err.ok()) {
return err;
}
}
const bool local_send =
RtpTransceiverDirectionHasSend(local_audio_description_.direction());
const bool local_recv =
!parameters.encodings.empty() && parameters.encodings[0].active;
const auto local_direction =
RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
// Validation is done, so we can attempt applying the descriptions. Received
// codecs and header extensions go in local description, streams go in
// remote.
//
// If there are no codecs or encodings, just leave the previous set of
// codecs. The media engine doesn't like an empty set of codecs.
if (remote_audio_description_.streams().empty() &&
local_audio_description_.codecs().empty()) {
} else {
local_audio_description_.set_codecs(codecs_result.MoveValue());
}
local_audio_description_.set_rtp_header_extensions(
extensions_result.MoveValue());
remote_audio_description_.mutable_streams() =
stream_params_result.MoveValue();
// Direction set based on encoding "active" flag.
local_audio_description_.set_direction(local_direction);
remote_audio_description_.set_direction(
RtpTransceiverDirectionReversed(local_direction));
if (!voice_channel_->SetLocalContent(&local_audio_description_,
SdpType::kOffer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply local parameters to media channel.");
}
if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
SdpType::kAnswer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply remote parameters to media channel.");
}
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoReceiverParameters(
const RtpParameters& parameters) {
RTC_DCHECK(video_channel_);
RTC_DCHECK(have_video_receiver_);
auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
if (!codecs_result.ok()) {
return codecs_result.MoveError();
}
auto extensions_result =
ToCricketRtpHeaderExtensions(parameters.header_extensions);
if (!extensions_result.ok()) {
return extensions_result.MoveError();
}
int bandwidth = cricket::kAutoBandwidth;
auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
if (!stream_params_result.ok()) {
return stream_params_result.MoveError();
}
// Check that audio/video receiver aren't using the same IDs to refer to
// different things, if they share the same transport.
if (inner_audio_transport_ == inner_video_transport_) {
RTCError err = CheckForIdConflicts(
codecs_result.value(), extensions_result.value(),
stream_params_result.value(), local_audio_description_.codecs(),
local_audio_description_.rtp_header_extensions(),
remote_audio_description_.streams());
if (!err.ok()) {
return err;
}
}
const bool local_send =
RtpTransceiverDirectionHasSend(local_video_description_.direction());
const bool local_recv =
!parameters.encodings.empty() && parameters.encodings[0].active;
const auto local_direction =
RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
// Validation is done, so we can attempt applying the descriptions. Received
// codecs and header extensions go in local description, streams go in
// remote.
//
// If there are no codecs or encodings, just leave the previous set of
// codecs. The media engine doesn't like an empty set of codecs.
if (remote_video_description_.streams().empty() &&
local_video_description_.codecs().empty()) {
} else {
local_video_description_.set_codecs(codecs_result.MoveValue());
}
local_video_description_.set_rtp_header_extensions(
extensions_result.MoveValue());
local_video_description_.set_bandwidth(bandwidth);
remote_video_description_.mutable_streams() =
stream_params_result.MoveValue();
// Direction set based on encoding "active" flag.
local_video_description_.set_direction(local_direction);
remote_video_description_.set_direction(
RtpTransceiverDirectionReversed(local_direction));
if (!video_channel_->SetLocalContent(&local_video_description_,
SdpType::kOffer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply local parameters to media channel.");
}
if (!video_channel_->SetRemoteContent(&remote_video_description_,
SdpType::kAnswer, nullptr)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to apply remote parameters to media channel.");
}
return RTCError::OK();
}
RtpTransportControllerAdapter::RtpTransportControllerAdapter(
const cricket::MediaConfig& config,
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread)
: signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
network_thread_(network_thread),
media_config_(config),
channel_manager_(channel_manager),
event_log_(event_log),
call_send_rtp_transport_controller_(nullptr) {
RTC_DCHECK_RUN_ON(signaling_thread_);
RTC_DCHECK(channel_manager_);
// Add "dummy" codecs to the descriptions, because the media engines
// currently reject empty lists of codecs. Note that these codecs will never
// actually be used, because when parameters are set, the dummy codecs will
// be replaced by actual codecs before any send/receive streams are created.
static const cricket::AudioCodec dummy_audio(0, cricket::kPcmuCodecName, 8000,
0, 1);
static const cricket::VideoCodec dummy_video(96, cricket::kVp8CodecName);
local_audio_description_.AddCodec(dummy_audio);
remote_audio_description_.AddCodec(dummy_audio);
local_video_description_.AddCodec(dummy_video);
remote_video_description_.AddCodec(dummy_video);
worker_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&RtpTransportControllerAdapter::Init_w, this));
}
// TODO(nisse): Duplicates corresponding method in PeerConnection (used
// to be in MediaController).
void RtpTransportControllerAdapter::Init_w() {
RTC_DCHECK(worker_thread_->IsCurrent());
RTC_DCHECK(!call_);
const int kMinBandwidthBps = 30000;
const int kStartBandwidthBps = 300000;
const int kMaxBandwidthBps = 2000000;
webrtc::Call::Config call_config(event_log_);
call_config.audio_state = channel_manager_->media_engine()->GetAudioState();
call_config.bitrate_config.min_bitrate_bps = kMinBandwidthBps;
call_config.bitrate_config.start_bitrate_bps = kStartBandwidthBps;
call_config.bitrate_config.max_bitrate_bps = kMaxBandwidthBps;
std::unique_ptr<RtpTransportControllerSend> controller_send =
rtc::MakeUnique<RtpTransportControllerSend>(
Clock::GetRealTimeClock(), event_log_, call_config.bitrate_config);
call_send_rtp_transport_controller_ = controller_send.get();
call_.reset(webrtc::Call::Create(call_config, std::move(controller_send)));
}
void RtpTransportControllerAdapter::Close_w() {
call_.reset();
call_send_rtp_transport_controller_ = nullptr;
}
RTCError RtpTransportControllerAdapter::AttachAudioSender(
OrtcRtpSenderAdapter* sender,
RtpTransportInterface* inner_transport) {
if (have_audio_sender_) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using two audio RtpSenders with the same "
"RtpTransportControllerAdapter is not currently "
"supported.");
}
if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using different transports for the audio "
"RtpSender and RtpReceiver is not currently "
"supported.");
}
// If setting new transport, extract its RTCP parameters and create voice
// channel.
if (!inner_audio_transport_) {
CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
&local_audio_description_,
&remote_audio_description_);
inner_audio_transport_ = inner_transport;
CreateVoiceChannel();
}
have_audio_sender_ = true;
sender->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnAudioSenderDestroyed);
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::AttachVideoSender(
OrtcRtpSenderAdapter* sender,
RtpTransportInterface* inner_transport) {
if (have_video_sender_) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using two video RtpSenders with the same "
"RtpTransportControllerAdapter is not currently "
"supported.");
}
if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using different transports for the video "
"RtpSender and RtpReceiver is not currently "
"supported.");
}
// If setting new transport, extract its RTCP parameters and create video
// channel.
if (!inner_video_transport_) {
CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
&local_video_description_,
&remote_video_description_);
inner_video_transport_ = inner_transport;
CreateVideoChannel();
}
have_video_sender_ = true;
sender->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnVideoSenderDestroyed);
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::AttachAudioReceiver(
OrtcRtpReceiverAdapter* receiver,
RtpTransportInterface* inner_transport) {
if (have_audio_receiver_) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using two audio RtpReceivers with the same "
"RtpTransportControllerAdapter is not currently "
"supported.");
}
if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using different transports for the audio "
"RtpReceiver and RtpReceiver is not currently "
"supported.");
}
// If setting new transport, extract its RTCP parameters and create voice
// channel.
if (!inner_audio_transport_) {
CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
&local_audio_description_,
&remote_audio_description_);
inner_audio_transport_ = inner_transport;
CreateVoiceChannel();
}
have_audio_receiver_ = true;
receiver->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnAudioReceiverDestroyed);
return RTCError::OK();
}
RTCError RtpTransportControllerAdapter::AttachVideoReceiver(
OrtcRtpReceiverAdapter* receiver,
RtpTransportInterface* inner_transport) {
if (have_video_receiver_) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using two video RtpReceivers with the same "
"RtpTransportControllerAdapter is not currently "
"supported.");
}
if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Using different transports for the video "
"RtpReceiver and RtpReceiver is not currently "
"supported.");
}
// If setting new transport, extract its RTCP parameters and create video
// channel.
if (!inner_video_transport_) {
CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
&local_video_description_,
&remote_video_description_);
inner_video_transport_ = inner_transport;
CreateVideoChannel();
}
have_video_receiver_ = true;
receiver->SignalDestroyed.connect(
this, &RtpTransportControllerAdapter::OnVideoReceiverDestroyed);
return RTCError::OK();
}
void RtpTransportControllerAdapter::OnRtpTransportDestroyed(
RtpTransportAdapter* transport) {
RTC_DCHECK_RUN_ON(signaling_thread_);
auto it = std::find_if(transport_proxies_.begin(), transport_proxies_.end(),
[transport](RtpTransportInterface* proxy) {
return proxy->GetInternal() == transport;
});
if (it == transport_proxies_.end()) {
RTC_NOTREACHED();
return;
}
transport_proxies_.erase(it);
}
void RtpTransportControllerAdapter::OnAudioSenderDestroyed() {
if (!have_audio_sender_) {
RTC_NOTREACHED();
return;
}
// Empty parameters should result in sending being stopped.
RTCError err =
ValidateAndApplyAudioSenderParameters(RtpParameters(), nullptr);
RTC_DCHECK(err.ok());
have_audio_sender_ = false;
if (!have_audio_receiver_) {
DestroyVoiceChannel();
}
}
void RtpTransportControllerAdapter::OnVideoSenderDestroyed() {
if (!have_video_sender_) {
RTC_NOTREACHED();
return;
}
// Empty parameters should result in sending being stopped.
RTCError err =
ValidateAndApplyVideoSenderParameters(RtpParameters(), nullptr);
RTC_DCHECK(err.ok());
have_video_sender_ = false;
if (!have_video_receiver_) {
DestroyVideoChannel();
}
}
void RtpTransportControllerAdapter::OnAudioReceiverDestroyed() {
if (!have_audio_receiver_) {
RTC_NOTREACHED();
return;
}
// Empty parameters should result in receiving being stopped.
RTCError err = ValidateAndApplyAudioReceiverParameters(RtpParameters());
RTC_DCHECK(err.ok());
have_audio_receiver_ = false;
if (!have_audio_sender_) {
DestroyVoiceChannel();
}
}
void RtpTransportControllerAdapter::OnVideoReceiverDestroyed() {
if (!have_video_receiver_) {
RTC_NOTREACHED();
return;
}
// Empty parameters should result in receiving being stopped.
RTCError err = ValidateAndApplyVideoReceiverParameters(RtpParameters());
RTC_DCHECK(err.ok());
have_video_receiver_ = false;
if (!have_video_sender_) {
DestroyVideoChannel();
}
}
void RtpTransportControllerAdapter::CreateVoiceChannel() {
voice_channel_ = channel_manager_->CreateVoiceChannel(
call_.get(), media_config_, inner_audio_transport_->GetInternal(),
signaling_thread_, "audio", false, rtc::CryptoOptions(),
cricket::AudioOptions());
RTC_DCHECK(voice_channel_);
voice_channel_->Enable(true);
voice_channel_->DisableEncryption(
!inner_audio_transport_->GetInternal()->IsSrtpTransport());
}
void RtpTransportControllerAdapter::CreateVideoChannel() {
video_channel_ = channel_manager_->CreateVideoChannel(
call_.get(), media_config_, inner_video_transport_->GetInternal(),
signaling_thread_, "video", false, rtc::CryptoOptions(),
cricket::VideoOptions());
RTC_DCHECK(video_channel_);
video_channel_->Enable(true);
video_channel_->DisableEncryption(
!inner_video_transport_->GetInternal()->IsSrtpTransport());
}
void RtpTransportControllerAdapter::DestroyVoiceChannel() {
RTC_DCHECK(voice_channel_);
channel_manager_->DestroyVoiceChannel(voice_channel_);
voice_channel_ = nullptr;
inner_audio_transport_ = nullptr;
}
void RtpTransportControllerAdapter::DestroyVideoChannel() {
RTC_DCHECK(video_channel_);
channel_manager_->DestroyVideoChannel(video_channel_);
video_channel_ = nullptr;
inner_video_transport_ = nullptr;
}
void RtpTransportControllerAdapter::CopyRtcpParametersToDescriptions(
const RtcpParameters& params,
cricket::MediaContentDescription* local,
cricket::MediaContentDescription* remote) {
local->set_rtcp_mux(params.mux);
remote->set_rtcp_mux(params.mux);
local->set_rtcp_reduced_size(params.reduced_size);
remote->set_rtcp_reduced_size(params.reduced_size);
for (cricket::StreamParams& stream_params : local->mutable_streams()) {
stream_params.cname = params.cname;
}
}
uint32_t RtpTransportControllerAdapter::GenerateUnusedSsrc(
std::set<uint32_t>* new_ssrcs) const {
uint32_t ssrc;
do {
ssrc = rtc::CreateRandomNonZeroId();
} while (
cricket::GetStreamBySsrc(local_audio_description_.streams(), ssrc) ||
cricket::GetStreamBySsrc(remote_audio_description_.streams(), ssrc) ||
cricket::GetStreamBySsrc(local_video_description_.streams(), ssrc) ||
cricket::GetStreamBySsrc(remote_video_description_.streams(), ssrc) ||
!new_ssrcs->insert(ssrc).second);
return ssrc;
}
RTCErrorOr<cricket::StreamParamsVec>
RtpTransportControllerAdapter::MakeSendStreamParamsVec(
std::vector<RtpEncodingParameters> encodings,
const std::string& cname,
const cricket::MediaContentDescription& description) const {
if (encodings.size() > 1u) {
LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER,
"ORTC API implementation doesn't currently "
"support simulcast or layered encodings.");
} else if (encodings.empty()) {
return cricket::StreamParamsVec();
}
RtpEncodingParameters& encoding = encodings[0];
std::set<uint32_t> new_ssrcs;
if (encoding.ssrc) {
new_ssrcs.insert(*encoding.ssrc);
}
if (encoding.rtx && encoding.rtx->ssrc) {
new_ssrcs.insert(*encoding.rtx->ssrc);
}
// May need to fill missing SSRCs with generated ones.
if (!encoding.ssrc) {
if (!description.streams().empty()) {
encoding.ssrc.emplace(description.streams()[0].first_ssrc());
} else {
encoding.ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
}
}
if (encoding.rtx && !encoding.rtx->ssrc) {
uint32_t existing_rtx_ssrc;
if (!description.streams().empty() &&
description.streams()[0].GetFidSsrc(
description.streams()[0].first_ssrc(), &existing_rtx_ssrc)) {
encoding.rtx->ssrc.emplace(existing_rtx_ssrc);
} else {
encoding.rtx->ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
}
}
auto result = ToCricketStreamParamsVec(encodings);
if (!result.ok()) {
return result.MoveError();
}
// If conversion was successful, there should be one StreamParams.
RTC_DCHECK_EQ(1u, result.value().size());
result.value()[0].cname = cname;
return result;
}
} // namespace webrtc