Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
11
TMessagesProj/jni/voip/webrtc/pc/OWNERS
Normal file
11
TMessagesProj/jni/voip/webrtc/pc/OWNERS
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
hbos@webrtc.org
|
||||
hta@webrtc.org
|
||||
perkj@webrtc.org
|
||||
tommi@webrtc.org
|
||||
deadbeef@webrtc.org
|
||||
orphis@webrtc.org
|
||||
|
||||
# Adding features via SDP munging requires approval from SDP owners
|
||||
per-file webrtc_sdp.cc = set noparent
|
||||
per-file webrtc_sdp.cc = hta@webrtc.org
|
||||
per-file webrtc_sdp.cc = hbos@webrtc.org
|
||||
347
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver.cc
Normal file
347
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver.cc
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/audio_rtp_receiver.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "pc/audio_track.h"
|
||||
#include "pc/media_stream_track_proxy.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioRtpReceiver::AudioRtpReceiver(
|
||||
rtc::Thread* worker_thread,
|
||||
std::string receiver_id,
|
||||
std::vector<std::string> stream_ids,
|
||||
bool is_unified_plan,
|
||||
cricket::VoiceMediaReceiveChannelInterface* voice_channel /*= nullptr*/)
|
||||
: AudioRtpReceiver(worker_thread,
|
||||
receiver_id,
|
||||
CreateStreamsFromIds(std::move(stream_ids)),
|
||||
is_unified_plan,
|
||||
voice_channel) {}
|
||||
|
||||
AudioRtpReceiver::AudioRtpReceiver(
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& receiver_id,
|
||||
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
|
||||
bool is_unified_plan,
|
||||
cricket::VoiceMediaReceiveChannelInterface* voice_channel /*= nullptr*/)
|
||||
: worker_thread_(worker_thread),
|
||||
id_(receiver_id),
|
||||
source_(rtc::make_ref_counted<RemoteAudioSource>(
|
||||
worker_thread,
|
||||
is_unified_plan
|
||||
? RemoteAudioSource::OnAudioChannelGoneAction::kSurvive
|
||||
: RemoteAudioSource::OnAudioChannelGoneAction::kEnd)),
|
||||
track_(AudioTrackProxyWithInternal<AudioTrack>::Create(
|
||||
rtc::Thread::Current(),
|
||||
AudioTrack::Create(receiver_id, source_))),
|
||||
media_channel_(voice_channel),
|
||||
cached_track_enabled_(track_->internal()->enabled()),
|
||||
attachment_id_(GenerateUniqueId()),
|
||||
worker_thread_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()) {
|
||||
RTC_DCHECK(worker_thread_);
|
||||
RTC_DCHECK(track_->GetSource()->remote());
|
||||
track_->RegisterObserver(this);
|
||||
track_->GetSource()->RegisterAudioObserver(this);
|
||||
SetStreams(streams);
|
||||
}
|
||||
|
||||
AudioRtpReceiver::~AudioRtpReceiver() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
RTC_DCHECK(!media_channel_);
|
||||
|
||||
track_->GetSource()->UnregisterAudioObserver(this);
|
||||
track_->UnregisterObserver(this);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::OnChanged() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
const bool enabled = track_->internal()->enabled();
|
||||
if (cached_track_enabled_ == enabled)
|
||||
return;
|
||||
cached_track_enabled_ = enabled;
|
||||
worker_thread_->PostTask(SafeTask(worker_thread_safety_, [this, enabled]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
Reconfigure(enabled);
|
||||
}));
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetOutputVolume_w(double volume) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
RTC_DCHECK_GE(volume, 0.0);
|
||||
RTC_DCHECK_LE(volume, 10.0);
|
||||
|
||||
if (!media_channel_)
|
||||
return;
|
||||
|
||||
signaled_ssrc_ ? media_channel_->SetOutputVolume(*signaled_ssrc_, volume)
|
||||
: media_channel_->SetDefaultOutputVolume(volume);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::OnSetVolume(double volume) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
RTC_DCHECK_GE(volume, 0);
|
||||
RTC_DCHECK_LE(volume, 10);
|
||||
|
||||
bool track_enabled = track_->internal()->enabled();
|
||||
worker_thread_->BlockingCall([&]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
// Update the cached_volume_ even when stopped, to allow clients to set
|
||||
// the volume before starting/restarting, eg see crbug.com/1272566.
|
||||
cached_volume_ = volume;
|
||||
// When the track is disabled, the volume of the source, which is the
|
||||
// corresponding WebRtc Voice Engine channel will be 0. So we do not
|
||||
// allow setting the volume to the source when the track is disabled.
|
||||
if (track_enabled)
|
||||
SetOutputVolume_w(volume);
|
||||
});
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<DtlsTransportInterface> AudioRtpReceiver::dtls_transport()
|
||||
const {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
return dtls_transport_;
|
||||
}
|
||||
|
||||
std::vector<std::string> AudioRtpReceiver::stream_ids() const {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
std::vector<std::string> stream_ids(streams_.size());
|
||||
for (size_t i = 0; i < streams_.size(); ++i)
|
||||
stream_ids[i] = streams_[i]->id();
|
||||
return stream_ids;
|
||||
}
|
||||
|
||||
std::vector<rtc::scoped_refptr<MediaStreamInterface>>
|
||||
AudioRtpReceiver::streams() const {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
return streams_;
|
||||
}
|
||||
|
||||
RtpParameters AudioRtpReceiver::GetParameters() const {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
if (!media_channel_)
|
||||
return RtpParameters();
|
||||
auto current_ssrc = ssrc();
|
||||
return current_ssrc.has_value()
|
||||
? media_channel_->GetRtpReceiverParameters(current_ssrc.value())
|
||||
: media_channel_->GetDefaultRtpReceiveParameters();
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetFrameDecryptor(
|
||||
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
frame_decryptor_ = std::move(frame_decryptor);
|
||||
// Special Case: Set the frame decryptor to any value on any existing channel.
|
||||
if (media_channel_ && signaled_ssrc_) {
|
||||
media_channel_->SetFrameDecryptor(*signaled_ssrc_, frame_decryptor_);
|
||||
}
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<FrameDecryptorInterface>
|
||||
AudioRtpReceiver::GetFrameDecryptor() const {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
return frame_decryptor_;
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::Stop() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
source_->SetState(MediaSourceInterface::kEnded);
|
||||
track_->internal()->set_ended();
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::RestartMediaChannel(absl::optional<uint32_t> ssrc) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
bool enabled = track_->internal()->enabled();
|
||||
MediaSourceInterface::SourceState state = source_->state();
|
||||
worker_thread_->BlockingCall([&]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
RestartMediaChannel_w(std::move(ssrc), enabled, state);
|
||||
});
|
||||
source_->SetState(MediaSourceInterface::kLive);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::RestartMediaChannel_w(
|
||||
absl::optional<uint32_t> ssrc,
|
||||
bool track_enabled,
|
||||
MediaSourceInterface::SourceState state) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
if (!media_channel_)
|
||||
return; // Can't restart.
|
||||
|
||||
// Make sure the safety flag is marked as `alive` for cases where the media
|
||||
// channel was provided via the ctor and not an explicit call to
|
||||
// SetMediaChannel.
|
||||
worker_thread_safety_->SetAlive();
|
||||
|
||||
if (state != MediaSourceInterface::kInitializing) {
|
||||
if (signaled_ssrc_ == ssrc)
|
||||
return;
|
||||
source_->Stop(media_channel_, signaled_ssrc_);
|
||||
}
|
||||
|
||||
signaled_ssrc_ = std::move(ssrc);
|
||||
source_->Start(media_channel_, signaled_ssrc_);
|
||||
if (signaled_ssrc_) {
|
||||
media_channel_->SetBaseMinimumPlayoutDelayMs(*signaled_ssrc_,
|
||||
delay_.GetMs());
|
||||
}
|
||||
|
||||
Reconfigure(track_enabled);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
RestartMediaChannel(ssrc);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetupUnsignaledMediaChannel() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
RestartMediaChannel(absl::nullopt);
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> AudioRtpReceiver::ssrc() const {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
if (!signaled_ssrc_.has_value() && media_channel_) {
|
||||
return media_channel_->GetUnsignaledSsrc();
|
||||
}
|
||||
return signaled_ssrc_;
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
SetStreams(CreateStreamsFromIds(std::move(stream_ids)));
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::set_transport(
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
dtls_transport_ = std::move(dtls_transport);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetStreams(
|
||||
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
// Remove remote track from any streams that are going away.
|
||||
for (const auto& existing_stream : streams_) {
|
||||
bool removed = true;
|
||||
for (const auto& stream : streams) {
|
||||
if (existing_stream->id() == stream->id()) {
|
||||
RTC_DCHECK_EQ(existing_stream.get(), stream.get());
|
||||
removed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
existing_stream->RemoveTrack(audio_track());
|
||||
}
|
||||
}
|
||||
// Add remote track to any streams that are new.
|
||||
for (const auto& stream : streams) {
|
||||
bool added = true;
|
||||
for (const auto& existing_stream : streams_) {
|
||||
if (stream->id() == existing_stream->id()) {
|
||||
RTC_DCHECK_EQ(stream.get(), existing_stream.get());
|
||||
added = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (added) {
|
||||
stream->AddTrack(audio_track());
|
||||
}
|
||||
}
|
||||
streams_ = streams;
|
||||
}
|
||||
|
||||
std::vector<RtpSource> AudioRtpReceiver::GetSources() const {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
auto current_ssrc = ssrc();
|
||||
if (!media_channel_ || !current_ssrc.has_value()) {
|
||||
return {};
|
||||
}
|
||||
return media_channel_->GetSources(current_ssrc.value());
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetDepacketizerToDecoderFrameTransformer(
|
||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
if (media_channel_) {
|
||||
media_channel_->SetDepacketizerToDecoderFrameTransformer(
|
||||
signaled_ssrc_.value_or(0), frame_transformer);
|
||||
}
|
||||
frame_transformer_ = std::move(frame_transformer);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::Reconfigure(bool track_enabled) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
RTC_DCHECK(media_channel_);
|
||||
|
||||
SetOutputVolume_w(track_enabled ? cached_volume_ : 0);
|
||||
|
||||
if (signaled_ssrc_ && frame_decryptor_) {
|
||||
// Reattach the frame decryptor if we were reconfigured.
|
||||
media_channel_->SetFrameDecryptor(*signaled_ssrc_, frame_decryptor_);
|
||||
}
|
||||
|
||||
if (frame_transformer_) {
|
||||
media_channel_->SetDepacketizerToDecoderFrameTransformer(
|
||||
signaled_ssrc_.value_or(0), frame_transformer_);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
observer_ = observer;
|
||||
// Deliver any notifications the observer may have missed by being set late.
|
||||
if (received_first_packet_ && observer_) {
|
||||
observer_->OnFirstPacketReceived(media_type());
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetJitterBufferMinimumDelay(
|
||||
absl::optional<double> delay_seconds) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
delay_.Set(delay_seconds);
|
||||
if (media_channel_ && signaled_ssrc_)
|
||||
media_channel_->SetBaseMinimumPlayoutDelayMs(*signaled_ssrc_,
|
||||
delay_.GetMs());
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::SetMediaChannel(
|
||||
cricket::MediaReceiveChannelInterface* media_channel) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
RTC_DCHECK(media_channel == nullptr ||
|
||||
media_channel->media_type() == media_type());
|
||||
if (!media_channel && media_channel_)
|
||||
SetOutputVolume_w(0.0);
|
||||
|
||||
media_channel ? worker_thread_safety_->SetAlive()
|
||||
: worker_thread_safety_->SetNotAlive();
|
||||
media_channel_ =
|
||||
static_cast<cricket::VoiceMediaReceiveChannelInterface*>(media_channel);
|
||||
}
|
||||
|
||||
void AudioRtpReceiver::NotifyFirstPacketReceived() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
if (observer_) {
|
||||
observer_->OnFirstPacketReceived(media_type());
|
||||
}
|
||||
received_first_packet_ = true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
164
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver.h
Normal file
164
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver.h
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_AUDIO_RTP_RECEIVER_H_
|
||||
#define PC_AUDIO_RTP_RECEIVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/crypto/frame_decryptor_interface.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/frame_transformer_interface.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/transport/rtp/rtp_source.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "pc/audio_track.h"
|
||||
#include "pc/jitter_buffer_delay.h"
|
||||
#include "pc/media_stream_track_proxy.h"
|
||||
#include "pc/remote_audio_source.h"
|
||||
#include "pc/rtp_receiver.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AudioRtpReceiver : public ObserverInterface,
|
||||
public AudioSourceInterface::AudioObserver,
|
||||
public RtpReceiverInternal {
|
||||
public:
|
||||
// The constructor supports optionally passing the voice channel to the
|
||||
// instance at construction time without having to call `SetMediaChannel()`
|
||||
// on the worker thread straight after construction.
|
||||
// However, when using that, the assumption is that right after construction,
|
||||
// a call to either `SetupUnsignaledMediaChannel` or `SetupMediaChannel`
|
||||
// will be made, which will internally start the source on the worker thread.
|
||||
AudioRtpReceiver(
|
||||
rtc::Thread* worker_thread,
|
||||
std::string receiver_id,
|
||||
std::vector<std::string> stream_ids,
|
||||
bool is_unified_plan,
|
||||
cricket::VoiceMediaReceiveChannelInterface* voice_channel = nullptr);
|
||||
// TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed.
|
||||
AudioRtpReceiver(
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& receiver_id,
|
||||
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
|
||||
bool is_unified_plan,
|
||||
cricket::VoiceMediaReceiveChannelInterface* media_channel = nullptr);
|
||||
virtual ~AudioRtpReceiver();
|
||||
|
||||
// ObserverInterface implementation
|
||||
void OnChanged() override;
|
||||
|
||||
// AudioSourceInterface::AudioObserver implementation
|
||||
void OnSetVolume(double volume) override;
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> audio_track() const { return track_; }
|
||||
|
||||
// RtpReceiverInterface implementation
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
|
||||
return track_;
|
||||
}
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override;
|
||||
std::vector<std::string> stream_ids() const override;
|
||||
std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams()
|
||||
const override;
|
||||
|
||||
cricket::MediaType media_type() const override {
|
||||
return cricket::MEDIA_TYPE_AUDIO;
|
||||
}
|
||||
|
||||
std::string id() const override { return id_; }
|
||||
|
||||
RtpParameters GetParameters() const override;
|
||||
|
||||
void SetFrameDecryptor(
|
||||
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) override;
|
||||
|
||||
rtc::scoped_refptr<FrameDecryptorInterface> GetFrameDecryptor()
|
||||
const override;
|
||||
|
||||
// RtpReceiverInternal implementation.
|
||||
void Stop() override;
|
||||
void SetupMediaChannel(uint32_t ssrc) override;
|
||||
void SetupUnsignaledMediaChannel() override;
|
||||
absl::optional<uint32_t> ssrc() const override;
|
||||
void NotifyFirstPacketReceived() override;
|
||||
void set_stream_ids(std::vector<std::string> stream_ids) override;
|
||||
void set_transport(
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport) override;
|
||||
void SetStreams(const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
|
||||
streams) override;
|
||||
void SetObserver(RtpReceiverObserverInterface* observer) override;
|
||||
|
||||
void SetJitterBufferMinimumDelay(
|
||||
absl::optional<double> delay_seconds) override;
|
||||
|
||||
void SetMediaChannel(
|
||||
cricket::MediaReceiveChannelInterface* media_channel) override;
|
||||
|
||||
std::vector<RtpSource> GetSources() const override;
|
||||
int AttachmentId() const override { return attachment_id_; }
|
||||
void SetDepacketizerToDecoderFrameTransformer(
|
||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) override;
|
||||
|
||||
private:
|
||||
void RestartMediaChannel(absl::optional<uint32_t> ssrc)
|
||||
RTC_RUN_ON(&signaling_thread_checker_);
|
||||
void RestartMediaChannel_w(absl::optional<uint32_t> ssrc,
|
||||
bool track_enabled,
|
||||
MediaSourceInterface::SourceState state)
|
||||
RTC_RUN_ON(worker_thread_);
|
||||
void Reconfigure(bool track_enabled) RTC_RUN_ON(worker_thread_);
|
||||
void SetOutputVolume_w(double volume) RTC_RUN_ON(worker_thread_);
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_;
|
||||
rtc::Thread* const worker_thread_;
|
||||
const std::string id_;
|
||||
const rtc::scoped_refptr<RemoteAudioSource> source_;
|
||||
const rtc::scoped_refptr<AudioTrackProxyWithInternal<AudioTrack>> track_;
|
||||
cricket::VoiceMediaReceiveChannelInterface* media_channel_
|
||||
RTC_GUARDED_BY(worker_thread_) = nullptr;
|
||||
absl::optional<uint32_t> signaled_ssrc_ RTC_GUARDED_BY(worker_thread_);
|
||||
std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_
|
||||
RTC_GUARDED_BY(&signaling_thread_checker_);
|
||||
bool cached_track_enabled_ RTC_GUARDED_BY(&signaling_thread_checker_);
|
||||
double cached_volume_ RTC_GUARDED_BY(worker_thread_) = 1.0;
|
||||
RtpReceiverObserverInterface* observer_
|
||||
RTC_GUARDED_BY(&signaling_thread_checker_) = nullptr;
|
||||
bool received_first_packet_ RTC_GUARDED_BY(&signaling_thread_checker_) =
|
||||
false;
|
||||
const int attachment_id_;
|
||||
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_
|
||||
RTC_GUARDED_BY(&signaling_thread_checker_);
|
||||
// Stores and updates the playout delay. Handles caching cases if
|
||||
// `SetJitterBufferMinimumDelay` is called before start.
|
||||
JitterBufferDelay delay_ RTC_GUARDED_BY(worker_thread_);
|
||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
const rtc::scoped_refptr<PendingTaskSafetyFlag> worker_thread_safety_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_AUDIO_RTP_RECEIVER_H_
|
||||
127
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver_unittest.cc
Normal file
127
TMessagesProj/jni/voip/webrtc/pc/audio_rtp_receiver_unittest.cc
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2021 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 "pc/audio_rtp_receiver.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "pc/test/mock_voice_media_receive_channel_interface.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/run_loop.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::InvokeWithoutArgs;
|
||||
using ::testing::Mock;
|
||||
|
||||
static const int kTimeOut = 100;
|
||||
static const double kDefaultVolume = 1;
|
||||
static const double kVolume = 3.7;
|
||||
static const double kVolumeMuted = 0.0;
|
||||
static const uint32_t kSsrc = 3;
|
||||
|
||||
namespace webrtc {
|
||||
class AudioRtpReceiverTest : public ::testing::Test {
|
||||
protected:
|
||||
AudioRtpReceiverTest()
|
||||
: worker_(rtc::Thread::Current()),
|
||||
receiver_(
|
||||
rtc::make_ref_counted<AudioRtpReceiver>(worker_,
|
||||
std::string(),
|
||||
std::vector<std::string>(),
|
||||
false)) {
|
||||
EXPECT_CALL(receive_channel_, SetRawAudioSink(kSsrc, _));
|
||||
EXPECT_CALL(receive_channel_, SetBaseMinimumPlayoutDelayMs(kSsrc, _));
|
||||
}
|
||||
|
||||
~AudioRtpReceiverTest() {
|
||||
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolumeMuted));
|
||||
receiver_->SetMediaChannel(nullptr);
|
||||
}
|
||||
|
||||
rtc::AutoThread main_thread_;
|
||||
rtc::Thread* worker_;
|
||||
rtc::scoped_refptr<AudioRtpReceiver> receiver_;
|
||||
cricket::MockVoiceMediaReceiveChannelInterface receive_channel_;
|
||||
};
|
||||
|
||||
TEST_F(AudioRtpReceiverTest, SetOutputVolumeIsCalled) {
|
||||
std::atomic_int set_volume_calls(0);
|
||||
|
||||
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kDefaultVolume))
|
||||
.WillOnce(InvokeWithoutArgs([&] {
|
||||
set_volume_calls++;
|
||||
return true;
|
||||
}));
|
||||
|
||||
receiver_->track();
|
||||
receiver_->track()->set_enabled(true);
|
||||
receiver_->SetMediaChannel(&receive_channel_);
|
||||
EXPECT_CALL(receive_channel_, SetDefaultRawAudioSink(_)).Times(0);
|
||||
receiver_->SetupMediaChannel(kSsrc);
|
||||
|
||||
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume))
|
||||
.WillOnce(InvokeWithoutArgs([&] {
|
||||
set_volume_calls++;
|
||||
return true;
|
||||
}));
|
||||
|
||||
receiver_->OnSetVolume(kVolume);
|
||||
EXPECT_TRUE_WAIT(set_volume_calls == 2, kTimeOut);
|
||||
}
|
||||
|
||||
TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) {
|
||||
// Set the volume before setting the media channel. It should still be used
|
||||
// as the initial volume.
|
||||
receiver_->OnSetVolume(kVolume);
|
||||
|
||||
receiver_->track()->set_enabled(true);
|
||||
receiver_->SetMediaChannel(&receive_channel_);
|
||||
|
||||
// The previosly set initial volume should be propagated to the provided
|
||||
// media_channel_ as soon as SetupMediaChannel is called.
|
||||
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume));
|
||||
|
||||
receiver_->SetupMediaChannel(kSsrc);
|
||||
}
|
||||
|
||||
// Tests that OnChanged notifications are processed correctly on the worker
|
||||
// thread when a media channel pointer is passed to the receiver via the
|
||||
// constructor.
|
||||
TEST(AudioRtpReceiver, OnChangedNotificationsAfterConstruction) {
|
||||
test::RunLoop loop;
|
||||
auto* thread = rtc::Thread::Current(); // Points to loop's thread.
|
||||
cricket::MockVoiceMediaReceiveChannelInterface receive_channel;
|
||||
auto receiver = rtc::make_ref_counted<AudioRtpReceiver>(
|
||||
thread, std::string(), std::vector<std::string>(), true,
|
||||
&receive_channel);
|
||||
|
||||
EXPECT_CALL(receive_channel, SetDefaultRawAudioSink(_)).Times(1);
|
||||
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kDefaultVolume)).Times(1);
|
||||
receiver->SetupUnsignaledMediaChannel();
|
||||
loop.Flush();
|
||||
|
||||
// Mark the track as disabled.
|
||||
receiver->track()->set_enabled(false);
|
||||
|
||||
// When the track was marked as disabled, an async notification was queued
|
||||
// for the worker thread. This notification should trigger the volume
|
||||
// of the media channel to be set to kVolumeMuted.
|
||||
// Flush the worker thread, but set the expectation first for the call.
|
||||
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1);
|
||||
loop.Flush();
|
||||
|
||||
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1);
|
||||
receiver->SetMediaChannel(nullptr);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
70
TMessagesProj/jni/voip/webrtc/pc/audio_track.cc
Normal file
70
TMessagesProj/jni/voip/webrtc/pc/audio_track.cc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2011 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 "pc/audio_track.h"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<AudioTrack> AudioTrack::Create(
|
||||
absl::string_view id,
|
||||
const rtc::scoped_refptr<AudioSourceInterface>& source) {
|
||||
return rtc::make_ref_counted<AudioTrack>(id, source);
|
||||
}
|
||||
|
||||
AudioTrack::AudioTrack(absl::string_view label,
|
||||
const rtc::scoped_refptr<AudioSourceInterface>& source)
|
||||
: MediaStreamTrack<AudioTrackInterface>(label), audio_source_(source) {
|
||||
if (audio_source_) {
|
||||
audio_source_->RegisterObserver(this);
|
||||
OnChanged();
|
||||
}
|
||||
}
|
||||
|
||||
AudioTrack::~AudioTrack() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
set_state(MediaStreamTrackInterface::kEnded);
|
||||
if (audio_source_)
|
||||
audio_source_->UnregisterObserver(this);
|
||||
}
|
||||
|
||||
std::string AudioTrack::kind() const {
|
||||
return kAudioKind;
|
||||
}
|
||||
|
||||
AudioSourceInterface* AudioTrack::GetSource() const {
|
||||
// Callable from any thread.
|
||||
return audio_source_.get();
|
||||
}
|
||||
|
||||
void AudioTrack::AddSink(AudioTrackSinkInterface* sink) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
if (audio_source_)
|
||||
audio_source_->AddSink(sink);
|
||||
}
|
||||
|
||||
void AudioTrack::RemoveSink(AudioTrackSinkInterface* sink) {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
if (audio_source_)
|
||||
audio_source_->RemoveSink(sink);
|
||||
}
|
||||
|
||||
void AudioTrack::OnChanged() {
|
||||
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
|
||||
if (audio_source_->state() == MediaSourceInterface::kEnded) {
|
||||
set_state(kEnded);
|
||||
} else {
|
||||
set_state(kLive);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
66
TMessagesProj/jni/voip/webrtc/pc/audio_track.h
Normal file
66
TMessagesProj/jni/voip/webrtc/pc/audio_track.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_AUDIO_TRACK_H_
|
||||
#define PC_AUDIO_TRACK_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/media_stream_track.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(tommi): Instead of inheriting from `MediaStreamTrack<>`, implement the
|
||||
// properties directly in this class. `MediaStreamTrack` doesn't guard against
|
||||
// conflicting access, so we'd need to override those methods anyway in this
|
||||
// class in order to make sure things are correctly checked.
|
||||
class AudioTrack : public MediaStreamTrack<AudioTrackInterface>,
|
||||
public ObserverInterface {
|
||||
protected:
|
||||
// Protected ctor to force use of factory method.
|
||||
AudioTrack(absl::string_view label,
|
||||
const rtc::scoped_refptr<AudioSourceInterface>& source);
|
||||
|
||||
AudioTrack() = delete;
|
||||
AudioTrack(const AudioTrack&) = delete;
|
||||
AudioTrack& operator=(const AudioTrack&) = delete;
|
||||
|
||||
~AudioTrack() override;
|
||||
|
||||
public:
|
||||
static rtc::scoped_refptr<AudioTrack> Create(
|
||||
absl::string_view id,
|
||||
const rtc::scoped_refptr<AudioSourceInterface>& source);
|
||||
|
||||
// MediaStreamTrack implementation.
|
||||
std::string kind() const override;
|
||||
|
||||
// AudioTrackInterface implementation.
|
||||
AudioSourceInterface* GetSource() const override;
|
||||
|
||||
void AddSink(AudioTrackSinkInterface* sink) override;
|
||||
void RemoveSink(AudioTrackSinkInterface* sink) override;
|
||||
|
||||
private:
|
||||
// ObserverInterface implementation.
|
||||
void OnChanged() override;
|
||||
|
||||
private:
|
||||
const rtc::scoped_refptr<AudioSourceInterface> audio_source_;
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_AUDIO_TRACK_H_
|
||||
1217
TMessagesProj/jni/voip/webrtc/pc/channel.cc
Normal file
1217
TMessagesProj/jni/voip/webrtc/pc/channel.cc
Normal file
File diff suppressed because it is too large
Load diff
507
TMessagesProj/jni/voip/webrtc/pc/channel.h
Normal file
507
TMessagesProj/jni/voip/webrtc/pc/channel.h
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright 2004 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_CHANNEL_H_
|
||||
#define PC_CHANNEL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "call/rtp_demuxer.h"
|
||||
#include "call/rtp_packet_sink_interface.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "media/base/media_channel_impl.h"
|
||||
#include "media/base/stream_params.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "pc/channel_interface.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/containers/flat_set.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/network/sent_packet.h"
|
||||
#include "rtc_base/network_route.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/unique_id_generator.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// BaseChannel contains logic common to voice and video, including enable,
|
||||
// marshaling calls to a worker and network threads, and connection and media
|
||||
// monitors.
|
||||
//
|
||||
// BaseChannel assumes signaling and other threads are allowed to make
|
||||
// synchronous calls to the worker thread, the worker thread makes synchronous
|
||||
// calls only to the network thread, and the network thread can't be blocked by
|
||||
// other threads.
|
||||
// All methods with _n suffix must be called on network thread,
|
||||
// methods with _w suffix on worker thread
|
||||
// and methods with _s suffix on signaling thread.
|
||||
// Network and worker threads may be the same thread.
|
||||
//
|
||||
class VideoChannel;
|
||||
class VoiceChannel;
|
||||
|
||||
class BaseChannel : public ChannelInterface,
|
||||
// TODO(tommi): Consider implementing these interfaces
|
||||
// via composition.
|
||||
public MediaChannelNetworkInterface,
|
||||
public webrtc::RtpPacketSinkInterface {
|
||||
public:
|
||||
// If `srtp_required` is true, the channel will not send or receive any
|
||||
// RTP/RTCP packets without using SRTP (either using SDES or DTLS-SRTP).
|
||||
// The BaseChannel does not own the UniqueRandomIdGenerator so it is the
|
||||
// responsibility of the user to ensure it outlives this object.
|
||||
// TODO(zhihuang:) Create a BaseChannel::Config struct for the parameter lists
|
||||
// which will make it easier to change the constructor.
|
||||
|
||||
// Constructor for use when the MediaChannels are split
|
||||
BaseChannel(
|
||||
webrtc::TaskQueueBase* worker_thread,
|
||||
rtc::Thread* network_thread,
|
||||
webrtc::TaskQueueBase* signaling_thread,
|
||||
std::unique_ptr<MediaSendChannelInterface> media_send_channel,
|
||||
std::unique_ptr<MediaReceiveChannelInterface> media_receive_channel,
|
||||
absl::string_view mid,
|
||||
bool srtp_required,
|
||||
webrtc::CryptoOptions crypto_options,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||
virtual ~BaseChannel();
|
||||
|
||||
webrtc::TaskQueueBase* worker_thread() const { return worker_thread_; }
|
||||
rtc::Thread* network_thread() const { return network_thread_; }
|
||||
const std::string& mid() const override { return demuxer_criteria_.mid(); }
|
||||
// TODO(deadbeef): This is redundant; remove this.
|
||||
absl::string_view transport_name() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (rtp_transport_)
|
||||
return rtp_transport_->transport_name();
|
||||
return "";
|
||||
}
|
||||
|
||||
// This function returns true if using SRTP (DTLS-based keying or SDES).
|
||||
bool srtp_active() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
return rtp_transport_ && rtp_transport_->IsSrtpActive();
|
||||
}
|
||||
|
||||
// Set an RTP level transport which could be an RtpTransport without
|
||||
// encryption, an SrtpTransport for SDES or a DtlsSrtpTransport for DTLS-SRTP.
|
||||
// This can be called from any thread and it hops to the network thread
|
||||
// internally. It would replace the `SetTransports` and its variants.
|
||||
bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) override;
|
||||
|
||||
webrtc::RtpTransportInternal* rtp_transport() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
return rtp_transport_;
|
||||
}
|
||||
|
||||
// Channel control
|
||||
bool SetLocalContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc) override;
|
||||
bool SetRemoteContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc) override;
|
||||
// Controls whether this channel will receive packets on the basis of
|
||||
// matching payload type alone. This is needed for legacy endpoints that
|
||||
// don't signal SSRCs or use MID/RID, but doesn't make sense if there is
|
||||
// more than channel of specific media type, As that creates an ambiguity.
|
||||
//
|
||||
// This method will also remove any existing streams that were bound to this
|
||||
// channel on the basis of payload type, since one of these streams might
|
||||
// actually belong to a new channel. See: crbug.com/webrtc/11477
|
||||
bool SetPayloadTypeDemuxingEnabled(bool enabled) override;
|
||||
|
||||
void Enable(bool enable) override;
|
||||
|
||||
const std::vector<StreamParams>& local_streams() const override {
|
||||
return local_streams_;
|
||||
}
|
||||
const std::vector<StreamParams>& remote_streams() const override {
|
||||
return remote_streams_;
|
||||
}
|
||||
|
||||
// Used for latency measurements.
|
||||
void SetFirstPacketReceivedCallback(std::function<void()> callback) override;
|
||||
|
||||
// From RtpTransport - public for testing only
|
||||
void OnTransportReadyToSend(bool ready);
|
||||
|
||||
// Only public for unit tests. Otherwise, consider protected.
|
||||
int SetOption(SocketType type, rtc::Socket::Option o, int val) override;
|
||||
|
||||
// RtpPacketSinkInterface overrides.
|
||||
void OnRtpPacket(const webrtc::RtpPacketReceived& packet) override;
|
||||
|
||||
VideoMediaSendChannelInterface* video_media_send_channel() override {
|
||||
RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
|
||||
return nullptr;
|
||||
}
|
||||
VoiceMediaSendChannelInterface* voice_media_send_channel() override {
|
||||
RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
|
||||
return nullptr;
|
||||
}
|
||||
VideoMediaReceiveChannelInterface* video_media_receive_channel() override {
|
||||
RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
|
||||
return nullptr;
|
||||
}
|
||||
VoiceMediaReceiveChannelInterface* voice_media_receive_channel() override {
|
||||
RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
void set_local_content_direction(webrtc::RtpTransceiverDirection direction)
|
||||
RTC_RUN_ON(worker_thread()) {
|
||||
local_content_direction_ = direction;
|
||||
}
|
||||
|
||||
webrtc::RtpTransceiverDirection local_content_direction() const
|
||||
RTC_RUN_ON(worker_thread()) {
|
||||
return local_content_direction_;
|
||||
}
|
||||
|
||||
void set_remote_content_direction(webrtc::RtpTransceiverDirection direction)
|
||||
RTC_RUN_ON(worker_thread()) {
|
||||
remote_content_direction_ = direction;
|
||||
}
|
||||
|
||||
webrtc::RtpTransceiverDirection remote_content_direction() const
|
||||
RTC_RUN_ON(worker_thread()) {
|
||||
return remote_content_direction_;
|
||||
}
|
||||
|
||||
webrtc::RtpExtension::Filter extensions_filter() const {
|
||||
return extensions_filter_;
|
||||
}
|
||||
|
||||
bool network_initialized() RTC_RUN_ON(network_thread()) {
|
||||
return media_send_channel()->HasNetworkInterface();
|
||||
}
|
||||
|
||||
bool enabled() const RTC_RUN_ON(worker_thread()) { return enabled_; }
|
||||
webrtc::TaskQueueBase* signaling_thread() const { return signaling_thread_; }
|
||||
|
||||
// Call to verify that:
|
||||
// * The required content description directions have been set.
|
||||
// * The channel is enabled.
|
||||
// * The SRTP filter is active if it's needed.
|
||||
// * The transport has been writable before, meaning it should be at least
|
||||
// possible to succeed in sending a packet.
|
||||
//
|
||||
// When any of these properties change, UpdateMediaSendRecvState_w should be
|
||||
// called.
|
||||
bool IsReadyToSendMedia_w() const RTC_RUN_ON(worker_thread());
|
||||
|
||||
// NetworkInterface implementation, called by MediaEngine
|
||||
bool SendPacket(rtc::CopyOnWriteBuffer* packet,
|
||||
const rtc::PacketOptions& options) override;
|
||||
bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
|
||||
const rtc::PacketOptions& options) override;
|
||||
|
||||
// From RtpTransportInternal
|
||||
void OnWritableState(bool writable);
|
||||
|
||||
void OnNetworkRouteChanged(absl::optional<rtc::NetworkRoute> network_route);
|
||||
|
||||
bool SendPacket(bool rtcp,
|
||||
rtc::CopyOnWriteBuffer* packet,
|
||||
const rtc::PacketOptions& options);
|
||||
|
||||
void EnableMedia_w() RTC_RUN_ON(worker_thread());
|
||||
void DisableMedia_w() RTC_RUN_ON(worker_thread());
|
||||
|
||||
// Performs actions if the RTP/RTCP writable state changed. This should
|
||||
// be called whenever a channel's writable state changes or when RTCP muxing
|
||||
// becomes active/inactive.
|
||||
void UpdateWritableState_n() RTC_RUN_ON(network_thread());
|
||||
void ChannelWritable_n() RTC_RUN_ON(network_thread());
|
||||
void ChannelNotWritable_n() RTC_RUN_ON(network_thread());
|
||||
|
||||
bool SetPayloadTypeDemuxingEnabled_w(bool enabled)
|
||||
RTC_RUN_ON(worker_thread());
|
||||
|
||||
// Should be called whenever the conditions for
|
||||
// IsReadyToReceiveMedia/IsReadyToSendMedia are satisfied (or unsatisfied).
|
||||
// Updates the send/recv state of the media channel.
|
||||
virtual void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) = 0;
|
||||
|
||||
bool UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread());
|
||||
bool UpdateRemoteStreams_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread());
|
||||
virtual bool SetLocalContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) = 0;
|
||||
virtual bool SetRemoteContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) = 0;
|
||||
|
||||
// Returns a list of RTP header extensions where any extension URI is unique.
|
||||
// Encrypted extensions will be either preferred or discarded, depending on
|
||||
// the current crypto_options_.
|
||||
RtpHeaderExtensions GetDeduplicatedRtpHeaderExtensions(
|
||||
const RtpHeaderExtensions& extensions);
|
||||
|
||||
// Add `payload_type` to `demuxer_criteria_` if payload type demuxing is
|
||||
// enabled.
|
||||
// Returns true if the demuxer payload type changed and a re-registration
|
||||
// is needed.
|
||||
bool MaybeAddHandledPayloadType(int payload_type) RTC_RUN_ON(worker_thread());
|
||||
|
||||
// Returns true if the demuxer payload type criteria was non-empty before
|
||||
// clearing.
|
||||
bool ClearHandledPayloadTypes() RTC_RUN_ON(worker_thread());
|
||||
|
||||
// Hops to the network thread to update the transport if an update is
|
||||
// requested. If `update_demuxer` is false and `extensions` is not set, the
|
||||
// function simply returns. If either of these is set, the function updates
|
||||
// the transport with either or both of the demuxer criteria and the supplied
|
||||
// rtp header extensions.
|
||||
// Returns `true` if either an update wasn't needed or one was successfully
|
||||
// applied. If the return value is `false`, then updating the demuxer criteria
|
||||
// failed, which needs to be treated as an error.
|
||||
bool MaybeUpdateDemuxerAndRtpExtensions_w(
|
||||
bool update_demuxer,
|
||||
absl::optional<RtpHeaderExtensions> extensions,
|
||||
std::string& error_desc) RTC_RUN_ON(worker_thread());
|
||||
|
||||
bool RegisterRtpDemuxerSink_w() RTC_RUN_ON(worker_thread());
|
||||
|
||||
// Return description of media channel to facilitate logging
|
||||
std::string ToString() const;
|
||||
|
||||
const std::unique_ptr<MediaSendChannelInterface> media_send_channel_;
|
||||
const std::unique_ptr<MediaReceiveChannelInterface> media_receive_channel_;
|
||||
|
||||
private:
|
||||
bool ConnectToRtpTransport_n() RTC_RUN_ON(network_thread());
|
||||
void DisconnectFromRtpTransport_n() RTC_RUN_ON(network_thread());
|
||||
void SignalSentPacket_n(const rtc::SentPacket& sent_packet);
|
||||
|
||||
webrtc::TaskQueueBase* const worker_thread_;
|
||||
rtc::Thread* const network_thread_;
|
||||
webrtc::TaskQueueBase* const signaling_thread_;
|
||||
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> alive_;
|
||||
|
||||
std::function<void()> on_first_packet_received_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
|
||||
webrtc::RtpTransportInternal* rtp_transport_
|
||||
RTC_GUARDED_BY(network_thread()) = nullptr;
|
||||
|
||||
std::vector<std::pair<rtc::Socket::Option, int> > socket_options_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
bool writable_ RTC_GUARDED_BY(network_thread()) = false;
|
||||
bool was_ever_writable_n_ RTC_GUARDED_BY(network_thread()) = false;
|
||||
bool was_ever_writable_ RTC_GUARDED_BY(worker_thread()) = false;
|
||||
const bool srtp_required_ = true;
|
||||
|
||||
// Set to either kPreferEncryptedExtension or kDiscardEncryptedExtension
|
||||
// based on the supplied CryptoOptions.
|
||||
const webrtc::RtpExtension::Filter extensions_filter_;
|
||||
|
||||
// Currently the `enabled_` flag is accessed from the signaling thread as
|
||||
// well, but it can be changed only when signaling thread does a synchronous
|
||||
// call to the worker thread, so it should be safe.
|
||||
bool enabled_ RTC_GUARDED_BY(worker_thread()) = false;
|
||||
bool enabled_s_ RTC_GUARDED_BY(signaling_thread()) = false;
|
||||
bool payload_type_demuxing_enabled_ RTC_GUARDED_BY(worker_thread()) = true;
|
||||
std::vector<StreamParams> local_streams_ RTC_GUARDED_BY(worker_thread());
|
||||
std::vector<StreamParams> remote_streams_ RTC_GUARDED_BY(worker_thread());
|
||||
webrtc::RtpTransceiverDirection local_content_direction_ RTC_GUARDED_BY(
|
||||
worker_thread()) = webrtc::RtpTransceiverDirection::kInactive;
|
||||
webrtc::RtpTransceiverDirection remote_content_direction_ RTC_GUARDED_BY(
|
||||
worker_thread()) = webrtc::RtpTransceiverDirection::kInactive;
|
||||
|
||||
// Cached list of payload types, used if payload type demuxing is re-enabled.
|
||||
webrtc::flat_set<uint8_t> payload_types_ RTC_GUARDED_BY(worker_thread());
|
||||
// A stored copy of the rtp header extensions as applied to the transport.
|
||||
RtpHeaderExtensions rtp_header_extensions_ RTC_GUARDED_BY(worker_thread());
|
||||
// TODO(bugs.webrtc.org/12239): Modified on worker thread, accessed
|
||||
// on network thread in RegisterRtpDemuxerSink_n (called from Init_w)
|
||||
webrtc::RtpDemuxerCriteria demuxer_criteria_;
|
||||
// This generator is used to generate SSRCs for local streams.
|
||||
// This is needed in cases where SSRCs are not negotiated or set explicitly
|
||||
// like in Simulcast.
|
||||
// This object is not owned by the channel so it must outlive it.
|
||||
rtc::UniqueRandomIdGenerator* const ssrc_generator_;
|
||||
};
|
||||
|
||||
// VoiceChannel is a specialization that adds support for early media, DTMF,
|
||||
// and input/output level monitoring.
|
||||
class VoiceChannel : public BaseChannel {
|
||||
public:
|
||||
VoiceChannel(
|
||||
webrtc::TaskQueueBase* worker_thread,
|
||||
rtc::Thread* network_thread,
|
||||
webrtc::TaskQueueBase* signaling_thread,
|
||||
std::unique_ptr<VoiceMediaSendChannelInterface> send_channel_impl,
|
||||
std::unique_ptr<VoiceMediaReceiveChannelInterface> receive_channel_impl,
|
||||
absl::string_view mid,
|
||||
bool srtp_required,
|
||||
webrtc::CryptoOptions crypto_options,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||
|
||||
~VoiceChannel();
|
||||
|
||||
VideoChannel* AsVideoChannel() override {
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
VoiceChannel* AsVoiceChannel() override { return this; }
|
||||
|
||||
VoiceMediaSendChannelInterface* send_channel() {
|
||||
return media_send_channel_->AsVoiceSendChannel();
|
||||
}
|
||||
|
||||
VoiceMediaReceiveChannelInterface* receive_channel() {
|
||||
return media_receive_channel_->AsVoiceReceiveChannel();
|
||||
}
|
||||
|
||||
VoiceMediaSendChannelInterface* media_send_channel() override {
|
||||
return send_channel();
|
||||
}
|
||||
|
||||
VoiceMediaSendChannelInterface* voice_media_send_channel() override {
|
||||
return send_channel();
|
||||
}
|
||||
|
||||
VoiceMediaReceiveChannelInterface* media_receive_channel() override {
|
||||
return receive_channel();
|
||||
}
|
||||
|
||||
VoiceMediaReceiveChannelInterface* voice_media_receive_channel() override {
|
||||
return receive_channel();
|
||||
}
|
||||
|
||||
cricket::MediaType media_type() const override {
|
||||
return cricket::MEDIA_TYPE_AUDIO;
|
||||
}
|
||||
|
||||
private:
|
||||
// overrides from BaseChannel
|
||||
void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override;
|
||||
bool SetLocalContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) override;
|
||||
bool SetRemoteContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) override;
|
||||
|
||||
// Last AudioSenderParameter sent down to the media_channel() via
|
||||
// SetSenderParameters.
|
||||
AudioSenderParameter last_send_params_ RTC_GUARDED_BY(worker_thread());
|
||||
// Last AudioReceiverParameters sent down to the media_channel() via
|
||||
// SetReceiverParameters.
|
||||
AudioReceiverParameters last_recv_params_ RTC_GUARDED_BY(worker_thread());
|
||||
};
|
||||
|
||||
// VideoChannel is a specialization for video.
|
||||
class VideoChannel : public BaseChannel {
|
||||
public:
|
||||
VideoChannel(
|
||||
webrtc::TaskQueueBase* worker_thread,
|
||||
rtc::Thread* network_thread,
|
||||
webrtc::TaskQueueBase* signaling_thread,
|
||||
std::unique_ptr<VideoMediaSendChannelInterface> media_send_channel,
|
||||
std::unique_ptr<VideoMediaReceiveChannelInterface> media_receive_channel,
|
||||
absl::string_view mid,
|
||||
bool srtp_required,
|
||||
webrtc::CryptoOptions crypto_options,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||
~VideoChannel();
|
||||
|
||||
VideoChannel* AsVideoChannel() override { return this; }
|
||||
VoiceChannel* AsVoiceChannel() override {
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VideoMediaSendChannelInterface* send_channel() {
|
||||
return media_send_channel_->AsVideoSendChannel();
|
||||
}
|
||||
|
||||
VideoMediaReceiveChannelInterface* receive_channel() {
|
||||
return media_receive_channel_->AsVideoReceiveChannel();
|
||||
}
|
||||
|
||||
VideoMediaSendChannelInterface* media_send_channel() override {
|
||||
return send_channel();
|
||||
}
|
||||
|
||||
VideoMediaSendChannelInterface* video_media_send_channel() override {
|
||||
return send_channel();
|
||||
}
|
||||
|
||||
VideoMediaReceiveChannelInterface* media_receive_channel() override {
|
||||
return receive_channel();
|
||||
}
|
||||
|
||||
VideoMediaReceiveChannelInterface* video_media_receive_channel() override {
|
||||
return receive_channel();
|
||||
}
|
||||
|
||||
cricket::MediaType media_type() const override {
|
||||
return cricket::MEDIA_TYPE_VIDEO;
|
||||
}
|
||||
|
||||
private:
|
||||
// overrides from BaseChannel
|
||||
void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override;
|
||||
bool SetLocalContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) override;
|
||||
bool SetRemoteContent_w(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc)
|
||||
RTC_RUN_ON(worker_thread()) override;
|
||||
|
||||
// Last VideoSenderParameters sent down to the media_channel() via
|
||||
// SetSenderParameters.
|
||||
VideoSenderParameters last_send_params_ RTC_GUARDED_BY(worker_thread());
|
||||
// Last VideoReceiverParameters sent down to the media_channel() via
|
||||
// SetReceiverParameters.
|
||||
VideoReceiverParameters last_recv_params_ RTC_GUARDED_BY(worker_thread());
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_CHANNEL_H_
|
||||
105
TMessagesProj/jni/voip/webrtc/pc/channel_interface.h
Normal file
105
TMessagesProj/jni/voip/webrtc/pc/channel_interface.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_CHANNEL_INTERFACE_H_
|
||||
#define PC_CHANNEL_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_types.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
|
||||
namespace webrtc {
|
||||
class Call;
|
||||
class VideoBitrateAllocatorFactory;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class VoiceChannel;
|
||||
class VideoChannel;
|
||||
class MediaContentDescription;
|
||||
struct MediaConfig;
|
||||
|
||||
// A Channel is a construct that groups media streams of the same type
|
||||
// (audio or video), both outgoing and incoming.
|
||||
// When the PeerConnection API is used, a Channel corresponds one to one
|
||||
// to an RtpTransceiver.
|
||||
// When Unified Plan is used, there can only be at most one outgoing and
|
||||
// one incoming stream. With Plan B, there can be more than one.
|
||||
|
||||
// ChannelInterface contains methods common to voice and video channels.
|
||||
// As more methods are added to BaseChannel, they should be included in the
|
||||
// interface as well.
|
||||
// TODO(bugs.webrtc.org/13931): Merge this class into RtpTransceiver.
|
||||
class ChannelInterface {
|
||||
public:
|
||||
virtual ~ChannelInterface() = default;
|
||||
virtual cricket::MediaType media_type() const = 0;
|
||||
|
||||
virtual VideoChannel* AsVideoChannel() = 0;
|
||||
virtual VoiceChannel* AsVoiceChannel() = 0;
|
||||
|
||||
virtual MediaSendChannelInterface* media_send_channel() = 0;
|
||||
// Typecasts of media_channel(). Will cause an exception if the
|
||||
// channel is of the wrong type.
|
||||
virtual VideoMediaSendChannelInterface* video_media_send_channel() = 0;
|
||||
virtual VoiceMediaSendChannelInterface* voice_media_send_channel() = 0;
|
||||
virtual MediaReceiveChannelInterface* media_receive_channel() = 0;
|
||||
// Typecasts of media_channel(). Will cause an exception if the
|
||||
// channel is of the wrong type.
|
||||
virtual VideoMediaReceiveChannelInterface* video_media_receive_channel() = 0;
|
||||
virtual VoiceMediaReceiveChannelInterface* voice_media_receive_channel() = 0;
|
||||
|
||||
// Returns a string view for the transport name. Fetching the transport name
|
||||
// must be done on the network thread only and note that the lifetime of
|
||||
// the returned object should be assumed to only be the calling scope.
|
||||
// TODO(deadbeef): This is redundant; remove this.
|
||||
virtual absl::string_view transport_name() const = 0;
|
||||
|
||||
// TODO(tommi): Change return type to string_view.
|
||||
virtual const std::string& mid() const = 0;
|
||||
|
||||
// Enables or disables this channel
|
||||
virtual void Enable(bool enable) = 0;
|
||||
|
||||
// Used for latency measurements.
|
||||
virtual void SetFirstPacketReceivedCallback(
|
||||
std::function<void()> callback) = 0;
|
||||
|
||||
// Channel control
|
||||
virtual bool SetLocalContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc) = 0;
|
||||
virtual bool SetRemoteContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string& error_desc) = 0;
|
||||
virtual bool SetPayloadTypeDemuxingEnabled(bool enabled) = 0;
|
||||
|
||||
// Access to the local and remote streams that were set on the channel.
|
||||
virtual const std::vector<StreamParams>& local_streams() const = 0;
|
||||
virtual const std::vector<StreamParams>& remote_streams() const = 0;
|
||||
|
||||
// Set an RTP level transport.
|
||||
// Some examples:
|
||||
// * An RtpTransport without encryption.
|
||||
// * An SrtpTransport for SDES.
|
||||
// * A DtlsSrtpTransport for DTLS-SRTP.
|
||||
virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_CHANNEL_INTERFACE_H_
|
||||
2525
TMessagesProj/jni/voip/webrtc/pc/channel_unittest.cc
Normal file
2525
TMessagesProj/jni/voip/webrtc/pc/channel_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
193
TMessagesProj/jni/voip/webrtc/pc/connection_context.cc
Normal file
193
TMessagesProj/jni/voip/webrtc/pc/connection_context.cc
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright 2020 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 "pc/connection_context.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "media/sctp/sctp_transport_factory.h"
|
||||
#include "pc/media_factory.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/internal/default_socket_server.h"
|
||||
#include "rtc_base/socket_server.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
rtc::Thread* MaybeStartNetworkThread(
|
||||
rtc::Thread* old_thread,
|
||||
std::unique_ptr<rtc::SocketFactory>& socket_factory_holder,
|
||||
std::unique_ptr<rtc::Thread>& thread_holder) {
|
||||
if (old_thread) {
|
||||
return old_thread;
|
||||
}
|
||||
std::unique_ptr<rtc::SocketServer> socket_server =
|
||||
rtc::CreateDefaultSocketServer();
|
||||
thread_holder = std::make_unique<rtc::Thread>(socket_server.get());
|
||||
socket_factory_holder = std::move(socket_server);
|
||||
|
||||
thread_holder->SetName("pc_network_thread", nullptr);
|
||||
thread_holder->Start();
|
||||
return thread_holder.get();
|
||||
}
|
||||
|
||||
rtc::Thread* MaybeWrapThread(rtc::Thread* signaling_thread,
|
||||
bool& wraps_current_thread) {
|
||||
wraps_current_thread = false;
|
||||
if (signaling_thread) {
|
||||
return signaling_thread;
|
||||
}
|
||||
auto this_thread = rtc::Thread::Current();
|
||||
if (!this_thread) {
|
||||
// If this thread isn't already wrapped by an rtc::Thread, create a
|
||||
// wrapper and own it in this class.
|
||||
this_thread = rtc::ThreadManager::Instance()->WrapCurrentThread();
|
||||
wraps_current_thread = true;
|
||||
}
|
||||
return this_thread;
|
||||
}
|
||||
|
||||
std::unique_ptr<SctpTransportFactoryInterface> MaybeCreateSctpFactory(
|
||||
std::unique_ptr<SctpTransportFactoryInterface> factory,
|
||||
rtc::Thread* network_thread) {
|
||||
if (factory) {
|
||||
return factory;
|
||||
}
|
||||
#ifdef WEBRTC_HAVE_SCTP
|
||||
return std::make_unique<cricket::SctpTransportFactory>(network_thread);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Static
|
||||
rtc::scoped_refptr<ConnectionContext> ConnectionContext::Create(
|
||||
const Environment& env,
|
||||
PeerConnectionFactoryDependencies* dependencies) {
|
||||
return rtc::scoped_refptr<ConnectionContext>(
|
||||
new ConnectionContext(env, dependencies));
|
||||
}
|
||||
|
||||
ConnectionContext::ConnectionContext(
|
||||
const Environment& env,
|
||||
PeerConnectionFactoryDependencies* dependencies)
|
||||
: network_thread_(MaybeStartNetworkThread(dependencies->network_thread,
|
||||
owned_socket_factory_,
|
||||
owned_network_thread_)),
|
||||
worker_thread_(dependencies->worker_thread,
|
||||
[]() {
|
||||
auto thread_holder = rtc::Thread::Create();
|
||||
thread_holder->SetName("pc_worker_thread", nullptr);
|
||||
thread_holder->Start();
|
||||
return thread_holder;
|
||||
}),
|
||||
signaling_thread_(MaybeWrapThread(dependencies->signaling_thread,
|
||||
wraps_current_thread_)),
|
||||
env_(env),
|
||||
media_engine_(
|
||||
dependencies->media_factory != nullptr
|
||||
? dependencies->media_factory->CreateMediaEngine(env_,
|
||||
*dependencies)
|
||||
: nullptr),
|
||||
network_monitor_factory_(
|
||||
std::move(dependencies->network_monitor_factory)),
|
||||
default_network_manager_(std::move(dependencies->network_manager)),
|
||||
call_factory_(std::move(dependencies->media_factory)),
|
||||
default_socket_factory_(std::move(dependencies->packet_socket_factory)),
|
||||
sctp_factory_(
|
||||
MaybeCreateSctpFactory(std::move(dependencies->sctp_factory),
|
||||
network_thread())),
|
||||
use_rtx_(true) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
RTC_DCHECK(!(default_network_manager_ && network_monitor_factory_))
|
||||
<< "You can't set both network_manager and network_monitor_factory.";
|
||||
|
||||
signaling_thread_->AllowInvokesToThread(worker_thread());
|
||||
signaling_thread_->AllowInvokesToThread(network_thread_);
|
||||
worker_thread_->AllowInvokesToThread(network_thread_);
|
||||
if (!network_thread_->IsCurrent()) {
|
||||
// network_thread_->IsCurrent() == true means signaling_thread_ is
|
||||
// network_thread_. In this case, no further action is required as
|
||||
// signaling_thread_ can already invoke network_thread_.
|
||||
network_thread_->PostTask(
|
||||
[thread = network_thread_, worker_thread = worker_thread_.get()] {
|
||||
thread->DisallowBlockingCalls();
|
||||
thread->DisallowAllInvokes();
|
||||
if (worker_thread == thread) {
|
||||
// In this case, worker_thread_ == network_thread_
|
||||
thread->AllowInvokesToThread(thread);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
rtc::InitRandom(rtc::Time32());
|
||||
|
||||
rtc::SocketFactory* socket_factory = dependencies->socket_factory;
|
||||
if (socket_factory == nullptr) {
|
||||
if (owned_socket_factory_) {
|
||||
socket_factory = owned_socket_factory_.get();
|
||||
} else {
|
||||
// TODO(bugs.webrtc.org/13145): This case should be deleted. Either
|
||||
// require that a PacketSocketFactory and NetworkManager always are
|
||||
// injected (with no need to construct these default objects), or require
|
||||
// that if a network_thread is injected, an approprite rtc::SocketServer
|
||||
// should be injected too.
|
||||
socket_factory = network_thread()->socketserver();
|
||||
}
|
||||
}
|
||||
if (!default_network_manager_) {
|
||||
// If network_monitor_factory_ is non-null, it will be used to create a
|
||||
// network monitor while on the network thread.
|
||||
default_network_manager_ = std::make_unique<rtc::BasicNetworkManager>(
|
||||
network_monitor_factory_.get(), socket_factory, &env_.field_trials());
|
||||
}
|
||||
if (!default_socket_factory_) {
|
||||
default_socket_factory_ =
|
||||
std::make_unique<rtc::BasicPacketSocketFactory>(socket_factory);
|
||||
}
|
||||
// Set warning levels on the threads, to give warnings when response
|
||||
// may be slower than is expected of the thread.
|
||||
// Since some of the threads may be the same, start with the least
|
||||
// restrictive limits and end with the least permissive ones.
|
||||
// This will give warnings for all cases.
|
||||
signaling_thread_->SetDispatchWarningMs(100);
|
||||
worker_thread_->SetDispatchWarningMs(30);
|
||||
network_thread_->SetDispatchWarningMs(10);
|
||||
|
||||
if (media_engine_) {
|
||||
// TODO(tommi): Change VoiceEngine to do ctor time initialization so that
|
||||
// this isn't necessary.
|
||||
worker_thread_->BlockingCall([&] { media_engine_->Init(); });
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionContext::~ConnectionContext() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
// `media_engine_` requires destruction to happen on the worker thread.
|
||||
worker_thread_->PostTask([media_engine = std::move(media_engine_)] {});
|
||||
|
||||
// Make sure `worker_thread()` and `signaling_thread()` outlive
|
||||
// `default_socket_factory_` and `default_network_manager_`.
|
||||
default_socket_factory_ = nullptr;
|
||||
default_network_manager_ = nullptr;
|
||||
|
||||
if (wraps_current_thread_)
|
||||
rtc::ThreadManager::Instance()->UnwrapCurrentThread();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
152
TMessagesProj/jni/voip/webrtc/pc/connection_context.h
Normal file
152
TMessagesProj/jni/voip/webrtc/pc/connection_context.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_CONNECTION_CONTEXT_H_
|
||||
#define PC_CONNECTION_CONTEXT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/sctp_transport_factory_interface.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/network_monitor_factory.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/socket_factory.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace rtc {
|
||||
class BasicPacketSocketFactory;
|
||||
class UniqueRandomIdGenerator;
|
||||
} // namespace rtc
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This class contains resources needed by PeerConnection and associated
|
||||
// objects. A reference to this object is passed to each PeerConnection. The
|
||||
// methods on this object are assumed not to change the state in any way that
|
||||
// interferes with the operation of other PeerConnections.
|
||||
//
|
||||
// This class must be created and destroyed on the signaling thread.
|
||||
class ConnectionContext final
|
||||
: public rtc::RefCountedNonVirtual<ConnectionContext> {
|
||||
public:
|
||||
// Creates a ConnectionContext. May return null if initialization fails.
|
||||
// The Dependencies class allows simple management of all new dependencies
|
||||
// being added to the ConnectionContext.
|
||||
static rtc::scoped_refptr<ConnectionContext> Create(
|
||||
const Environment& env,
|
||||
PeerConnectionFactoryDependencies* dependencies);
|
||||
|
||||
// This class is not copyable or movable.
|
||||
ConnectionContext(const ConnectionContext&) = delete;
|
||||
ConnectionContext& operator=(const ConnectionContext&) = delete;
|
||||
|
||||
// Functions called from PeerConnection and friends
|
||||
SctpTransportFactoryInterface* sctp_transport_factory() const {
|
||||
return sctp_factory_.get();
|
||||
}
|
||||
|
||||
cricket::MediaEngineInterface* media_engine() const {
|
||||
return media_engine_.get();
|
||||
}
|
||||
|
||||
rtc::Thread* signaling_thread() { return signaling_thread_; }
|
||||
const rtc::Thread* signaling_thread() const { return signaling_thread_; }
|
||||
rtc::Thread* worker_thread() { return worker_thread_.get(); }
|
||||
const rtc::Thread* worker_thread() const { return worker_thread_.get(); }
|
||||
rtc::Thread* network_thread() { return network_thread_; }
|
||||
const rtc::Thread* network_thread() const { return network_thread_; }
|
||||
|
||||
// Environment associated with the PeerConnectionFactory.
|
||||
// Note: environments are different for different PeerConnections,
|
||||
// but they are not supposed to change after creating the PeerConnection.
|
||||
const Environment& env() const { return env_; }
|
||||
|
||||
// Accessors only used from the PeerConnectionFactory class
|
||||
rtc::NetworkManager* default_network_manager() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return default_network_manager_.get();
|
||||
}
|
||||
rtc::PacketSocketFactory* default_socket_factory() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return default_socket_factory_.get();
|
||||
}
|
||||
MediaFactory* call_factory() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
return call_factory_.get();
|
||||
}
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator() { return &ssrc_generator_; }
|
||||
// Note: There is lots of code that wants to know whether or not we
|
||||
// use RTX, but so far, no code has been found that sets it to false.
|
||||
// Kept in the API in order to ease introduction if we want to resurrect
|
||||
// the functionality.
|
||||
bool use_rtx() { return use_rtx_; }
|
||||
|
||||
// For use by tests.
|
||||
void set_use_rtx(bool use_rtx) { use_rtx_ = use_rtx; }
|
||||
|
||||
protected:
|
||||
ConnectionContext(const Environment& env,
|
||||
PeerConnectionFactoryDependencies* dependencies);
|
||||
|
||||
friend class rtc::RefCountedNonVirtual<ConnectionContext>;
|
||||
~ConnectionContext();
|
||||
|
||||
private:
|
||||
// The following three variables are used to communicate between the
|
||||
// constructor and the destructor, and are never exposed externally.
|
||||
bool wraps_current_thread_;
|
||||
std::unique_ptr<rtc::SocketFactory> owned_socket_factory_;
|
||||
std::unique_ptr<rtc::Thread> owned_network_thread_
|
||||
RTC_GUARDED_BY(signaling_thread_);
|
||||
rtc::Thread* const network_thread_;
|
||||
AlwaysValidPointer<rtc::Thread> const worker_thread_;
|
||||
rtc::Thread* const signaling_thread_;
|
||||
|
||||
const Environment env_;
|
||||
|
||||
// This object is const over the lifetime of the ConnectionContext, and is
|
||||
// only altered in the destructor.
|
||||
std::unique_ptr<cricket::MediaEngineInterface> media_engine_;
|
||||
|
||||
// This object should be used to generate any SSRC that is not explicitly
|
||||
// specified by the user (or by the remote party).
|
||||
// TODO(bugs.webrtc.org/12666): This variable is used from both the signaling
|
||||
// and worker threads. See if we can't restrict usage to a single thread.
|
||||
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||
std::unique_ptr<rtc::NetworkMonitorFactory> const network_monitor_factory_
|
||||
RTC_GUARDED_BY(signaling_thread_);
|
||||
std::unique_ptr<rtc::NetworkManager> default_network_manager_
|
||||
RTC_GUARDED_BY(signaling_thread_);
|
||||
std::unique_ptr<MediaFactory> const call_factory_
|
||||
RTC_GUARDED_BY(worker_thread());
|
||||
|
||||
std::unique_ptr<rtc::PacketSocketFactory> default_socket_factory_
|
||||
RTC_GUARDED_BY(signaling_thread_);
|
||||
std::unique_ptr<SctpTransportFactoryInterface> const sctp_factory_;
|
||||
|
||||
// Controls whether to announce support for the the rfc4588 payload format
|
||||
// for retransmitted video packets.
|
||||
bool use_rtx_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_CONNECTION_CONTEXT_H_
|
||||
440
TMessagesProj/jni/voip/webrtc/pc/data_channel_controller.cc
Normal file
440
TMessagesProj/jni/voip/webrtc/pc/data_channel_controller.cc
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/data_channel_controller.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "pc/peer_connection_internal.h"
|
||||
#include "pc/sctp_utils.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
DataChannelController::~DataChannelController() {
|
||||
RTC_DCHECK(sctp_data_channels_n_.empty())
|
||||
<< "Missing call to TeardownDataChannelTransport_n?";
|
||||
RTC_DCHECK(!signaling_safety_.flag()->alive())
|
||||
<< "Missing call to PrepareForShutdown?";
|
||||
}
|
||||
|
||||
bool DataChannelController::HasDataChannels() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return channel_usage_ == DataChannelUsage::kInUse;
|
||||
}
|
||||
|
||||
bool DataChannelController::HasUsedDataChannels() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return channel_usage_ != DataChannelUsage::kNeverUsed;
|
||||
}
|
||||
|
||||
RTCError DataChannelController::SendData(
|
||||
StreamId sid,
|
||||
const SendDataParams& params,
|
||||
const rtc::CopyOnWriteBuffer& payload) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (!data_channel_transport_) {
|
||||
RTC_LOG(LS_ERROR) << "SendData called before transport is ready";
|
||||
return RTCError(RTCErrorType::INVALID_STATE);
|
||||
}
|
||||
return data_channel_transport_->SendData(sid.stream_id_int(), params,
|
||||
payload);
|
||||
}
|
||||
|
||||
void DataChannelController::AddSctpDataStream(StreamId sid) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK(sid.HasValue());
|
||||
if (data_channel_transport_) {
|
||||
data_channel_transport_->OpenChannel(sid.stream_id_int());
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::RemoveSctpDataStream(StreamId sid) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (data_channel_transport_) {
|
||||
data_channel_transport_->CloseChannel(sid.stream_id_int());
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::OnChannelStateChanged(
|
||||
SctpDataChannel* channel,
|
||||
DataChannelInterface::DataState state) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
|
||||
// Stash away the internal id here in case `OnSctpDataChannelClosed` ends up
|
||||
// releasing the last reference to the channel.
|
||||
const int channel_id = channel->internal_id();
|
||||
|
||||
if (state == DataChannelInterface::DataState::kClosed)
|
||||
OnSctpDataChannelClosed(channel);
|
||||
|
||||
DataChannelUsage channel_usage = sctp_data_channels_n_.empty()
|
||||
? DataChannelUsage::kHaveBeenUsed
|
||||
: DataChannelUsage::kInUse;
|
||||
signaling_thread()->PostTask(SafeTask(
|
||||
signaling_safety_.flag(), [this, channel_id, state, channel_usage] {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
channel_usage_ = channel_usage;
|
||||
pc_->OnSctpDataChannelStateChanged(channel_id, state);
|
||||
}));
|
||||
}
|
||||
|
||||
void DataChannelController::OnDataReceived(
|
||||
int channel_id,
|
||||
DataMessageType type,
|
||||
const rtc::CopyOnWriteBuffer& buffer) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
|
||||
if (HandleOpenMessage_n(channel_id, type, buffer))
|
||||
return;
|
||||
|
||||
auto it = absl::c_find_if(sctp_data_channels_n_, [&](const auto& c) {
|
||||
return c->sid_n().stream_id_int() == channel_id;
|
||||
});
|
||||
|
||||
if (it != sctp_data_channels_n_.end())
|
||||
(*it)->OnDataReceived(type, buffer);
|
||||
}
|
||||
|
||||
void DataChannelController::OnChannelClosing(int channel_id) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
auto it = absl::c_find_if(sctp_data_channels_n_, [&](const auto& c) {
|
||||
return c->sid_n().stream_id_int() == channel_id;
|
||||
});
|
||||
|
||||
if (it != sctp_data_channels_n_.end())
|
||||
(*it)->OnClosingProcedureStartedRemotely();
|
||||
}
|
||||
|
||||
void DataChannelController::OnChannelClosed(int channel_id) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
StreamId sid(channel_id);
|
||||
sid_allocator_.ReleaseSid(sid);
|
||||
auto it = absl::c_find_if(sctp_data_channels_n_,
|
||||
[&](const auto& c) { return c->sid_n() == sid; });
|
||||
|
||||
if (it != sctp_data_channels_n_.end()) {
|
||||
rtc::scoped_refptr<SctpDataChannel> channel = std::move(*it);
|
||||
sctp_data_channels_n_.erase(it);
|
||||
channel->OnClosingProcedureComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::OnReadyToSend() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
auto copy = sctp_data_channels_n_;
|
||||
for (const auto& channel : copy) {
|
||||
if (channel->sid_n().HasValue()) {
|
||||
channel->OnTransportReady();
|
||||
} else {
|
||||
// This happens for role==SSL_SERVER channels when we get notified by
|
||||
// the transport *before* the SDP code calls `AllocateSctpSids` to
|
||||
// trigger assignment of sids. In this case OnTransportReady() will be
|
||||
// called from within `AllocateSctpSids` below.
|
||||
RTC_LOG(LS_INFO) << "OnReadyToSend: Still waiting for an id for channel.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::OnTransportClosed(RTCError error) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
|
||||
// This loop will close all data channels and trigger a callback to
|
||||
// `OnSctpDataChannelClosed`. We'll empty `sctp_data_channels_n_`, first
|
||||
// and `OnSctpDataChannelClosed` will become a noop but we'll release the
|
||||
// StreamId here.
|
||||
std::vector<rtc::scoped_refptr<SctpDataChannel>> temp_sctp_dcs;
|
||||
temp_sctp_dcs.swap(sctp_data_channels_n_);
|
||||
for (const auto& channel : temp_sctp_dcs) {
|
||||
channel->OnTransportChannelClosed(error);
|
||||
sid_allocator_.ReleaseSid(channel->sid_n());
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::SetupDataChannelTransport_n(
|
||||
DataChannelTransportInterface* transport) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK(transport);
|
||||
set_data_channel_transport(transport);
|
||||
}
|
||||
|
||||
void DataChannelController::PrepareForShutdown() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
signaling_safety_.reset(PendingTaskSafetyFlag::CreateDetachedInactive());
|
||||
if (channel_usage_ != DataChannelUsage::kNeverUsed)
|
||||
channel_usage_ = DataChannelUsage::kHaveBeenUsed;
|
||||
}
|
||||
|
||||
void DataChannelController::TeardownDataChannelTransport_n(RTCError error) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
OnTransportClosed(error);
|
||||
set_data_channel_transport(nullptr);
|
||||
RTC_DCHECK(sctp_data_channels_n_.empty());
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
}
|
||||
|
||||
void DataChannelController::OnTransportChanged(
|
||||
DataChannelTransportInterface* new_data_channel_transport) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (data_channel_transport_ &&
|
||||
data_channel_transport_ != new_data_channel_transport) {
|
||||
// Changed which data channel transport is used for `sctp_mid_` (eg. now
|
||||
// it's bundled).
|
||||
set_data_channel_transport(new_data_channel_transport);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DataChannelStats> DataChannelController::GetDataChannelStats()
|
||||
const {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
std::vector<DataChannelStats> stats;
|
||||
stats.reserve(sctp_data_channels_n_.size());
|
||||
for (const auto& channel : sctp_data_channels_n_)
|
||||
stats.push_back(channel->GetStats());
|
||||
return stats;
|
||||
}
|
||||
|
||||
bool DataChannelController::HandleOpenMessage_n(
|
||||
int channel_id,
|
||||
DataMessageType type,
|
||||
const rtc::CopyOnWriteBuffer& buffer) {
|
||||
if (type != DataMessageType::kControl || !IsOpenMessage(buffer))
|
||||
return false;
|
||||
|
||||
// Received OPEN message; parse and signal that a new data channel should
|
||||
// be created.
|
||||
std::string label;
|
||||
InternalDataChannelInit config;
|
||||
config.id = channel_id;
|
||||
if (!ParseDataChannelOpenMessage(buffer, &label, &config)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse the OPEN message for sid "
|
||||
<< channel_id;
|
||||
} else {
|
||||
config.open_handshake_role = InternalDataChannelInit::kAcker;
|
||||
auto channel_or_error = CreateDataChannel(label, config);
|
||||
if (channel_or_error.ok()) {
|
||||
signaling_thread()->PostTask(SafeTask(
|
||||
signaling_safety_.flag(),
|
||||
[this, channel = channel_or_error.MoveValue(),
|
||||
ready_to_send = data_channel_transport_->IsReadyToSend()] {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
OnDataChannelOpenMessage(std::move(channel), ready_to_send);
|
||||
}));
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message."
|
||||
<< ToString(channel_or_error.error().type());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataChannelController::OnDataChannelOpenMessage(
|
||||
rtc::scoped_refptr<SctpDataChannel> channel,
|
||||
bool ready_to_send) {
|
||||
channel_usage_ = DataChannelUsage::kInUse;
|
||||
auto proxy = SctpDataChannel::CreateProxy(channel, signaling_safety_.flag());
|
||||
|
||||
pc_->Observer()->OnDataChannel(proxy);
|
||||
pc_->NoteDataAddedEvent();
|
||||
|
||||
if (ready_to_send) {
|
||||
network_thread()->PostTask([channel = std::move(channel)] {
|
||||
if (channel->state() != DataChannelInterface::DataState::kClosed)
|
||||
channel->OnTransportReady();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// RTC_RUN_ON(network_thread())
|
||||
RTCError DataChannelController::ReserveOrAllocateSid(
|
||||
StreamId& sid,
|
||||
absl::optional<rtc::SSLRole> fallback_ssl_role) {
|
||||
if (sid.HasValue()) {
|
||||
return sid_allocator_.ReserveSid(sid)
|
||||
? RTCError::OK()
|
||||
: RTCError(RTCErrorType::INVALID_RANGE,
|
||||
"StreamId out of range or reserved.");
|
||||
}
|
||||
|
||||
// Attempt to allocate an ID based on the negotiated role.
|
||||
absl::optional<rtc::SSLRole> role = pc_->GetSctpSslRole_n();
|
||||
if (!role)
|
||||
role = fallback_ssl_role;
|
||||
if (role) {
|
||||
sid = sid_allocator_.AllocateSid(*role);
|
||||
if (!sid.HasValue())
|
||||
return RTCError(RTCErrorType::RESOURCE_EXHAUSTED);
|
||||
}
|
||||
// When we get here, we may still not have an ID, but that's a supported case
|
||||
// whereby an id will be assigned later.
|
||||
RTC_DCHECK(sid.HasValue() || !role);
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
// RTC_RUN_ON(network_thread())
|
||||
RTCErrorOr<rtc::scoped_refptr<SctpDataChannel>>
|
||||
DataChannelController::CreateDataChannel(const std::string& label,
|
||||
InternalDataChannelInit& config) {
|
||||
StreamId sid(config.id);
|
||||
RTCError err = ReserveOrAllocateSid(sid, config.fallback_ssl_role);
|
||||
if (!err.ok())
|
||||
return err;
|
||||
|
||||
// In case `sid` has changed. Update `config` accordingly.
|
||||
config.id = sid.stream_id_int();
|
||||
|
||||
rtc::scoped_refptr<SctpDataChannel> channel = SctpDataChannel::Create(
|
||||
weak_factory_.GetWeakPtr(), label, data_channel_transport_ != nullptr,
|
||||
config, signaling_thread(), network_thread());
|
||||
RTC_DCHECK(channel);
|
||||
sctp_data_channels_n_.push_back(channel);
|
||||
|
||||
// If we have an id already, notify the transport.
|
||||
if (sid.HasValue())
|
||||
AddSctpDataStream(sid);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>>
|
||||
DataChannelController::InternalCreateDataChannelWithProxy(
|
||||
const std::string& label,
|
||||
const InternalDataChannelInit& config) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
RTC_DCHECK(!pc_->IsClosed());
|
||||
if (!config.IsValid()) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
|
||||
"Invalid DataChannelInit");
|
||||
}
|
||||
|
||||
bool ready_to_send = false;
|
||||
InternalDataChannelInit new_config = config;
|
||||
StreamId sid(new_config.id);
|
||||
auto ret = network_thread()->BlockingCall(
|
||||
[&]() -> RTCErrorOr<rtc::scoped_refptr<SctpDataChannel>> {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
auto channel = CreateDataChannel(label, new_config);
|
||||
if (!channel.ok())
|
||||
return channel;
|
||||
ready_to_send =
|
||||
data_channel_transport_ && data_channel_transport_->IsReadyToSend();
|
||||
if (ready_to_send) {
|
||||
// If the transport is ready to send because the initial channel
|
||||
// ready signal may have been sent before the DataChannel creation.
|
||||
// This has to be done async because the upper layer objects (e.g.
|
||||
// Chrome glue and WebKit) are not wired up properly until after
|
||||
// `InternalCreateDataChannelWithProxy` returns.
|
||||
network_thread()->PostTask([channel = channel.value()] {
|
||||
if (channel->state() != DataChannelInterface::DataState::kClosed)
|
||||
channel->OnTransportReady();
|
||||
});
|
||||
}
|
||||
|
||||
return channel;
|
||||
});
|
||||
|
||||
if (!ret.ok())
|
||||
return ret.MoveError();
|
||||
|
||||
channel_usage_ = DataChannelUsage::kInUse;
|
||||
return SctpDataChannel::CreateProxy(ret.MoveValue(),
|
||||
signaling_safety_.flag());
|
||||
}
|
||||
|
||||
void DataChannelController::AllocateSctpSids(rtc::SSLRole role) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
|
||||
const bool ready_to_send =
|
||||
data_channel_transport_ && data_channel_transport_->IsReadyToSend();
|
||||
|
||||
std::vector<std::pair<SctpDataChannel*, StreamId>> channels_to_update;
|
||||
std::vector<rtc::scoped_refptr<SctpDataChannel>> channels_to_close;
|
||||
for (auto it = sctp_data_channels_n_.begin();
|
||||
it != sctp_data_channels_n_.end();) {
|
||||
if (!(*it)->sid_n().HasValue()) {
|
||||
StreamId sid = sid_allocator_.AllocateSid(role);
|
||||
if (sid.HasValue()) {
|
||||
(*it)->SetSctpSid_n(sid);
|
||||
AddSctpDataStream(sid);
|
||||
if (ready_to_send) {
|
||||
RTC_LOG(LS_INFO) << "AllocateSctpSids: Id assigned, ready to send.";
|
||||
(*it)->OnTransportReady();
|
||||
}
|
||||
channels_to_update.push_back(std::make_pair((*it).get(), sid));
|
||||
} else {
|
||||
channels_to_close.push_back(std::move(*it));
|
||||
it = sctp_data_channels_n_.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// Since closing modifies the list of channels, we have to do the actual
|
||||
// closing outside the loop.
|
||||
for (const auto& channel : channels_to_close) {
|
||||
channel->CloseAbruptlyWithDataChannelFailure("Failed to allocate SCTP SID");
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::OnSctpDataChannelClosed(SctpDataChannel* channel) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
// After the closing procedure is done, it's safe to use this ID for
|
||||
// another data channel.
|
||||
if (channel->sid_n().HasValue()) {
|
||||
sid_allocator_.ReleaseSid(channel->sid_n());
|
||||
}
|
||||
auto it = absl::c_find_if(sctp_data_channels_n_,
|
||||
[&](const auto& c) { return c.get() == channel; });
|
||||
if (it != sctp_data_channels_n_.end())
|
||||
sctp_data_channels_n_.erase(it);
|
||||
}
|
||||
|
||||
void DataChannelController::set_data_channel_transport(
|
||||
DataChannelTransportInterface* transport) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
|
||||
if (data_channel_transport_)
|
||||
data_channel_transport_->SetDataSink(nullptr);
|
||||
|
||||
data_channel_transport_ = transport;
|
||||
|
||||
if (data_channel_transport_) {
|
||||
// There's a new data channel transport. This needs to be signaled to the
|
||||
// `sctp_data_channels_n_` so that they can reopen and reconnect. This is
|
||||
// necessary when bundling is applied.
|
||||
NotifyDataChannelsOfTransportCreated();
|
||||
data_channel_transport_->SetDataSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannelController::NotifyDataChannelsOfTransportCreated() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK(data_channel_transport_);
|
||||
|
||||
for (const auto& channel : sctp_data_channels_n_) {
|
||||
if (channel->sid_n().HasValue())
|
||||
AddSctpDataStream(channel->sid_n());
|
||||
channel->OnTransportChannelCreated();
|
||||
}
|
||||
}
|
||||
|
||||
rtc::Thread* DataChannelController::network_thread() const {
|
||||
return pc_->network_thread();
|
||||
}
|
||||
|
||||
rtc::Thread* DataChannelController::signaling_thread() const {
|
||||
return pc_->signaling_thread();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
165
TMessagesProj/jni/voip/webrtc/pc/data_channel_controller.h
Normal file
165
TMessagesProj/jni/voip/webrtc/pc/data_channel_controller.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_DATA_CHANNEL_CONTROLLER_H_
|
||||
#define PC_DATA_CHANNEL_CONTROLLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/transport/data_channel_transport_interface.h"
|
||||
#include "pc/data_channel_utils.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/weak_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnectionInternal;
|
||||
|
||||
class DataChannelController : public SctpDataChannelControllerInterface,
|
||||
public DataChannelSink {
|
||||
public:
|
||||
explicit DataChannelController(PeerConnectionInternal* pc) : pc_(pc) {}
|
||||
~DataChannelController();
|
||||
|
||||
// Not copyable or movable.
|
||||
DataChannelController(DataChannelController&) = delete;
|
||||
DataChannelController& operator=(const DataChannelController& other) = delete;
|
||||
DataChannelController(DataChannelController&&) = delete;
|
||||
DataChannelController& operator=(DataChannelController&& other) = delete;
|
||||
|
||||
// Implements
|
||||
// SctpDataChannelProviderInterface.
|
||||
RTCError SendData(StreamId sid,
|
||||
const SendDataParams& params,
|
||||
const rtc::CopyOnWriteBuffer& payload) override;
|
||||
void AddSctpDataStream(StreamId sid) override;
|
||||
void RemoveSctpDataStream(StreamId sid) override;
|
||||
void OnChannelStateChanged(SctpDataChannel* channel,
|
||||
DataChannelInterface::DataState state) override;
|
||||
|
||||
// Implements DataChannelSink.
|
||||
void OnDataReceived(int channel_id,
|
||||
DataMessageType type,
|
||||
const rtc::CopyOnWriteBuffer& buffer) override;
|
||||
void OnChannelClosing(int channel_id) override;
|
||||
void OnChannelClosed(int channel_id) override;
|
||||
void OnReadyToSend() override;
|
||||
void OnTransportClosed(RTCError error) override;
|
||||
|
||||
// Called as part of destroying the owning PeerConnection.
|
||||
void PrepareForShutdown();
|
||||
|
||||
// Called from PeerConnection::SetupDataChannelTransport_n
|
||||
void SetupDataChannelTransport_n(DataChannelTransportInterface* transport);
|
||||
// Called from PeerConnection::TeardownDataChannelTransport_n
|
||||
void TeardownDataChannelTransport_n(RTCError error);
|
||||
|
||||
// Called from PeerConnection::OnTransportChanged
|
||||
// to make required changes to datachannels' transports.
|
||||
void OnTransportChanged(
|
||||
DataChannelTransportInterface* data_channel_transport);
|
||||
|
||||
// Called from PeerConnection::GetDataChannelStats on the signaling thread.
|
||||
std::vector<DataChannelStats> GetDataChannelStats() const;
|
||||
|
||||
// Creates channel and adds it to the collection of DataChannels that will
|
||||
// be offered in a SessionDescription, and wraps it in a proxy object.
|
||||
RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>>
|
||||
InternalCreateDataChannelWithProxy(const std::string& label,
|
||||
const InternalDataChannelInit& config);
|
||||
void AllocateSctpSids(rtc::SSLRole role);
|
||||
|
||||
// Check if data channels are currently tracked. Used to decide whether a
|
||||
// rejected m=application section should be reoffered.
|
||||
bool HasDataChannels() const;
|
||||
|
||||
// At some point in time, a data channel has existed.
|
||||
bool HasUsedDataChannels() const;
|
||||
|
||||
protected:
|
||||
rtc::Thread* network_thread() const;
|
||||
rtc::Thread* signaling_thread() const;
|
||||
|
||||
private:
|
||||
void OnSctpDataChannelClosed(SctpDataChannel* channel);
|
||||
|
||||
// Creates a new SctpDataChannel object on the network thread.
|
||||
RTCErrorOr<rtc::scoped_refptr<SctpDataChannel>> CreateDataChannel(
|
||||
const std::string& label,
|
||||
InternalDataChannelInit& config) RTC_RUN_ON(network_thread());
|
||||
|
||||
// Parses and handles open messages. Returns true if the message is an open
|
||||
// message and should be considered to be handled, false otherwise.
|
||||
bool HandleOpenMessage_n(int channel_id,
|
||||
DataMessageType type,
|
||||
const rtc::CopyOnWriteBuffer& buffer)
|
||||
RTC_RUN_ON(network_thread());
|
||||
// Called when a valid data channel OPEN message is received.
|
||||
void OnDataChannelOpenMessage(rtc::scoped_refptr<SctpDataChannel> channel,
|
||||
bool ready_to_send)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Accepts a `StreamId` which may be pre-negotiated or unassigned. For
|
||||
// pre-negotiated sids, attempts to reserve the sid in the allocation pool,
|
||||
// for unassigned sids attempts to generate a new sid if possible. Returns
|
||||
// RTCError::OK() if the sid is reserved (and may have been generated) or
|
||||
// if not enough information exists to generate a sid, in which case the sid
|
||||
// will still be unassigned upon return, but will be assigned later.
|
||||
// If the pool has been exhausted or a sid has already been reserved, an
|
||||
// error will be returned.
|
||||
RTCError ReserveOrAllocateSid(StreamId& sid,
|
||||
absl::optional<rtc::SSLRole> fallback_ssl_role)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
// Called when all data channels need to be notified of a transport channel
|
||||
// (calls OnTransportChannelCreated on the signaling thread).
|
||||
void NotifyDataChannelsOfTransportCreated();
|
||||
|
||||
void set_data_channel_transport(DataChannelTransportInterface* transport);
|
||||
|
||||
// Plugin transport used for data channels. Pointer may be accessed and
|
||||
// checked from any thread, but the object may only be touched on the
|
||||
// network thread.
|
||||
DataChannelTransportInterface* data_channel_transport_
|
||||
RTC_GUARDED_BY(network_thread()) = nullptr;
|
||||
SctpSidAllocator sid_allocator_ RTC_GUARDED_BY(network_thread());
|
||||
std::vector<rtc::scoped_refptr<SctpDataChannel>> sctp_data_channels_n_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
enum class DataChannelUsage : uint8_t {
|
||||
kNeverUsed = 0,
|
||||
kHaveBeenUsed,
|
||||
kInUse
|
||||
};
|
||||
DataChannelUsage channel_usage_ RTC_GUARDED_BY(signaling_thread()) =
|
||||
DataChannelUsage::kNeverUsed;
|
||||
|
||||
// Owning PeerConnection.
|
||||
PeerConnectionInternal* const pc_;
|
||||
// The weak pointers must be dereferenced and invalidated on the network
|
||||
// thread only.
|
||||
rtc::WeakPtrFactory<DataChannelController> weak_factory_
|
||||
RTC_GUARDED_BY(network_thread()){this};
|
||||
ScopedTaskSafety signaling_safety_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_DATA_CHANNEL_CONTROLLER_H_
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2022 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 "pc/data_channel_controller.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "pc/peer_connection_internal.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "pc/test/mock_peer_connection_internal.h"
|
||||
#include "rtc_base/null_socket_server.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/run_loop.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
|
||||
class MockDataChannelTransport : public DataChannelTransportInterface {
|
||||
public:
|
||||
~MockDataChannelTransport() override {}
|
||||
|
||||
MOCK_METHOD(RTCError, OpenChannel, (int channel_id), (override));
|
||||
MOCK_METHOD(RTCError,
|
||||
SendData,
|
||||
(int channel_id,
|
||||
const SendDataParams& params,
|
||||
const rtc::CopyOnWriteBuffer& buffer),
|
||||
(override));
|
||||
MOCK_METHOD(RTCError, CloseChannel, (int channel_id), (override));
|
||||
MOCK_METHOD(void, SetDataSink, (DataChannelSink * sink), (override));
|
||||
MOCK_METHOD(bool, IsReadyToSend, (), (const, override));
|
||||
};
|
||||
|
||||
// Convenience class for tests to ensure that shutdown methods for DCC
|
||||
// are consistently called. In practice SdpOfferAnswerHandler will call
|
||||
// TeardownDataChannelTransport_n on the network thread when destroying the
|
||||
// data channel transport and PeerConnection calls PrepareForShutdown() from
|
||||
// within PeerConnection::Close(). The DataChannelControllerForTest class mimics
|
||||
// behavior by calling those methods from within its destructor.
|
||||
class DataChannelControllerForTest : public DataChannelController {
|
||||
public:
|
||||
explicit DataChannelControllerForTest(
|
||||
PeerConnectionInternal* pc,
|
||||
DataChannelTransportInterface* transport = nullptr)
|
||||
: DataChannelController(pc) {
|
||||
if (transport) {
|
||||
network_thread()->BlockingCall(
|
||||
[&] { SetupDataChannelTransport_n(transport); });
|
||||
}
|
||||
}
|
||||
|
||||
~DataChannelControllerForTest() override {
|
||||
network_thread()->BlockingCall(
|
||||
[&] { TeardownDataChannelTransport_n(RTCError::OK()); });
|
||||
PrepareForShutdown();
|
||||
}
|
||||
};
|
||||
|
||||
class DataChannelControllerTest : public ::testing::Test {
|
||||
protected:
|
||||
DataChannelControllerTest()
|
||||
: network_thread_(std::make_unique<rtc::NullSocketServer>()) {
|
||||
network_thread_.Start();
|
||||
pc_ = rtc::make_ref_counted<NiceMock<MockPeerConnectionInternal>>();
|
||||
ON_CALL(*pc_, signaling_thread)
|
||||
.WillByDefault(Return(rtc::Thread::Current()));
|
||||
ON_CALL(*pc_, network_thread).WillByDefault(Return(&network_thread_));
|
||||
}
|
||||
|
||||
~DataChannelControllerTest() override {
|
||||
run_loop_.Flush();
|
||||
network_thread_.Stop();
|
||||
}
|
||||
|
||||
test::RunLoop run_loop_;
|
||||
rtc::Thread network_thread_;
|
||||
rtc::scoped_refptr<NiceMock<MockPeerConnectionInternal>> pc_;
|
||||
};
|
||||
|
||||
TEST_F(DataChannelControllerTest, CreateAndDestroy) {
|
||||
DataChannelControllerForTest dcc(pc_.get());
|
||||
}
|
||||
|
||||
TEST_F(DataChannelControllerTest, CreateDataChannelEarlyRelease) {
|
||||
DataChannelControllerForTest dcc(pc_.get());
|
||||
auto ret = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()));
|
||||
ASSERT_TRUE(ret.ok());
|
||||
auto channel = ret.MoveValue();
|
||||
// DCC still holds a reference to the channel. Release this reference early.
|
||||
channel = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(DataChannelControllerTest, CreateDataChannelEarlyClose) {
|
||||
DataChannelControllerForTest dcc(pc_.get());
|
||||
EXPECT_FALSE(dcc.HasDataChannels());
|
||||
EXPECT_FALSE(dcc.HasUsedDataChannels());
|
||||
auto ret = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()));
|
||||
ASSERT_TRUE(ret.ok());
|
||||
auto channel = ret.MoveValue();
|
||||
EXPECT_TRUE(dcc.HasDataChannels());
|
||||
EXPECT_TRUE(dcc.HasUsedDataChannels());
|
||||
channel->Close();
|
||||
run_loop_.Flush();
|
||||
EXPECT_FALSE(dcc.HasDataChannels());
|
||||
EXPECT_TRUE(dcc.HasUsedDataChannels());
|
||||
}
|
||||
|
||||
TEST_F(DataChannelControllerTest, CreateDataChannelLateRelease) {
|
||||
auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get());
|
||||
auto ret = dcc->InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()));
|
||||
ASSERT_TRUE(ret.ok());
|
||||
auto channel = ret.MoveValue();
|
||||
dcc.reset();
|
||||
channel = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(DataChannelControllerTest, CloseAfterControllerDestroyed) {
|
||||
auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get());
|
||||
auto ret = dcc->InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()));
|
||||
ASSERT_TRUE(ret.ok());
|
||||
auto channel = ret.MoveValue();
|
||||
dcc.reset();
|
||||
channel->Close();
|
||||
}
|
||||
|
||||
// Allocate the maximum number of data channels and then one more.
|
||||
// The last allocation should fail.
|
||||
TEST_F(DataChannelControllerTest, MaxChannels) {
|
||||
NiceMock<MockDataChannelTransport> transport;
|
||||
int channel_id = 0;
|
||||
|
||||
ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() {
|
||||
return absl::optional<rtc::SSLRole>((channel_id & 1) ? rtc::SSL_SERVER
|
||||
: rtc::SSL_CLIENT);
|
||||
});
|
||||
|
||||
DataChannelControllerForTest dcc(pc_.get(), &transport);
|
||||
|
||||
// Allocate the maximum number of channels + 1. Inside the loop, the creation
|
||||
// process will allocate a stream id for each channel.
|
||||
for (channel_id = 0; channel_id <= cricket::kMaxSctpStreams; ++channel_id) {
|
||||
auto ret = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()));
|
||||
if (channel_id == cricket::kMaxSctpStreams) {
|
||||
// We've reached the maximum and the previous call should have failed.
|
||||
EXPECT_FALSE(ret.ok());
|
||||
} else {
|
||||
// We're still working on saturating the pool. Things should be working.
|
||||
EXPECT_TRUE(ret.ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that while a data channel is in the `kClosing` state, its StreamId does
|
||||
// not get re-used for new channels. Only once the state reaches `kClosed`
|
||||
// should a StreamId be available again for allocation.
|
||||
TEST_F(DataChannelControllerTest, NoStreamIdReuseWhileClosing) {
|
||||
ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() {
|
||||
return rtc::SSL_CLIENT;
|
||||
});
|
||||
|
||||
NiceMock<MockDataChannelTransport> transport; // Wider scope than `dcc`.
|
||||
DataChannelControllerForTest dcc(pc_.get(), &transport);
|
||||
|
||||
// Create the first channel and check that we got the expected, first sid.
|
||||
auto channel1 = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label", InternalDataChannelInit(DataChannelInit()))
|
||||
.MoveValue();
|
||||
ASSERT_EQ(channel1->id(), 0);
|
||||
|
||||
// Start closing the channel and make sure its state is `kClosing`
|
||||
channel1->Close();
|
||||
ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosing);
|
||||
|
||||
// Create a second channel and make sure we get a new StreamId, not the same
|
||||
// as that of channel1.
|
||||
auto channel2 = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label2", InternalDataChannelInit(DataChannelInit()))
|
||||
.MoveValue();
|
||||
ASSERT_NE(channel2->id(), channel1->id()); // In practice the id will be 2.
|
||||
|
||||
// Simulate the acknowledgement of the channel closing from the transport.
|
||||
// This completes the closing operation of channel1.
|
||||
pc_->network_thread()->BlockingCall([&] { dcc.OnChannelClosed(0); });
|
||||
run_loop_.Flush();
|
||||
ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosed);
|
||||
|
||||
// Now create a third channel. This time, the id of the first channel should
|
||||
// be available again and therefore the ids of the first and third channels
|
||||
// should be the same.
|
||||
auto channel3 = dcc.InternalCreateDataChannelWithProxy(
|
||||
"label3", InternalDataChannelInit(DataChannelInit()))
|
||||
.MoveValue();
|
||||
EXPECT_EQ(channel3->id(), channel1->id());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
1167
TMessagesProj/jni/voip/webrtc/pc/data_channel_integrationtest.cc
Normal file
1167
TMessagesProj/jni/voip/webrtc/pc/data_channel_integrationtest.cc
Normal file
File diff suppressed because it is too large
Load diff
1151
TMessagesProj/jni/voip/webrtc/pc/data_channel_unittest.cc
Normal file
1151
TMessagesProj/jni/voip/webrtc/pc/data_channel_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
54
TMessagesProj/jni/voip/webrtc/pc/data_channel_utils.cc
Normal file
54
TMessagesProj/jni/voip/webrtc/pc/data_channel_utils.cc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2020 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 "pc/data_channel_utils.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool PacketQueue::Empty() const {
|
||||
return packets_.empty();
|
||||
}
|
||||
|
||||
std::unique_ptr<DataBuffer> PacketQueue::PopFront() {
|
||||
RTC_DCHECK(!packets_.empty());
|
||||
byte_count_ -= packets_.front()->size();
|
||||
std::unique_ptr<DataBuffer> packet = std::move(packets_.front());
|
||||
packets_.pop_front();
|
||||
return packet;
|
||||
}
|
||||
|
||||
void PacketQueue::PushFront(std::unique_ptr<DataBuffer> packet) {
|
||||
byte_count_ += packet->size();
|
||||
packets_.push_front(std::move(packet));
|
||||
}
|
||||
|
||||
void PacketQueue::PushBack(std::unique_ptr<DataBuffer> packet) {
|
||||
byte_count_ += packet->size();
|
||||
packets_.push_back(std::move(packet));
|
||||
}
|
||||
|
||||
void PacketQueue::Clear() {
|
||||
packets_.clear();
|
||||
byte_count_ = 0;
|
||||
}
|
||||
|
||||
void PacketQueue::Swap(PacketQueue* other) {
|
||||
size_t other_byte_count = other->byte_count_;
|
||||
other->byte_count_ = byte_count_;
|
||||
byte_count_ = other_byte_count;
|
||||
|
||||
other->packets_.swap(packets_);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
63
TMessagesProj/jni/voip/webrtc/pc/data_channel_utils.h
Normal file
63
TMessagesProj/jni/voip/webrtc/pc/data_channel_utils.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_DATA_CHANNEL_UTILS_H_
|
||||
#define PC_DATA_CHANNEL_UTILS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "media/base/media_engine.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A packet queue which tracks the total queued bytes. Queued packets are
|
||||
// owned by this class.
|
||||
class PacketQueue final {
|
||||
public:
|
||||
size_t byte_count() const { return byte_count_; }
|
||||
|
||||
bool Empty() const;
|
||||
|
||||
std::unique_ptr<DataBuffer> PopFront();
|
||||
|
||||
void PushFront(std::unique_ptr<DataBuffer> packet);
|
||||
void PushBack(std::unique_ptr<DataBuffer> packet);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Swap(PacketQueue* other);
|
||||
|
||||
private:
|
||||
std::deque<std::unique_ptr<DataBuffer>> packets_;
|
||||
size_t byte_count_ = 0;
|
||||
};
|
||||
|
||||
struct DataChannelStats {
|
||||
int internal_id;
|
||||
int id;
|
||||
std::string label;
|
||||
std::string protocol;
|
||||
DataChannelInterface::DataState state;
|
||||
uint32_t messages_sent;
|
||||
uint32_t messages_received;
|
||||
uint64_t bytes_sent;
|
||||
uint64_t bytes_received;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_DATA_CHANNEL_UTILS_H_
|
||||
330
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport.cc
Normal file
330
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport.cc
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* 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 "pc/dtls_srtp_transport.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
|
||||
namespace {
|
||||
// Value specified in RFC 5764.
|
||||
static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp";
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled,
|
||||
const FieldTrialsView& field_trials)
|
||||
: SrtpTransport(rtcp_mux_enabled, field_trials) {}
|
||||
|
||||
void DtlsSrtpTransport::SetDtlsTransports(
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport,
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
|
||||
// Transport names should be the same.
|
||||
if (rtp_dtls_transport && rtcp_dtls_transport) {
|
||||
RTC_DCHECK(rtp_dtls_transport->transport_name() ==
|
||||
rtcp_dtls_transport->transport_name());
|
||||
}
|
||||
|
||||
// When using DTLS-SRTP, we must reset the SrtpTransport every time the
|
||||
// DtlsTransport changes and wait until the DTLS handshake is complete to set
|
||||
// the newly negotiated parameters.
|
||||
// If `active_reset_srtp_params_` is true, intentionally reset the SRTP
|
||||
// parameter even though the DtlsTransport may not change.
|
||||
if (IsSrtpActive() && (rtp_dtls_transport != rtp_dtls_transport_ ||
|
||||
active_reset_srtp_params_)) {
|
||||
ResetParams();
|
||||
}
|
||||
|
||||
const std::string transport_name =
|
||||
rtp_dtls_transport ? rtp_dtls_transport->transport_name() : "null";
|
||||
|
||||
if (rtcp_dtls_transport && rtcp_dtls_transport != rtcp_dtls_transport_) {
|
||||
// This would only be possible if using BUNDLE but not rtcp-mux, which isn't
|
||||
// allowed according to the BUNDLE spec.
|
||||
RTC_CHECK(!(IsSrtpActive()))
|
||||
<< "Setting RTCP for DTLS/SRTP after the DTLS is active "
|
||||
"should never happen.";
|
||||
}
|
||||
|
||||
if (rtcp_dtls_transport) {
|
||||
RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << transport_name
|
||||
<< " transport " << rtcp_dtls_transport;
|
||||
}
|
||||
SetRtcpDtlsTransport(rtcp_dtls_transport);
|
||||
SetRtcpPacketTransport(rtcp_dtls_transport);
|
||||
|
||||
RTC_LOG(LS_INFO) << "Setting RTP Transport on " << transport_name
|
||||
<< " transport " << rtp_dtls_transport;
|
||||
SetRtpDtlsTransport(rtp_dtls_transport);
|
||||
SetRtpPacketTransport(rtp_dtls_transport);
|
||||
|
||||
MaybeSetupDtlsSrtp();
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) {
|
||||
SrtpTransport::SetRtcpMuxEnabled(enable);
|
||||
if (enable) {
|
||||
MaybeSetupDtlsSrtp();
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& send_extension_ids) {
|
||||
if (send_extension_ids_ == send_extension_ids) {
|
||||
return;
|
||||
}
|
||||
send_extension_ids_.emplace(send_extension_ids);
|
||||
if (DtlsHandshakeCompleted()) {
|
||||
// Reset the crypto parameters to update the send extension IDs.
|
||||
SetupRtpDtlsSrtp();
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::UpdateRecvEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& recv_extension_ids) {
|
||||
if (recv_extension_ids_ == recv_extension_ids) {
|
||||
return;
|
||||
}
|
||||
recv_extension_ids_.emplace(recv_extension_ids);
|
||||
if (DtlsHandshakeCompleted()) {
|
||||
// Reset the crypto parameters to update the receive extension IDs.
|
||||
SetupRtpDtlsSrtp();
|
||||
}
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::IsDtlsActive() {
|
||||
auto rtcp_dtls_transport =
|
||||
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
|
||||
return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() &&
|
||||
(!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive()));
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::IsDtlsConnected() {
|
||||
auto rtcp_dtls_transport =
|
||||
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
|
||||
return (rtp_dtls_transport_ &&
|
||||
rtp_dtls_transport_->dtls_state() == DtlsTransportState::kConnected &&
|
||||
(!rtcp_dtls_transport || rtcp_dtls_transport->dtls_state() ==
|
||||
DtlsTransportState::kConnected));
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::IsDtlsWritable() {
|
||||
auto rtcp_packet_transport =
|
||||
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
|
||||
return rtp_dtls_transport_ && rtp_dtls_transport_->writable() &&
|
||||
(!rtcp_packet_transport || rtcp_packet_transport->writable());
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::DtlsHandshakeCompleted() {
|
||||
return IsDtlsActive() && IsDtlsConnected();
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::MaybeSetupDtlsSrtp() {
|
||||
if (IsSrtpActive() || !IsDtlsWritable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetupRtpDtlsSrtp();
|
||||
|
||||
if (!rtcp_mux_enabled() && rtcp_dtls_transport_) {
|
||||
SetupRtcpDtlsSrtp();
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetupRtpDtlsSrtp() {
|
||||
// Use an empty encrypted header extension ID vector if not set. This could
|
||||
// happen when the DTLS handshake is completed before processing the
|
||||
// Offer/Answer which contains the encrypted header extension IDs.
|
||||
std::vector<int> send_extension_ids;
|
||||
std::vector<int> recv_extension_ids;
|
||||
if (send_extension_ids_) {
|
||||
send_extension_ids = *send_extension_ids_;
|
||||
}
|
||||
if (recv_extension_ids_) {
|
||||
recv_extension_ids = *recv_extension_ids_;
|
||||
}
|
||||
|
||||
int selected_crypto_suite;
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> send_key;
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> recv_key;
|
||||
|
||||
if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key,
|
||||
&recv_key) ||
|
||||
!SetRtpParams(selected_crypto_suite, &send_key[0],
|
||||
static_cast<int>(send_key.size()), send_extension_ids,
|
||||
selected_crypto_suite, &recv_key[0],
|
||||
static_cast<int>(recv_key.size()), recv_extension_ids)) {
|
||||
RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed";
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetupRtcpDtlsSrtp() {
|
||||
// Return if the DTLS-SRTP is active because the encrypted header extension
|
||||
// IDs don't need to be updated for RTCP and the crypto params don't need to
|
||||
// be reset.
|
||||
if (IsSrtpActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<int> send_extension_ids;
|
||||
std::vector<int> recv_extension_ids;
|
||||
if (send_extension_ids_) {
|
||||
send_extension_ids = *send_extension_ids_;
|
||||
}
|
||||
if (recv_extension_ids_) {
|
||||
recv_extension_ids = *recv_extension_ids_;
|
||||
}
|
||||
|
||||
int selected_crypto_suite;
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> rtcp_send_key;
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> rtcp_recv_key;
|
||||
if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite,
|
||||
&rtcp_send_key, &rtcp_recv_key) ||
|
||||
!SetRtcpParams(selected_crypto_suite, &rtcp_send_key[0],
|
||||
static_cast<int>(rtcp_send_key.size()), send_extension_ids,
|
||||
selected_crypto_suite, &rtcp_recv_key[0],
|
||||
static_cast<int>(rtcp_recv_key.size()),
|
||||
recv_extension_ids)) {
|
||||
RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed";
|
||||
}
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::ExtractParams(
|
||||
cricket::DtlsTransportInternal* dtls_transport,
|
||||
int* selected_crypto_suite,
|
||||
rtc::ZeroOnFreeBuffer<unsigned char>* send_key,
|
||||
rtc::ZeroOnFreeBuffer<unsigned char>* recv_key) {
|
||||
if (!dtls_transport || !dtls_transport->IsDtlsActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) {
|
||||
RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite";
|
||||
return false;
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << "Extracting keys from transport: "
|
||||
<< dtls_transport->transport_name();
|
||||
|
||||
int key_len;
|
||||
int salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len,
|
||||
&salt_len)) {
|
||||
RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite"
|
||||
<< selected_crypto_suite;
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK, we're now doing DTLS (RFC 5764)
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2);
|
||||
|
||||
// RFC 5705 exporter using the RFC 5764 parameters
|
||||
if (!dtls_transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0,
|
||||
false, &dtls_buffer[0],
|
||||
dtls_buffer.size())) {
|
||||
RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed";
|
||||
RTC_DCHECK_NOTREACHED(); // This should never happen
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sync up the keys with the DTLS-SRTP interface
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> client_write_key(key_len + salt_len);
|
||||
rtc::ZeroOnFreeBuffer<unsigned char> server_write_key(key_len + salt_len);
|
||||
size_t offset = 0;
|
||||
memcpy(&client_write_key[0], &dtls_buffer[offset], key_len);
|
||||
offset += key_len;
|
||||
memcpy(&server_write_key[0], &dtls_buffer[offset], key_len);
|
||||
offset += key_len;
|
||||
memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len);
|
||||
offset += salt_len;
|
||||
memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
|
||||
|
||||
rtc::SSLRole role;
|
||||
if (!dtls_transport->GetDtlsRole(&role)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to get the DTLS role.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == rtc::SSL_SERVER) {
|
||||
*send_key = std::move(server_write_key);
|
||||
*recv_key = std::move(client_write_key);
|
||||
} else {
|
||||
*send_key = std::move(client_write_key);
|
||||
*recv_key = std::move(server_write_key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetDtlsTransport(
|
||||
cricket::DtlsTransportInternal* new_dtls_transport,
|
||||
cricket::DtlsTransportInternal** old_dtls_transport) {
|
||||
if (*old_dtls_transport == new_dtls_transport) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*old_dtls_transport) {
|
||||
(*old_dtls_transport)->UnsubscribeDtlsTransportState(this);
|
||||
}
|
||||
|
||||
*old_dtls_transport = new_dtls_transport;
|
||||
|
||||
if (new_dtls_transport) {
|
||||
new_dtls_transport->SubscribeDtlsTransportState(
|
||||
this,
|
||||
[this](cricket::DtlsTransportInternal* transport,
|
||||
DtlsTransportState state) { OnDtlsState(transport, state); });
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetRtpDtlsTransport(
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport) {
|
||||
SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_);
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetRtcpDtlsTransport(
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
|
||||
SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_);
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::OnDtlsState(cricket::DtlsTransportInternal* transport,
|
||||
DtlsTransportState state) {
|
||||
RTC_DCHECK(transport == rtp_dtls_transport_ ||
|
||||
transport == rtcp_dtls_transport_);
|
||||
|
||||
if (on_dtls_state_change_) {
|
||||
on_dtls_state_change_();
|
||||
}
|
||||
|
||||
if (state != DtlsTransportState::kConnected) {
|
||||
ResetParams();
|
||||
return;
|
||||
}
|
||||
|
||||
MaybeSetupDtlsSrtp();
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::OnWritableState(
|
||||
rtc::PacketTransportInternal* packet_transport) {
|
||||
MaybeSetupDtlsSrtp();
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::SetOnDtlsStateChange(
|
||||
std::function<void(void)> callback) {
|
||||
on_dtls_state_change_ = std::move(callback);
|
||||
}
|
||||
} // namespace webrtc
|
||||
95
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport.h
Normal file
95
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_DTLS_SRTP_TRANSPORT_H_
|
||||
#define PC_DTLS_SRTP_TRANSPORT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "pc/srtp_transport.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The subclass of SrtpTransport is used for DTLS-SRTP. When the DTLS handshake
|
||||
// is finished, it extracts the keying materials from DtlsTransport and
|
||||
// configures the SrtpSessions in the base class.
|
||||
class DtlsSrtpTransport : public SrtpTransport {
|
||||
public:
|
||||
DtlsSrtpTransport(bool rtcp_mux_enabled, const FieldTrialsView& field_trials);
|
||||
|
||||
// Set P2P layer RTP/RTCP DtlsTransports. When using RTCP-muxing,
|
||||
// `rtcp_dtls_transport` is null.
|
||||
void SetDtlsTransports(cricket::DtlsTransportInternal* rtp_dtls_transport,
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport);
|
||||
|
||||
void SetRtcpMuxEnabled(bool enable) override;
|
||||
|
||||
// Set the header extension ids that should be encrypted.
|
||||
void UpdateSendEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& send_extension_ids);
|
||||
|
||||
void UpdateRecvEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& recv_extension_ids);
|
||||
|
||||
void SetOnDtlsStateChange(std::function<void(void)> callback);
|
||||
|
||||
// If `active_reset_srtp_params_` is set to be true, the SRTP parameters will
|
||||
// be reset whenever the DtlsTransports are reset.
|
||||
void SetActiveResetSrtpParams(bool active_reset_srtp_params) {
|
||||
active_reset_srtp_params_ = active_reset_srtp_params;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsDtlsActive();
|
||||
bool IsDtlsConnected();
|
||||
bool IsDtlsWritable();
|
||||
bool DtlsHandshakeCompleted();
|
||||
void MaybeSetupDtlsSrtp();
|
||||
void SetupRtpDtlsSrtp();
|
||||
void SetupRtcpDtlsSrtp();
|
||||
bool ExtractParams(cricket::DtlsTransportInternal* dtls_transport,
|
||||
int* selected_crypto_suite,
|
||||
rtc::ZeroOnFreeBuffer<unsigned char>* send_key,
|
||||
rtc::ZeroOnFreeBuffer<unsigned char>* recv_key);
|
||||
void SetDtlsTransport(cricket::DtlsTransportInternal* new_dtls_transport,
|
||||
cricket::DtlsTransportInternal** old_dtls_transport);
|
||||
void SetRtpDtlsTransport(cricket::DtlsTransportInternal* rtp_dtls_transport);
|
||||
void SetRtcpDtlsTransport(
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport);
|
||||
|
||||
void OnDtlsState(cricket::DtlsTransportInternal* dtls_transport,
|
||||
DtlsTransportState state);
|
||||
|
||||
// Override the SrtpTransport::OnWritableState.
|
||||
void OnWritableState(rtc::PacketTransportInternal* packet_transport) override;
|
||||
|
||||
// Owned by the TransportController.
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
|
||||
|
||||
// The encrypted header extension IDs.
|
||||
absl::optional<std::vector<int>> send_extension_ids_;
|
||||
absl::optional<std::vector<int>> recv_extension_ids_;
|
||||
|
||||
bool active_reset_srtp_params_ = false;
|
||||
std::function<void(void)> on_dtls_state_change_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_DTLS_SRTP_TRANSPORT_H_
|
||||
576
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport_unittest.cc
Normal file
576
TMessagesProj/jni/voip/webrtc/pc/dtls_srtp_transport_unittest.cc
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
* 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 "pc/dtls_srtp_transport.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "call/rtp_demuxer.h"
|
||||
#include "media/base/fake_rtp.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/fake_dtls_transport.h"
|
||||
#include "p2p/base/fake_ice_transport.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "pc/rtp_transport.h"
|
||||
#include "pc/test/rtp_transport_test_util.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/byte_order.h"
|
||||
#include "rtc_base/containers/flat_set.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_identity.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
using cricket::FakeDtlsTransport;
|
||||
using cricket::FakeIceTransport;
|
||||
using webrtc::DtlsSrtpTransport;
|
||||
using webrtc::RtpTransport;
|
||||
using webrtc::SrtpTransport;
|
||||
|
||||
const int kRtpAuthTagLen = 10;
|
||||
|
||||
class DtlsSrtpTransportTest : public ::testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
protected:
|
||||
DtlsSrtpTransportTest() {}
|
||||
|
||||
~DtlsSrtpTransportTest() {
|
||||
if (dtls_srtp_transport1_) {
|
||||
dtls_srtp_transport1_->UnregisterRtpDemuxerSink(&transport_observer1_);
|
||||
}
|
||||
if (dtls_srtp_transport2_) {
|
||||
dtls_srtp_transport2_->UnregisterRtpDemuxerSink(&transport_observer2_);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<DtlsSrtpTransport> MakeDtlsSrtpTransport(
|
||||
FakeDtlsTransport* rtp_dtls,
|
||||
FakeDtlsTransport* rtcp_dtls,
|
||||
bool rtcp_mux_enabled) {
|
||||
auto dtls_srtp_transport =
|
||||
std::make_unique<DtlsSrtpTransport>(rtcp_mux_enabled, field_trials_);
|
||||
|
||||
dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls);
|
||||
|
||||
return dtls_srtp_transport;
|
||||
}
|
||||
|
||||
void MakeDtlsSrtpTransports(FakeDtlsTransport* rtp_dtls1,
|
||||
FakeDtlsTransport* rtcp_dtls1,
|
||||
FakeDtlsTransport* rtp_dtls2,
|
||||
FakeDtlsTransport* rtcp_dtls2,
|
||||
bool rtcp_mux_enabled) {
|
||||
dtls_srtp_transport1_ =
|
||||
MakeDtlsSrtpTransport(rtp_dtls1, rtcp_dtls1, rtcp_mux_enabled);
|
||||
dtls_srtp_transport2_ =
|
||||
MakeDtlsSrtpTransport(rtp_dtls2, rtcp_dtls2, rtcp_mux_enabled);
|
||||
|
||||
dtls_srtp_transport1_->SubscribeRtcpPacketReceived(
|
||||
&transport_observer1_,
|
||||
[this](rtc::CopyOnWriteBuffer* buffer, int64_t packet_time_ms) {
|
||||
transport_observer1_.OnRtcpPacketReceived(buffer, packet_time_ms);
|
||||
});
|
||||
dtls_srtp_transport1_->SubscribeReadyToSend(
|
||||
&transport_observer1_,
|
||||
[this](bool ready) { transport_observer1_.OnReadyToSend(ready); });
|
||||
|
||||
dtls_srtp_transport2_->SubscribeRtcpPacketReceived(
|
||||
&transport_observer2_,
|
||||
[this](rtc::CopyOnWriteBuffer* buffer, int64_t packet_time_ms) {
|
||||
transport_observer2_.OnRtcpPacketReceived(buffer, packet_time_ms);
|
||||
});
|
||||
dtls_srtp_transport2_->SubscribeReadyToSend(
|
||||
&transport_observer2_,
|
||||
[this](bool ready) { transport_observer2_.OnReadyToSend(ready); });
|
||||
webrtc::RtpDemuxerCriteria demuxer_criteria;
|
||||
// 0x00 is the payload type used in kPcmuFrame.
|
||||
demuxer_criteria.payload_types() = {0x00};
|
||||
dtls_srtp_transport1_->RegisterRtpDemuxerSink(demuxer_criteria,
|
||||
&transport_observer1_);
|
||||
dtls_srtp_transport2_->RegisterRtpDemuxerSink(demuxer_criteria,
|
||||
&transport_observer2_);
|
||||
}
|
||||
|
||||
void CompleteDtlsHandshake(FakeDtlsTransport* fake_dtls1,
|
||||
FakeDtlsTransport* fake_dtls2) {
|
||||
auto cert1 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
fake_dtls1->SetLocalCertificate(cert1);
|
||||
auto cert2 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
fake_dtls2->SetLocalCertificate(cert2);
|
||||
fake_dtls1->SetDestination(fake_dtls2);
|
||||
}
|
||||
|
||||
void SendRecvRtpPackets() {
|
||||
ASSERT_TRUE(dtls_srtp_transport1_);
|
||||
ASSERT_TRUE(dtls_srtp_transport2_);
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
|
||||
size_t rtp_len = sizeof(kPcmuFrame);
|
||||
size_t packet_size = rtp_len + kRtpAuthTagLen;
|
||||
rtc::Buffer rtp_packet_buffer(packet_size);
|
||||
char* rtp_packet_data = rtp_packet_buffer.data<char>();
|
||||
memcpy(rtp_packet_data, kPcmuFrame, rtp_len);
|
||||
// In order to be able to run this test function multiple times we can not
|
||||
// use the same sequence number twice. Increase the sequence number by one.
|
||||
rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
|
||||
++sequence_number_);
|
||||
rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
|
||||
packet_size);
|
||||
rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
|
||||
packet_size);
|
||||
|
||||
rtc::PacketOptions options;
|
||||
// Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
|
||||
// that the packet can be successfully received and decrypted.
|
||||
int prev_received_packets = transport_observer2_.rtp_count();
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
|
||||
kPcmuFrame, rtp_len));
|
||||
EXPECT_EQ(prev_received_packets + 1, transport_observer2_.rtp_count());
|
||||
|
||||
prev_received_packets = transport_observer1_.rtp_count();
|
||||
ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
|
||||
kPcmuFrame, rtp_len));
|
||||
EXPECT_EQ(prev_received_packets + 1, transport_observer1_.rtp_count());
|
||||
}
|
||||
|
||||
void SendRecvRtcpPackets() {
|
||||
size_t rtcp_len = sizeof(kRtcpReport);
|
||||
size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen;
|
||||
rtc::Buffer rtcp_packet_buffer(packet_size);
|
||||
|
||||
// TODO(zhihuang): Remove the extra copy when the SendRtpPacket method
|
||||
// doesn't take the CopyOnWriteBuffer by pointer.
|
||||
rtc::CopyOnWriteBuffer rtcp_packet1to2(kRtcpReport, rtcp_len, packet_size);
|
||||
rtc::CopyOnWriteBuffer rtcp_packet2to1(kRtcpReport, rtcp_len, packet_size);
|
||||
|
||||
rtc::PacketOptions options;
|
||||
// Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
|
||||
// that the packet can be successfully received and decrypted.
|
||||
int prev_received_packets = transport_observer2_.rtcp_count();
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(&rtcp_packet1to2, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer2_.last_recv_rtcp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtcp_packet().data(),
|
||||
kRtcpReport, rtcp_len));
|
||||
EXPECT_EQ(prev_received_packets + 1, transport_observer2_.rtcp_count());
|
||||
|
||||
// Do the same thing in the opposite direction;
|
||||
prev_received_packets = transport_observer1_.rtcp_count();
|
||||
ASSERT_TRUE(dtls_srtp_transport2_->SendRtcpPacket(&rtcp_packet2to1, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer1_.last_recv_rtcp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtcp_packet().data(),
|
||||
kRtcpReport, rtcp_len));
|
||||
EXPECT_EQ(prev_received_packets + 1, transport_observer1_.rtcp_count());
|
||||
}
|
||||
|
||||
void SendRecvRtpPacketsWithHeaderExtension(
|
||||
const std::vector<int>& encrypted_header_ids) {
|
||||
ASSERT_TRUE(dtls_srtp_transport1_);
|
||||
ASSERT_TRUE(dtls_srtp_transport2_);
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
|
||||
size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
|
||||
size_t packet_size = rtp_len + kRtpAuthTagLen;
|
||||
rtc::Buffer rtp_packet_buffer(packet_size);
|
||||
char* rtp_packet_data = rtp_packet_buffer.data<char>();
|
||||
memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len);
|
||||
// In order to be able to run this test function multiple times we can not
|
||||
// use the same sequence number twice. Increase the sequence number by one.
|
||||
rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
|
||||
++sequence_number_);
|
||||
rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
|
||||
packet_size);
|
||||
rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
|
||||
packet_size);
|
||||
|
||||
char original_rtp_data[sizeof(kPcmuFrameWithExtensions)];
|
||||
memcpy(original_rtp_data, rtp_packet_data, rtp_len);
|
||||
|
||||
rtc::PacketOptions options;
|
||||
// Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
|
||||
// that the packet can be successfully received and decrypted.
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
|
||||
original_rtp_data, rtp_len));
|
||||
// Get the encrypted packet from underneath packet transport and verify the
|
||||
// data and header extension are actually encrypted.
|
||||
auto fake_dtls_transport = static_cast<FakeDtlsTransport*>(
|
||||
dtls_srtp_transport1_->rtp_packet_transport());
|
||||
auto fake_ice_transport =
|
||||
static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
|
||||
EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
|
||||
original_rtp_data, rtp_len));
|
||||
CompareHeaderExtensions(reinterpret_cast<const char*>(
|
||||
fake_ice_transport->last_sent_packet().data()),
|
||||
fake_ice_transport->last_sent_packet().size(),
|
||||
original_rtp_data, rtp_len, encrypted_header_ids,
|
||||
false);
|
||||
|
||||
// Do the same thing in the opposite direction.
|
||||
ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
|
||||
cricket::PF_SRTP_BYPASS));
|
||||
ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
|
||||
EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
|
||||
original_rtp_data, rtp_len));
|
||||
// Get the encrypted packet from underneath packet transport and verify the
|
||||
// data and header extension are actually encrypted.
|
||||
fake_dtls_transport = static_cast<FakeDtlsTransport*>(
|
||||
dtls_srtp_transport2_->rtp_packet_transport());
|
||||
fake_ice_transport =
|
||||
static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
|
||||
EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
|
||||
original_rtp_data, rtp_len));
|
||||
CompareHeaderExtensions(reinterpret_cast<const char*>(
|
||||
fake_ice_transport->last_sent_packet().data()),
|
||||
fake_ice_transport->last_sent_packet().size(),
|
||||
original_rtp_data, rtp_len, encrypted_header_ids,
|
||||
false);
|
||||
}
|
||||
|
||||
void SendRecvPackets() {
|
||||
SendRecvRtpPackets();
|
||||
SendRecvRtcpPackets();
|
||||
}
|
||||
|
||||
rtc::AutoThread main_thread_;
|
||||
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport1_;
|
||||
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport2_;
|
||||
webrtc::TransportObserver transport_observer1_;
|
||||
webrtc::TransportObserver transport_observer2_;
|
||||
|
||||
int sequence_number_ = 0;
|
||||
webrtc::test::ScopedKeyValueConfig field_trials_;
|
||||
};
|
||||
|
||||
// Tests that if RTCP muxing is enabled and transports are set after RTP
|
||||
// transport finished the handshake, SRTP is set up.
|
||||
TEST_F(DtlsSrtpTransportTest, SetTransportsAfterHandshakeCompleteWithRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
|
||||
/*rtcp_mux_enabled=*/true);
|
||||
|
||||
auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
|
||||
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
|
||||
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
|
||||
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests that if RTCP muxing is not enabled and transports are set after both
|
||||
// RTP and RTCP transports finished the handshake, SRTP is set up.
|
||||
TEST_F(DtlsSrtpTransportTest,
|
||||
SetTransportsAfterHandshakeCompleteWithoutRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
|
||||
|
||||
auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls3 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls4 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
|
||||
CompleteDtlsHandshake(rtcp_dtls3.get(), rtcp_dtls4.get());
|
||||
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), rtcp_dtls3.get());
|
||||
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), rtcp_dtls4.get());
|
||||
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests if RTCP muxing is enabled, SRTP is set up as soon as the RTP DTLS
|
||||
// handshake is finished.
|
||||
TEST_F(DtlsSrtpTransportTest, SetTransportsBeforeHandshakeCompleteWithRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(),
|
||||
/*rtcp_mux_enabled=*/false);
|
||||
|
||||
dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
|
||||
dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests if RTCP muxing is not enabled, SRTP is set up when both the RTP and
|
||||
// RTCP DTLS handshake are finished.
|
||||
TEST_F(DtlsSrtpTransportTest,
|
||||
SetTransportsBeforeHandshakeCompleteWithoutRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
|
||||
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests that if the DtlsTransport underneath is changed, the previous DTLS-SRTP
|
||||
// context will be reset and will be re-setup once the new transports' handshake
|
||||
// complete.
|
||||
TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
|
||||
/*rtcp_mux_enabled=*/true);
|
||||
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
EXPECT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
EXPECT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
|
||||
auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
// The previous context is reset.
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
|
||||
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
|
||||
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
|
||||
// Re-setup.
|
||||
CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests if only the RTP DTLS handshake complete, and then RTCP muxing is
|
||||
// enabled, SRTP is set up.
|
||||
TEST_F(DtlsSrtpTransportTest,
|
||||
RtcpMuxEnabledAfterRtpTransportHandshakeComplete) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
|
||||
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
// Inactive because the RTCP transport handshake didn't complete.
|
||||
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
|
||||
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
|
||||
|
||||
dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
|
||||
dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
|
||||
// The transports should be active and be able to send packets when the
|
||||
// RTCP muxing is enabled.
|
||||
SendRecvPackets();
|
||||
}
|
||||
|
||||
// Tests that when SetSend/RecvEncryptedHeaderExtensionIds is called, the SRTP
|
||||
// sessions are updated with new encryped header extension IDs immediately.
|
||||
TEST_F(DtlsSrtpTransportTest, EncryptedHeaderExtensionIdUpdated) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
|
||||
/*rtcp_mux_enabled=*/true);
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
|
||||
std::vector<int> encrypted_headers;
|
||||
encrypted_headers.push_back(kHeaderExtensionIDs[0]);
|
||||
encrypted_headers.push_back(kHeaderExtensionIDs[1]);
|
||||
|
||||
dtls_srtp_transport1_->UpdateSendEncryptedHeaderExtensionIds(
|
||||
encrypted_headers);
|
||||
dtls_srtp_transport1_->UpdateRecvEncryptedHeaderExtensionIds(
|
||||
encrypted_headers);
|
||||
dtls_srtp_transport2_->UpdateSendEncryptedHeaderExtensionIds(
|
||||
encrypted_headers);
|
||||
dtls_srtp_transport2_->UpdateRecvEncryptedHeaderExtensionIds(
|
||||
encrypted_headers);
|
||||
}
|
||||
|
||||
// Tests if RTCP muxing is enabled. DtlsSrtpTransport is ready to send once the
|
||||
// RTP DtlsTransport is ready.
|
||||
TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
|
||||
/*rtcp_mux_enabled=*/true);
|
||||
|
||||
rtp_dtls1->SetDestination(rtp_dtls2.get());
|
||||
EXPECT_TRUE(transport_observer1_.ready_to_send());
|
||||
EXPECT_TRUE(transport_observer2_.ready_to_send());
|
||||
}
|
||||
|
||||
// Tests if RTCP muxing is not enabled. DtlsSrtpTransport is ready to send once
|
||||
// both the RTP and RTCP DtlsTransport are ready.
|
||||
TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithoutRtcpMux) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
|
||||
|
||||
rtp_dtls1->SetDestination(rtp_dtls2.get());
|
||||
EXPECT_FALSE(transport_observer1_.ready_to_send());
|
||||
EXPECT_FALSE(transport_observer2_.ready_to_send());
|
||||
|
||||
rtcp_dtls1->SetDestination(rtcp_dtls2.get());
|
||||
EXPECT_TRUE(transport_observer1_.ready_to_send());
|
||||
EXPECT_TRUE(transport_observer2_.ready_to_send());
|
||||
}
|
||||
|
||||
// Test that if an endpoint "fully" enables RTCP mux, setting the RTCP
|
||||
// transport to null, it *doesn't* reset its SRTP context. That would cause the
|
||||
// ROC and SRTCP index to be reset, causing replay detection and other errors
|
||||
// when attempting to unprotect packets.
|
||||
// Regression test for bugs.webrtc.org/8996
|
||||
TEST_F(DtlsSrtpTransportTest, SrtpSessionNotResetWhenRtcpTransportRemoved) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/true);
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
|
||||
|
||||
// Send some RTCP packets, causing the SRTCP index to be incremented.
|
||||
SendRecvRtcpPackets();
|
||||
|
||||
// Set RTCP transport to null, which previously would trigger this problem.
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
|
||||
|
||||
// Attempt to send more RTCP packets. If the issue occurred, one side would
|
||||
// reset its context while the other would not, causing replay detection
|
||||
// errors when a packet with a duplicate SRTCP index is received.
|
||||
SendRecvRtcpPackets();
|
||||
}
|
||||
|
||||
// Tests that RTCP packets can be sent and received if both sides actively reset
|
||||
// the SRTP parameters with the `active_reset_srtp_params_` flag.
|
||||
TEST_F(DtlsSrtpTransportTest, ActivelyResetSrtpParams) {
|
||||
auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
|
||||
MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
|
||||
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/true);
|
||||
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
|
||||
CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
|
||||
|
||||
// Send some RTCP packets, causing the SRTCP index to be incremented.
|
||||
SendRecvRtcpPackets();
|
||||
|
||||
// Only set the `active_reset_srtp_params_` flag to be true one side.
|
||||
dtls_srtp_transport1_->SetActiveResetSrtpParams(true);
|
||||
// Set RTCP transport to null to trigger the SRTP parameters update.
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
|
||||
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls2.get(), nullptr);
|
||||
|
||||
// Sending some RTCP packets.
|
||||
size_t rtcp_len = sizeof(kRtcpReport);
|
||||
size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen;
|
||||
rtc::Buffer rtcp_packet_buffer(packet_size);
|
||||
rtc::CopyOnWriteBuffer rtcp_packet(kRtcpReport, rtcp_len, packet_size);
|
||||
int prev_received_packets = transport_observer2_.rtcp_count();
|
||||
ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(
|
||||
&rtcp_packet, rtc::PacketOptions(), cricket::PF_SRTP_BYPASS));
|
||||
// The RTCP packet is not exepected to be received because the SRTP parameters
|
||||
// are only reset on one side and the SRTCP index is out of sync.
|
||||
EXPECT_EQ(prev_received_packets, transport_observer2_.rtcp_count());
|
||||
|
||||
// Set the flag to be true on the other side.
|
||||
dtls_srtp_transport2_->SetActiveResetSrtpParams(true);
|
||||
// Set RTCP transport to null to trigger the SRTP parameters update.
|
||||
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
|
||||
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls2.get(), nullptr);
|
||||
|
||||
// RTCP packets flow is expected to work just fine.
|
||||
SendRecvRtcpPackets();
|
||||
}
|
||||
152
TMessagesProj/jni/voip/webrtc/pc/dtls_transport.cc
Normal file
152
TMessagesProj/jni/voip/webrtc/pc/dtls_transport.cc
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2018 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 "pc/dtls_transport.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "pc/ice_transport.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Implementation of DtlsTransportInterface
|
||||
DtlsTransport::DtlsTransport(
|
||||
std::unique_ptr<cricket::DtlsTransportInternal> internal)
|
||||
: owner_thread_(rtc::Thread::Current()),
|
||||
info_(DtlsTransportState::kNew),
|
||||
internal_dtls_transport_(std::move(internal)),
|
||||
ice_transport_(rtc::make_ref_counted<IceTransportWithPointer>(
|
||||
internal_dtls_transport_->ice_transport())) {
|
||||
RTC_DCHECK(internal_dtls_transport_.get());
|
||||
internal_dtls_transport_->SubscribeDtlsTransportState(
|
||||
[this](cricket::DtlsTransportInternal* transport,
|
||||
DtlsTransportState state) {
|
||||
OnInternalDtlsState(transport, state);
|
||||
});
|
||||
UpdateInformation();
|
||||
}
|
||||
|
||||
DtlsTransport::~DtlsTransport() {
|
||||
// TODO(tommi): Due to a reference being held by the RtpSenderBase
|
||||
// implementation, the last reference to the `DtlsTransport` instance can
|
||||
// be released on the signaling thread.
|
||||
// RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
|
||||
// We depend on the signaling thread to call Clear() before dropping
|
||||
// its last reference to this object.
|
||||
|
||||
// If there are non `owner_thread_` references outstanding, and those
|
||||
// references are the last ones released, we depend on Clear() having been
|
||||
// called from the owner_thread before the last reference is deleted.
|
||||
// `Clear()` is currently called from `JsepTransport::~JsepTransport`.
|
||||
RTC_DCHECK(owner_thread_->IsCurrent() || !internal_dtls_transport_);
|
||||
}
|
||||
|
||||
DtlsTransportInformation DtlsTransport::Information() {
|
||||
MutexLock lock(&lock_);
|
||||
return info_;
|
||||
}
|
||||
|
||||
void DtlsTransport::RegisterObserver(DtlsTransportObserverInterface* observer) {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
RTC_DCHECK(observer);
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
void DtlsTransport::UnregisterObserver() {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
observer_ = nullptr;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<IceTransportInterface> DtlsTransport::ice_transport() {
|
||||
return ice_transport_;
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
void DtlsTransport::Clear() {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
RTC_DCHECK(internal());
|
||||
bool must_send_event =
|
||||
(internal()->dtls_state() != DtlsTransportState::kClosed);
|
||||
internal_dtls_transport_.reset();
|
||||
ice_transport_->Clear();
|
||||
UpdateInformation();
|
||||
if (observer_ && must_send_event) {
|
||||
observer_->OnStateChange(Information());
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnInternalDtlsState(
|
||||
cricket::DtlsTransportInternal* transport,
|
||||
DtlsTransportState state) {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
RTC_DCHECK(transport == internal());
|
||||
RTC_DCHECK(state == internal()->dtls_state());
|
||||
UpdateInformation();
|
||||
if (observer_) {
|
||||
observer_->OnStateChange(Information());
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::UpdateInformation() {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
if (internal_dtls_transport_) {
|
||||
if (internal_dtls_transport_->dtls_state() ==
|
||||
DtlsTransportState::kConnected) {
|
||||
bool success = true;
|
||||
rtc::SSLRole internal_role;
|
||||
absl::optional<DtlsTransportTlsRole> role;
|
||||
int ssl_cipher_suite;
|
||||
int tls_version;
|
||||
int srtp_cipher;
|
||||
success &= internal_dtls_transport_->GetDtlsRole(&internal_role);
|
||||
if (success) {
|
||||
switch (internal_role) {
|
||||
case rtc::SSL_CLIENT:
|
||||
role = DtlsTransportTlsRole::kClient;
|
||||
break;
|
||||
case rtc::SSL_SERVER:
|
||||
role = DtlsTransportTlsRole::kServer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
success &= internal_dtls_transport_->GetSslVersionBytes(&tls_version);
|
||||
success &= internal_dtls_transport_->GetSslCipherSuite(&ssl_cipher_suite);
|
||||
success &= internal_dtls_transport_->GetSrtpCryptoSuite(&srtp_cipher);
|
||||
if (success) {
|
||||
set_info(DtlsTransportInformation(
|
||||
internal_dtls_transport_->dtls_state(), role, tls_version,
|
||||
ssl_cipher_suite, srtp_cipher,
|
||||
internal_dtls_transport_->GetRemoteSSLCertChain()));
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "DtlsTransport in connected state has incomplete "
|
||||
"TLS information";
|
||||
set_info(DtlsTransportInformation(
|
||||
internal_dtls_transport_->dtls_state(), role, absl::nullopt,
|
||||
absl::nullopt, absl::nullopt,
|
||||
internal_dtls_transport_->GetRemoteSSLCertChain()));
|
||||
}
|
||||
} else {
|
||||
set_info(
|
||||
DtlsTransportInformation(internal_dtls_transport_->dtls_state()));
|
||||
}
|
||||
} else {
|
||||
set_info(DtlsTransportInformation(DtlsTransportState::kClosed));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
88
TMessagesProj/jni/voip/webrtc/pc/dtls_transport.h
Normal file
88
TMessagesProj/jni/voip/webrtc/pc/dtls_transport.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_DTLS_TRANSPORT_H_
|
||||
#define PC_DTLS_TRANSPORT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "p2p/base/dtls_transport.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "pc/ice_transport.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class IceTransportWithPointer;
|
||||
|
||||
// This implementation wraps a cricket::DtlsTransport, and takes
|
||||
// ownership of it.
|
||||
class DtlsTransport : public DtlsTransportInterface {
|
||||
public:
|
||||
// This object must be constructed and updated on a consistent thread,
|
||||
// the same thread as the one the cricket::DtlsTransportInternal object
|
||||
// lives on.
|
||||
// The Information() function can be called from a different thread,
|
||||
// such as the signalling thread.
|
||||
explicit DtlsTransport(
|
||||
std::unique_ptr<cricket::DtlsTransportInternal> internal);
|
||||
|
||||
rtc::scoped_refptr<IceTransportInterface> ice_transport() override;
|
||||
|
||||
// Currently called from the signaling thread and potentially Chromium's
|
||||
// JS thread.
|
||||
DtlsTransportInformation Information() override;
|
||||
|
||||
void RegisterObserver(DtlsTransportObserverInterface* observer) override;
|
||||
void UnregisterObserver() override;
|
||||
void Clear();
|
||||
|
||||
cricket::DtlsTransportInternal* internal() {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
return internal_dtls_transport_.get();
|
||||
}
|
||||
|
||||
const cricket::DtlsTransportInternal* internal() const {
|
||||
RTC_DCHECK_RUN_ON(owner_thread_);
|
||||
return internal_dtls_transport_.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
~DtlsTransport();
|
||||
|
||||
private:
|
||||
void OnInternalDtlsState(cricket::DtlsTransportInternal* transport,
|
||||
DtlsTransportState state);
|
||||
void UpdateInformation();
|
||||
|
||||
// Called when changing `info_`. We only change the values from the
|
||||
// `owner_thread_` (a.k.a. the network thread).
|
||||
void set_info(DtlsTransportInformation&& info) RTC_RUN_ON(owner_thread_) {
|
||||
MutexLock lock(&lock_);
|
||||
info_ = std::move(info);
|
||||
}
|
||||
|
||||
DtlsTransportObserverInterface* observer_ = nullptr;
|
||||
rtc::Thread* owner_thread_;
|
||||
mutable Mutex lock_;
|
||||
DtlsTransportInformation info_ RTC_GUARDED_BY(lock_);
|
||||
std::unique_ptr<cricket::DtlsTransportInternal> internal_dtls_transport_
|
||||
RTC_GUARDED_BY(owner_thread_);
|
||||
const rtc::scoped_refptr<IceTransportWithPointer> ice_transport_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // PC_DTLS_TRANSPORT_H_
|
||||
181
TMessagesProj/jni/voip/webrtc/pc/dtls_transport_unittest.cc
Normal file
181
TMessagesProj/jni/voip/webrtc/pc/dtls_transport_unittest.cc
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright 2018 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 "pc/dtls_transport.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/fake_dtls_transport.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/fake_ssl_identity.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_identity.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
constexpr int kDefaultTimeout = 1000; // milliseconds
|
||||
constexpr int kNonsenseCipherSuite = 1234;
|
||||
|
||||
using cricket::FakeDtlsTransport;
|
||||
using ::testing::ElementsAre;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class TestDtlsTransportObserver : public DtlsTransportObserverInterface {
|
||||
public:
|
||||
void OnStateChange(DtlsTransportInformation info) override {
|
||||
state_change_called_ = true;
|
||||
states_.push_back(info.state());
|
||||
info_ = info;
|
||||
}
|
||||
|
||||
void OnError(RTCError error) override {}
|
||||
|
||||
DtlsTransportState state() {
|
||||
if (states_.size() > 0) {
|
||||
return states_[states_.size() - 1];
|
||||
} else {
|
||||
return DtlsTransportState::kNew;
|
||||
}
|
||||
}
|
||||
|
||||
bool state_change_called_ = false;
|
||||
DtlsTransportInformation info_;
|
||||
std::vector<DtlsTransportState> states_;
|
||||
};
|
||||
|
||||
class DtlsTransportTest : public ::testing::Test {
|
||||
public:
|
||||
DtlsTransport* transport() { return transport_.get(); }
|
||||
DtlsTransportObserverInterface* observer() { return &observer_; }
|
||||
|
||||
void CreateTransport(rtc::FakeSSLCertificate* certificate = nullptr) {
|
||||
auto cricket_transport = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
if (certificate) {
|
||||
cricket_transport->SetRemoteSSLCertificate(certificate);
|
||||
}
|
||||
cricket_transport->SetSslCipherSuite(kNonsenseCipherSuite);
|
||||
transport_ =
|
||||
rtc::make_ref_counted<DtlsTransport>(std::move(cricket_transport));
|
||||
}
|
||||
|
||||
void CompleteDtlsHandshake() {
|
||||
auto fake_dtls1 = static_cast<FakeDtlsTransport*>(transport_->internal());
|
||||
auto fake_dtls2 = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto cert1 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
fake_dtls1->SetLocalCertificate(cert1);
|
||||
auto cert2 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
fake_dtls2->SetLocalCertificate(cert2);
|
||||
fake_dtls1->SetDestination(fake_dtls2.get());
|
||||
}
|
||||
|
||||
rtc::AutoThread main_thread_;
|
||||
rtc::scoped_refptr<DtlsTransport> transport_;
|
||||
TestDtlsTransportObserver observer_;
|
||||
};
|
||||
|
||||
TEST_F(DtlsTransportTest, CreateClearDelete) {
|
||||
auto cricket_transport = std::make_unique<FakeDtlsTransport>(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto webrtc_transport =
|
||||
rtc::make_ref_counted<DtlsTransport>(std::move(cricket_transport));
|
||||
ASSERT_TRUE(webrtc_transport->internal());
|
||||
ASSERT_EQ(DtlsTransportState::kNew, webrtc_transport->Information().state());
|
||||
webrtc_transport->Clear();
|
||||
ASSERT_FALSE(webrtc_transport->internal());
|
||||
ASSERT_EQ(DtlsTransportState::kClosed,
|
||||
webrtc_transport->Information().state());
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, EventsObservedWhenConnecting) {
|
||||
CreateTransport();
|
||||
transport()->RegisterObserver(observer());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state_change_called_, kDefaultTimeout);
|
||||
EXPECT_THAT(
|
||||
observer_.states_,
|
||||
ElementsAre( // FakeDtlsTransport doesn't signal the "connecting" state.
|
||||
// TODO(hta): fix FakeDtlsTransport or file bug on it.
|
||||
// DtlsTransportState::kConnecting,
|
||||
DtlsTransportState::kConnected));
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, CloseWhenClearing) {
|
||||
CreateTransport();
|
||||
transport()->RegisterObserver(observer());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected,
|
||||
kDefaultTimeout);
|
||||
transport()->Clear();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kClosed,
|
||||
kDefaultTimeout);
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, RoleAppearsOnConnect) {
|
||||
rtc::FakeSSLCertificate fake_certificate("fake data");
|
||||
CreateTransport(&fake_certificate);
|
||||
transport()->RegisterObserver(observer());
|
||||
EXPECT_FALSE(transport()->Information().role());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected,
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE(observer_.info_.role());
|
||||
EXPECT_TRUE(transport()->Information().role());
|
||||
EXPECT_EQ(transport()->Information().role(), DtlsTransportTlsRole::kClient);
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, CertificateAppearsOnConnect) {
|
||||
rtc::FakeSSLCertificate fake_certificate("fake data");
|
||||
CreateTransport(&fake_certificate);
|
||||
transport()->RegisterObserver(observer());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected,
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE(observer_.info_.remote_ssl_certificates() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, CertificateDisappearsOnClose) {
|
||||
rtc::FakeSSLCertificate fake_certificate("fake data");
|
||||
CreateTransport(&fake_certificate);
|
||||
transport()->RegisterObserver(observer());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected,
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE(observer_.info_.remote_ssl_certificates() != nullptr);
|
||||
transport()->Clear();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kClosed,
|
||||
kDefaultTimeout);
|
||||
EXPECT_FALSE(observer_.info_.remote_ssl_certificates());
|
||||
}
|
||||
|
||||
TEST_F(DtlsTransportTest, CipherSuiteVisibleWhenConnected) {
|
||||
CreateTransport();
|
||||
transport()->RegisterObserver(observer());
|
||||
CompleteDtlsHandshake();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected,
|
||||
kDefaultTimeout);
|
||||
ASSERT_TRUE(observer_.info_.ssl_cipher_suite());
|
||||
EXPECT_EQ(kNonsenseCipherSuite, *observer_.info_.ssl_cipher_suite());
|
||||
transport()->Clear();
|
||||
ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kClosed,
|
||||
kDefaultTimeout);
|
||||
EXPECT_FALSE(observer_.info_.ssl_cipher_suite());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
243
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender.cc
Normal file
243
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender.cc
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright 2012 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 "pc/dtmf_sender.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// RFC4733
|
||||
// +-------+--------+------+---------+
|
||||
// | Event | Code | Type | Volume? |
|
||||
// +-------+--------+------+---------+
|
||||
// | 0--9 | 0--9 | tone | yes |
|
||||
// | * | 10 | tone | yes |
|
||||
// | # | 11 | tone | yes |
|
||||
// | A--D | 12--15 | tone | yes |
|
||||
// +-------+--------+------+---------+
|
||||
// The "," is a special event defined by the WebRTC spec. It means to delay for
|
||||
// 2 seconds before processing the next tone. We use -1 as its code.
|
||||
static const int kDtmfCommaDelay = -1;
|
||||
static const char kDtmfValidTones[] = ",0123456789*#ABCDabcd";
|
||||
static const char kDtmfTonesTable[] = ",0123456789*#ABCD";
|
||||
// The duration cannot be more than 6000ms or less than 40ms. The gap between
|
||||
// tones must be at least 50 ms.
|
||||
// Source for values: W3C WEBRTC specification.
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcdtmfsender-insertdtmf
|
||||
static const int kDtmfDefaultDurationMs = 100;
|
||||
static const int kDtmfMinDurationMs = 40;
|
||||
static const int kDtmfMaxDurationMs = 6000;
|
||||
static const int kDtmfDefaultGapMs = 50;
|
||||
static const int kDtmfMinGapMs = 30;
|
||||
|
||||
// Get DTMF code from the DTMF event character.
|
||||
bool GetDtmfCode(char tone, int* code) {
|
||||
// Convert a-d to A-D.
|
||||
char event = toupper(tone);
|
||||
const char* p = strchr(kDtmfTonesTable, event);
|
||||
if (!p) {
|
||||
return false;
|
||||
}
|
||||
*code = p - kDtmfTonesTable - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<DtmfSender> DtmfSender::Create(
|
||||
TaskQueueBase* signaling_thread,
|
||||
DtmfProviderInterface* provider) {
|
||||
if (!signaling_thread) {
|
||||
return nullptr;
|
||||
}
|
||||
return rtc::make_ref_counted<DtmfSender>(signaling_thread, provider);
|
||||
}
|
||||
|
||||
DtmfSender::DtmfSender(TaskQueueBase* signaling_thread,
|
||||
DtmfProviderInterface* provider)
|
||||
: observer_(nullptr),
|
||||
signaling_thread_(signaling_thread),
|
||||
provider_(provider),
|
||||
duration_(kDtmfDefaultDurationMs),
|
||||
inter_tone_gap_(kDtmfDefaultGapMs),
|
||||
comma_delay_(kDtmfDefaultCommaDelayMs) {
|
||||
RTC_DCHECK(signaling_thread_);
|
||||
RTC_DCHECK(provider_);
|
||||
}
|
||||
|
||||
void DtmfSender::OnDtmfProviderDestroyed() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
RTC_DLOG(LS_INFO) << "The Dtmf provider is deleted. Clear the sending queue.";
|
||||
StopSending();
|
||||
provider_ = nullptr;
|
||||
}
|
||||
|
||||
DtmfSender::~DtmfSender() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
StopSending();
|
||||
}
|
||||
|
||||
void DtmfSender::RegisterObserver(DtmfSenderObserverInterface* observer) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
void DtmfSender::UnregisterObserver() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
observer_ = nullptr;
|
||||
}
|
||||
|
||||
bool DtmfSender::CanInsertDtmf() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
if (!provider_) {
|
||||
return false;
|
||||
}
|
||||
return provider_->CanInsertDtmf();
|
||||
}
|
||||
|
||||
bool DtmfSender::InsertDtmf(const std::string& tones,
|
||||
int duration,
|
||||
int inter_tone_gap,
|
||||
int comma_delay) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
|
||||
if (duration > kDtmfMaxDurationMs || duration < kDtmfMinDurationMs ||
|
||||
inter_tone_gap < kDtmfMinGapMs || comma_delay < kDtmfMinGapMs) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "InsertDtmf is called with invalid duration or tones gap. "
|
||||
"The duration cannot be more than "
|
||||
<< kDtmfMaxDurationMs << "ms or less than " << kDtmfMinDurationMs
|
||||
<< "ms. The gap between tones must be at least " << kDtmfMinGapMs
|
||||
<< "ms.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanInsertDtmf()) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "InsertDtmf is called on DtmfSender that can't send DTMF.";
|
||||
return false;
|
||||
}
|
||||
|
||||
tones_ = tones;
|
||||
duration_ = duration;
|
||||
inter_tone_gap_ = inter_tone_gap;
|
||||
comma_delay_ = comma_delay;
|
||||
|
||||
// Cancel any remaining tasks for previous tones.
|
||||
if (safety_flag_) {
|
||||
safety_flag_->SetNotAlive();
|
||||
}
|
||||
safety_flag_ = PendingTaskSafetyFlag::Create();
|
||||
// Kick off a new DTMF task.
|
||||
QueueInsertDtmf(1 /*ms*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DtmfSender::tones() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return tones_;
|
||||
}
|
||||
|
||||
int DtmfSender::duration() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return duration_;
|
||||
}
|
||||
|
||||
int DtmfSender::inter_tone_gap() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return inter_tone_gap_;
|
||||
}
|
||||
|
||||
int DtmfSender::comma_delay() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
return comma_delay_;
|
||||
}
|
||||
|
||||
void DtmfSender::QueueInsertDtmf(uint32_t delay_ms) {
|
||||
signaling_thread_->PostDelayedHighPrecisionTask(
|
||||
SafeTask(safety_flag_,
|
||||
[this] {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
DoInsertDtmf();
|
||||
}),
|
||||
TimeDelta::Millis(delay_ms));
|
||||
}
|
||||
|
||||
void DtmfSender::DoInsertDtmf() {
|
||||
// Get the first DTMF tone from the tone buffer. Unrecognized characters will
|
||||
// be ignored and skipped.
|
||||
size_t first_tone_pos = tones_.find_first_of(kDtmfValidTones);
|
||||
int code = 0;
|
||||
if (first_tone_pos == std::string::npos) {
|
||||
tones_.clear();
|
||||
// Fire a “OnToneChange” event with an empty string and stop.
|
||||
if (observer_) {
|
||||
observer_->OnToneChange(std::string(), tones_);
|
||||
observer_->OnToneChange(std::string());
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
char tone = tones_[first_tone_pos];
|
||||
if (!GetDtmfCode(tone, &code)) {
|
||||
// The find_first_of(kDtmfValidTones) should have guarantee `tone` is
|
||||
// a valid DTMF tone.
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int tone_gap = inter_tone_gap_;
|
||||
if (code == kDtmfCommaDelay) {
|
||||
// Special case defined by WebRTC - By default, the character ',' indicates
|
||||
// a delay of 2 seconds before processing the next character in the tones
|
||||
// parameter. The comma delay can be set to a non default value via
|
||||
// InsertDtmf to comply with legacy WebRTC clients.
|
||||
tone_gap = comma_delay_;
|
||||
} else {
|
||||
if (!provider_) {
|
||||
RTC_LOG(LS_ERROR) << "The DtmfProvider has been destroyed.";
|
||||
return;
|
||||
}
|
||||
// The provider starts playout of the given tone on the
|
||||
// associated RTP media stream, using the appropriate codec.
|
||||
if (!provider_->InsertDtmf(code, duration_)) {
|
||||
RTC_LOG(LS_ERROR) << "The DtmfProvider can no longer send DTMF.";
|
||||
return;
|
||||
}
|
||||
// Wait for the number of milliseconds specified by `duration_`.
|
||||
tone_gap += duration_;
|
||||
}
|
||||
|
||||
// Fire a “OnToneChange” event with the tone that's just processed.
|
||||
if (observer_) {
|
||||
observer_->OnToneChange(tones_.substr(first_tone_pos, 1),
|
||||
tones_.substr(first_tone_pos + 1));
|
||||
observer_->OnToneChange(tones_.substr(first_tone_pos, 1));
|
||||
}
|
||||
|
||||
// Erase the unrecognized characters plus the tone that's just processed.
|
||||
tones_.erase(0, first_tone_pos + 1);
|
||||
|
||||
// Continue with the next tone.
|
||||
QueueInsertDtmf(tone_gap);
|
||||
}
|
||||
|
||||
void DtmfSender::StopSending() {
|
||||
if (safety_flag_) {
|
||||
safety_flag_->SetNotAlive();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
118
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender.h
Normal file
118
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_DTMF_SENDER_H_
|
||||
#define PC_DTMF_SENDER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/dtmf_sender_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "pc/proxy.h"
|
||||
#include "rtc_base/ref_count.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
// DtmfSender is the native implementation of the RTCDTMFSender defined by
|
||||
// the WebRTC W3C Editor's Draft.
|
||||
// https://w3c.github.io/webrtc-pc/#rtcdtmfsender
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This interface is called by DtmfSender to talk to the actual audio channel
|
||||
// to send DTMF.
|
||||
class DtmfProviderInterface {
|
||||
public:
|
||||
// Returns true if the audio sender is capable of sending DTMF. Otherwise
|
||||
// returns false.
|
||||
virtual bool CanInsertDtmf() = 0;
|
||||
// Sends DTMF `code`.
|
||||
// The `duration` indicates the length of the DTMF tone in ms.
|
||||
// Returns true on success and false on failure.
|
||||
virtual bool InsertDtmf(int code, int duration) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~DtmfProviderInterface() {}
|
||||
};
|
||||
|
||||
class DtmfSender : public DtmfSenderInterface {
|
||||
public:
|
||||
static rtc::scoped_refptr<DtmfSender> Create(TaskQueueBase* signaling_thread,
|
||||
DtmfProviderInterface* provider);
|
||||
|
||||
void OnDtmfProviderDestroyed();
|
||||
|
||||
// Implements DtmfSenderInterface.
|
||||
void RegisterObserver(DtmfSenderObserverInterface* observer) override;
|
||||
void UnregisterObserver() override;
|
||||
bool CanInsertDtmf() override;
|
||||
bool InsertDtmf(const std::string& tones,
|
||||
int duration,
|
||||
int inter_tone_gap,
|
||||
int comma_delay = kDtmfDefaultCommaDelayMs) override;
|
||||
std::string tones() const override;
|
||||
int duration() const override;
|
||||
int inter_tone_gap() const override;
|
||||
int comma_delay() const override;
|
||||
|
||||
protected:
|
||||
DtmfSender(TaskQueueBase* signaling_thread, DtmfProviderInterface* provider);
|
||||
virtual ~DtmfSender();
|
||||
|
||||
DtmfSender(const DtmfSender&) = delete;
|
||||
DtmfSender& operator=(const DtmfSender&) = delete;
|
||||
|
||||
private:
|
||||
DtmfSender();
|
||||
|
||||
void QueueInsertDtmf(uint32_t delay_ms) RTC_RUN_ON(signaling_thread_);
|
||||
|
||||
// The DTMF sending task.
|
||||
void DoInsertDtmf() RTC_RUN_ON(signaling_thread_);
|
||||
|
||||
void StopSending() RTC_RUN_ON(signaling_thread_);
|
||||
|
||||
DtmfSenderObserverInterface* observer_ RTC_GUARDED_BY(signaling_thread_);
|
||||
TaskQueueBase* const signaling_thread_;
|
||||
DtmfProviderInterface* provider_ RTC_GUARDED_BY(signaling_thread_);
|
||||
std::string tones_ RTC_GUARDED_BY(signaling_thread_);
|
||||
int duration_ RTC_GUARDED_BY(signaling_thread_);
|
||||
int inter_tone_gap_ RTC_GUARDED_BY(signaling_thread_);
|
||||
int comma_delay_ RTC_GUARDED_BY(signaling_thread_);
|
||||
|
||||
// For cancelling the tasks which feed the DTMF provider one tone at a time.
|
||||
rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag_ RTC_GUARDED_BY(
|
||||
signaling_thread_) RTC_PT_GUARDED_BY(signaling_thread_) = nullptr;
|
||||
};
|
||||
|
||||
// Define proxy for DtmfSenderInterface.
|
||||
BEGIN_PRIMARY_PROXY_MAP(DtmfSender)
|
||||
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
PROXY_METHOD1(void, RegisterObserver, DtmfSenderObserverInterface*)
|
||||
PROXY_METHOD0(void, UnregisterObserver)
|
||||
PROXY_METHOD0(bool, CanInsertDtmf)
|
||||
PROXY_METHOD4(bool, InsertDtmf, const std::string&, int, int, int)
|
||||
PROXY_CONSTMETHOD0(std::string, tones)
|
||||
PROXY_CONSTMETHOD0(int, duration)
|
||||
PROXY_CONSTMETHOD0(int, inter_tone_gap)
|
||||
PROXY_CONSTMETHOD0(int, comma_delay)
|
||||
END_PROXY_MAP(DtmfSender)
|
||||
|
||||
// Get DTMF code from the DTMF event character.
|
||||
bool GetDtmfCode(char tone, int* code);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_DTMF_SENDER_H_
|
||||
371
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender_unittest.cc
Normal file
371
TMessagesProj/jni/voip/webrtc/pc/dtmf_sender_unittest.cc
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Copyright 2012 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 "pc/dtmf_sender.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rtc_base/fake_clock.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using webrtc::DtmfProviderInterface;
|
||||
using webrtc::DtmfSender;
|
||||
using webrtc::DtmfSenderObserverInterface;
|
||||
|
||||
// TODO(deadbeef): Even though this test now uses a fake clock, it has a
|
||||
// generous 3-second timeout for every test case. The timeout could be tuned
|
||||
// to each test based on the tones sent, instead.
|
||||
static const int kMaxWaitMs = 3000;
|
||||
|
||||
class FakeDtmfObserver : public DtmfSenderObserverInterface {
|
||||
public:
|
||||
FakeDtmfObserver() : completed_(false) {}
|
||||
|
||||
// Implements DtmfSenderObserverInterface.
|
||||
void OnToneChange(const std::string& tone) override {
|
||||
tones_from_single_argument_callback_.push_back(tone);
|
||||
if (tone.empty()) {
|
||||
completed_ = true;
|
||||
}
|
||||
}
|
||||
void OnToneChange(const std::string& tone,
|
||||
const std::string& tone_buffer) override {
|
||||
tones_.push_back(tone);
|
||||
tones_remaining_ = tone_buffer;
|
||||
if (tone.empty()) {
|
||||
completed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// getters
|
||||
const std::vector<std::string>& tones() const { return tones_; }
|
||||
const std::vector<std::string>& tones_from_single_argument_callback() const {
|
||||
return tones_from_single_argument_callback_;
|
||||
}
|
||||
const std::string tones_remaining() { return tones_remaining_; }
|
||||
bool completed() const { return completed_; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> tones_;
|
||||
std::vector<std::string> tones_from_single_argument_callback_;
|
||||
std::string tones_remaining_;
|
||||
bool completed_;
|
||||
};
|
||||
|
||||
class FakeDtmfProvider : public DtmfProviderInterface {
|
||||
public:
|
||||
struct DtmfInfo {
|
||||
DtmfInfo(int code, int duration, int gap)
|
||||
: code(code), duration(duration), gap(gap) {}
|
||||
int code;
|
||||
int duration;
|
||||
int gap;
|
||||
};
|
||||
|
||||
FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
|
||||
|
||||
// Implements DtmfProviderInterface.
|
||||
bool CanInsertDtmf() override { return can_insert_; }
|
||||
|
||||
bool InsertDtmf(int code, int duration) override {
|
||||
int gap = 0;
|
||||
// TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
|
||||
// mockable and use a fake timer in the unit tests.
|
||||
if (last_insert_dtmf_call_ > 0) {
|
||||
gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
|
||||
}
|
||||
last_insert_dtmf_call_ = rtc::TimeMillis();
|
||||
|
||||
dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
|
||||
return true;
|
||||
}
|
||||
|
||||
// getter and setter
|
||||
const std::vector<DtmfInfo>& dtmf_info_queue() const {
|
||||
return dtmf_info_queue_;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
|
||||
|
||||
private:
|
||||
bool can_insert_ = false;
|
||||
std::vector<DtmfInfo> dtmf_info_queue_;
|
||||
int64_t last_insert_dtmf_call_;
|
||||
};
|
||||
|
||||
class DtmfSenderTest : public ::testing::Test {
|
||||
protected:
|
||||
DtmfSenderTest()
|
||||
: observer_(new FakeDtmfObserver()), provider_(new FakeDtmfProvider()) {
|
||||
provider_->SetCanInsertDtmf(true);
|
||||
dtmf_ = DtmfSender::Create(rtc::Thread::Current(), provider_.get());
|
||||
dtmf_->RegisterObserver(observer_.get());
|
||||
}
|
||||
|
||||
~DtmfSenderTest() {
|
||||
if (dtmf_.get()) {
|
||||
dtmf_->UnregisterObserver();
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a list of DtmfInfo from `tones`, `duration` and
|
||||
// `inter_tone_gap`.
|
||||
void GetDtmfInfoFromString(
|
||||
const std::string& tones,
|
||||
int duration,
|
||||
int inter_tone_gap,
|
||||
std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs,
|
||||
int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
|
||||
// Init extra_delay as -inter_tone_gap - duration to ensure the first
|
||||
// DtmfInfo's gap field will be 0.
|
||||
int extra_delay = -1 * (inter_tone_gap + duration);
|
||||
|
||||
std::string::const_iterator it = tones.begin();
|
||||
for (; it != tones.end(); ++it) {
|
||||
char tone = *it;
|
||||
int code = 0;
|
||||
webrtc::GetDtmfCode(tone, &code);
|
||||
if (tone == ',') {
|
||||
extra_delay = comma_delay;
|
||||
} else {
|
||||
dtmfs->push_back(FakeDtmfProvider::DtmfInfo(
|
||||
code, duration, duration + inter_tone_gap + extra_delay));
|
||||
extra_delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyExpectedState(const std::string& tones,
|
||||
int duration,
|
||||
int inter_tone_gap) {
|
||||
EXPECT_EQ(tones, dtmf_->tones());
|
||||
EXPECT_EQ(duration, dtmf_->duration());
|
||||
EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
|
||||
}
|
||||
|
||||
// Verify the provider got all the expected calls.
|
||||
void VerifyOnProvider(
|
||||
const std::string& tones,
|
||||
int duration,
|
||||
int inter_tone_gap,
|
||||
int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
|
||||
std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
|
||||
GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref,
|
||||
comma_delay);
|
||||
VerifyOnProvider(dtmf_queue_ref);
|
||||
}
|
||||
|
||||
void VerifyOnProvider(
|
||||
const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
|
||||
const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
|
||||
provider_->dtmf_info_queue();
|
||||
ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
|
||||
std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
|
||||
dtmf_queue_ref.begin();
|
||||
std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
|
||||
dtmf_queue.begin();
|
||||
while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
|
||||
EXPECT_EQ(it_ref->code, it->code);
|
||||
EXPECT_EQ(it_ref->duration, it->duration);
|
||||
// Allow ~10ms error (can be small since we're using a fake clock).
|
||||
EXPECT_GE(it_ref->gap, it->gap - 10);
|
||||
EXPECT_LE(it_ref->gap, it->gap + 10);
|
||||
++it_ref;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the observer got all the expected callbacks.
|
||||
void VerifyOnObserver(const std::string& tones_ref) {
|
||||
const std::vector<std::string>& tones = observer_->tones();
|
||||
// The observer will get an empty string at the end.
|
||||
EXPECT_EQ(tones_ref.size() + 1, tones.size());
|
||||
EXPECT_EQ(observer_->tones(),
|
||||
observer_->tones_from_single_argument_callback());
|
||||
EXPECT_TRUE(tones.back().empty());
|
||||
EXPECT_TRUE(observer_->tones_remaining().empty());
|
||||
std::string::const_iterator it_ref = tones_ref.begin();
|
||||
std::vector<std::string>::const_iterator it = tones.begin();
|
||||
while (it_ref != tones_ref.end() && it != tones.end()) {
|
||||
EXPECT_EQ(*it_ref, it->at(0));
|
||||
++it_ref;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
rtc::AutoThread main_thread_;
|
||||
std::unique_ptr<FakeDtmfObserver> observer_;
|
||||
std::unique_ptr<FakeDtmfProvider> provider_;
|
||||
rtc::scoped_refptr<DtmfSender> dtmf_;
|
||||
rtc::ScopedFakeClock fake_clock_;
|
||||
};
|
||||
|
||||
TEST_F(DtmfSenderTest, CanInsertDtmf) {
|
||||
EXPECT_TRUE(dtmf_->CanInsertDtmf());
|
||||
provider_->SetCanInsertDtmf(false);
|
||||
EXPECT_FALSE(dtmf_->CanInsertDtmf());
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmf) {
|
||||
std::string tones = "@1%a&*$";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
|
||||
|
||||
// The unrecognized characters should be ignored.
|
||||
std::string known_tones = "1a*";
|
||||
VerifyOnProvider(known_tones, duration, inter_tone_gap);
|
||||
VerifyOnObserver(known_tones);
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfTwice) {
|
||||
std::string tones1 = "12";
|
||||
std::string tones2 = "ab";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
|
||||
VerifyExpectedState(tones1, duration, inter_tone_gap);
|
||||
// Wait until the first tone got sent.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
|
||||
fake_clock_);
|
||||
VerifyExpectedState("2", duration, inter_tone_gap);
|
||||
// Insert with another tone buffer.
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
|
||||
VerifyExpectedState(tones2, duration, inter_tone_gap);
|
||||
// Wait until it's completed.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
|
||||
|
||||
std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
|
||||
GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
|
||||
GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
|
||||
VerifyOnProvider(dtmf_queue_ref);
|
||||
VerifyOnObserver("1ab");
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
|
||||
std::string tones = "@1%a&*$";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
// Wait until the first tone got sent.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
|
||||
fake_clock_);
|
||||
// Delete provider.
|
||||
dtmf_->OnDtmfProviderDestroyed();
|
||||
provider_.reset();
|
||||
// The queue should be discontinued so no more tone callbacks.
|
||||
SIMULATED_WAIT(false, 200, fake_clock_);
|
||||
EXPECT_EQ(1U, observer_->tones().size());
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
|
||||
std::string tones = "@1%a&*$";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
// Wait until the first tone got sent.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
|
||||
fake_clock_);
|
||||
// Delete the sender.
|
||||
dtmf_ = NULL;
|
||||
// The queue should be discontinued so no more tone callbacks.
|
||||
SIMULATED_WAIT(false, 200, fake_clock_);
|
||||
EXPECT_EQ(1U, observer_->tones().size());
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
|
||||
std::string tones1 = "12";
|
||||
std::string tones2 = "";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
|
||||
// Wait until the first tone got sent.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
|
||||
fake_clock_);
|
||||
// Insert with another tone buffer.
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
|
||||
// Wait until it's completed.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
|
||||
|
||||
std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
|
||||
GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
|
||||
VerifyOnProvider(dtmf_queue_ref);
|
||||
VerifyOnObserver("1");
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfWithDefaultCommaDelay) {
|
||||
std::string tones = "3,4";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
|
||||
EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
|
||||
|
||||
VerifyOnProvider(tones, duration, inter_tone_gap);
|
||||
VerifyOnObserver(tones);
|
||||
EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfWithNonDefaultCommaDelay) {
|
||||
std::string tones = "3,4";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
|
||||
int comma_delay = 500;
|
||||
EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, comma_delay));
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
|
||||
|
||||
VerifyOnProvider(tones, duration, inter_tone_gap, comma_delay);
|
||||
VerifyOnObserver(tones);
|
||||
EXPECT_EQ(dtmf_->comma_delay(), comma_delay);
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
|
||||
std::string tones = "3,4";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
provider_->SetCanInsertDtmf(false);
|
||||
EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
|
||||
std::string tones = "3,4";
|
||||
int duration = 40;
|
||||
int inter_tone_gap = 50;
|
||||
|
||||
EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
|
||||
EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
|
||||
EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 29));
|
||||
EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, 29));
|
||||
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
}
|
||||
|
||||
TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) {
|
||||
std::string tones = "ABC";
|
||||
int duration = 100;
|
||||
int inter_tone_gap = 50;
|
||||
EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
|
||||
VerifyExpectedState("ABC", duration, inter_tone_gap);
|
||||
// Wait until the first tone got sent.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
|
||||
fake_clock_);
|
||||
VerifyExpectedState("BC", duration, inter_tone_gap);
|
||||
}
|
||||
143
TMessagesProj/jni/voip/webrtc/pc/external_hmac.cc
Normal file
143
TMessagesProj/jni/voip/webrtc/pc/external_hmac.cc
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2014 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 "pc/external_hmac.h"
|
||||
|
||||
#include <stdlib.h> // For malloc/free.
|
||||
#include <string.h>
|
||||
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/zero_memory.h"
|
||||
#include "third_party/libsrtp/include/srtp.h"
|
||||
|
||||
// Begin test case 0 */
|
||||
static const uint8_t kExternalHmacTestCase0Key[20] = {
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b};
|
||||
|
||||
static const uint8_t kExternalHmacTestCase0Data[8] = {
|
||||
0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 // "Hi There"
|
||||
};
|
||||
|
||||
static const uint8_t kExternalHmacFakeTag[10] = {0xba, 0xdd, 0xba, 0xdd, 0xba,
|
||||
0xdd, 0xba, 0xdd, 0xba, 0xdd};
|
||||
|
||||
static const srtp_auth_test_case_t kExternalHmacTestCase0 = {
|
||||
20, // Octets in key
|
||||
const_cast<uint8_t*>(kExternalHmacTestCase0Key), // Key
|
||||
8, // Octets in data
|
||||
const_cast<uint8_t*>(kExternalHmacTestCase0Data), // Data
|
||||
10, // Octets in tag
|
||||
const_cast<uint8_t*>(kExternalHmacFakeTag), // Tag
|
||||
NULL // Pointer to next
|
||||
// testcase
|
||||
};
|
||||
|
||||
static const char kExternalHmacDescription[] =
|
||||
"external hmac sha-1 authentication";
|
||||
|
||||
// srtp_auth_type_t external_hmac is the hmac metaobject
|
||||
|
||||
static const srtp_auth_type_t external_hmac = {
|
||||
external_hmac_alloc,
|
||||
external_hmac_dealloc,
|
||||
external_hmac_init,
|
||||
external_hmac_compute,
|
||||
external_hmac_update,
|
||||
external_hmac_start,
|
||||
const_cast<char*>(kExternalHmacDescription),
|
||||
const_cast<srtp_auth_test_case_t*>(&kExternalHmacTestCase0),
|
||||
EXTERNAL_HMAC_SHA1};
|
||||
|
||||
srtp_err_status_t external_hmac_alloc(srtp_auth_t** a,
|
||||
int key_len,
|
||||
int out_len) {
|
||||
uint8_t* pointer;
|
||||
|
||||
// Check key length - note that we don't support keys larger
|
||||
// than 20 bytes yet
|
||||
if (key_len > 20)
|
||||
return srtp_err_status_bad_param;
|
||||
|
||||
// Check output length - should be less than 20 bytes/
|
||||
if (out_len > 20)
|
||||
return srtp_err_status_bad_param;
|
||||
|
||||
// Allocate memory for auth and hmac_ctx_t structures.
|
||||
pointer = new uint8_t[(sizeof(ExternalHmacContext) + sizeof(srtp_auth_t))];
|
||||
if (pointer == NULL)
|
||||
return srtp_err_status_alloc_fail;
|
||||
|
||||
// Set pointers
|
||||
*a = reinterpret_cast<srtp_auth_t*>(pointer);
|
||||
// `external_hmac` is const and libsrtp expects `type` to be non-const.
|
||||
// const conversion is required. `external_hmac` is constant because we don't
|
||||
// want to increase global count in Chrome.
|
||||
(*a)->type = const_cast<srtp_auth_type_t*>(&external_hmac);
|
||||
(*a)->state = pointer + sizeof(srtp_auth_t);
|
||||
(*a)->out_len = out_len;
|
||||
(*a)->key_len = key_len;
|
||||
(*a)->prefix_len = 0;
|
||||
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_hmac_dealloc(srtp_auth_t* a) {
|
||||
rtc::ExplicitZeroMemory(a, sizeof(ExternalHmacContext) + sizeof(srtp_auth_t));
|
||||
|
||||
// Free memory
|
||||
delete[] a;
|
||||
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_hmac_init(void* state,
|
||||
const uint8_t* key,
|
||||
int key_len) {
|
||||
if (key_len > HMAC_KEY_LENGTH)
|
||||
return srtp_err_status_bad_param;
|
||||
|
||||
ExternalHmacContext* context = static_cast<ExternalHmacContext*>(state);
|
||||
memcpy(context->key, key, key_len);
|
||||
context->key_length = key_len;
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_hmac_start(void* /*state*/) {
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_hmac_update(void* /*state*/,
|
||||
const uint8_t* /*message*/,
|
||||
int /*msg_octets*/) {
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_hmac_compute(void* /*state*/,
|
||||
const uint8_t* /*message*/,
|
||||
int /*msg_octets*/,
|
||||
int tag_len,
|
||||
uint8_t* result) {
|
||||
memcpy(result, kExternalHmacFakeTag, tag_len);
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
|
||||
srtp_err_status_t external_crypto_init() {
|
||||
// `external_hmac` is const. const_cast is required as libsrtp expects
|
||||
// non-const.
|
||||
srtp_err_status_t status = srtp_replace_auth_type(
|
||||
const_cast<srtp_auth_type_t*>(&external_hmac), EXTERNAL_HMAC_SHA1);
|
||||
if (status) {
|
||||
RTC_LOG(LS_ERROR) << "Error in replacing default auth module, error: "
|
||||
<< status;
|
||||
return srtp_err_status_fail;
|
||||
}
|
||||
return srtp_err_status_ok;
|
||||
}
|
||||
72
TMessagesProj/jni/voip/webrtc/pc/external_hmac.h
Normal file
72
TMessagesProj/jni/voip/webrtc/pc/external_hmac.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_EXTERNAL_HMAC_H_
|
||||
#define PC_EXTERNAL_HMAC_H_
|
||||
|
||||
// External libsrtp HMAC auth module which implements methods defined in
|
||||
// auth_type_t.
|
||||
// The default auth module will be replaced only when the ENABLE_EXTERNAL_AUTH
|
||||
// flag is enabled. This allows us to access to authentication keys,
|
||||
// as the default auth implementation doesn't provide access and avoids
|
||||
// hashing each packet twice.
|
||||
|
||||
// How will libsrtp select this module?
|
||||
// Libsrtp defines authentication function types identified by an unsigned
|
||||
// integer, e.g. SRTP_HMAC_SHA1 is 3. Using authentication ids, the
|
||||
// application can plug any desired authentication modules into libsrtp.
|
||||
// libsrtp also provides a mechanism to select different auth functions for
|
||||
// individual streams. This can be done by setting the right value in
|
||||
// the auth_type of srtp_policy_t. The application must first register auth
|
||||
// functions and the corresponding authentication id using
|
||||
// crypto_kernel_replace_auth_type function.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "third_party/libsrtp/crypto/include/crypto_types.h"
|
||||
#include "third_party/libsrtp/include/srtp.h"
|
||||
#include "third_party/libsrtp/include/srtp_priv.h"
|
||||
|
||||
#define EXTERNAL_HMAC_SHA1 SRTP_HMAC_SHA1 + 1
|
||||
#define HMAC_KEY_LENGTH 20
|
||||
|
||||
// The HMAC context structure used to store authentication keys.
|
||||
// The pointer to the key will be allocated in the external_hmac_init function.
|
||||
// This pointer is owned by srtp_t in a template context.
|
||||
typedef struct {
|
||||
uint8_t key[HMAC_KEY_LENGTH];
|
||||
int key_length;
|
||||
} ExternalHmacContext;
|
||||
|
||||
srtp_err_status_t external_hmac_alloc(srtp_auth_t** a,
|
||||
int key_len,
|
||||
int out_len);
|
||||
|
||||
srtp_err_status_t external_hmac_dealloc(srtp_auth_t* a);
|
||||
|
||||
srtp_err_status_t external_hmac_init(void* state,
|
||||
const uint8_t* key,
|
||||
int key_len);
|
||||
|
||||
srtp_err_status_t external_hmac_start(void* state);
|
||||
|
||||
srtp_err_status_t external_hmac_update(void* state,
|
||||
const uint8_t* message,
|
||||
int msg_octets);
|
||||
|
||||
srtp_err_status_t external_hmac_compute(void* state,
|
||||
const uint8_t* message,
|
||||
int msg_octets,
|
||||
int tag_len,
|
||||
uint8_t* result);
|
||||
|
||||
srtp_err_status_t external_crypto_init();
|
||||
|
||||
#endif // PC_EXTERNAL_HMAC_H_
|
||||
360
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing.cc
Normal file
360
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing.cc
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* 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 "pc/ice_server_parsing.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cctype> // For std::isdigit.
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
#include "rtc_base/string_to_number.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
// Number of tokens must be preset when TURN uri has transport param.
|
||||
const size_t kTurnTransportTokensNum = 2;
|
||||
// The default stun port.
|
||||
const int kDefaultStunPort = 3478;
|
||||
const int kDefaultStunTlsPort = 5349;
|
||||
const char kTransport[] = "transport";
|
||||
|
||||
// Allowed characters in hostname per RFC 3986 Appendix A "reg-name"
|
||||
const char kRegNameCharacters[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
"-._~" // unreserved
|
||||
"%" // pct-encoded
|
||||
"!$&'()*+,;="; // sub-delims
|
||||
|
||||
// NOTE: Must be in the same order as the ServiceType enum.
|
||||
const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"};
|
||||
|
||||
// NOTE: A loop below assumes that the first value of this enum is 0 and all
|
||||
// other values are incremental.
|
||||
enum class ServiceType {
|
||||
STUN = 0, // Indicates a STUN server.
|
||||
STUNS, // Indicates a STUN server used with a TLS session.
|
||||
TURN, // Indicates a TURN server
|
||||
TURNS, // Indicates a TURN server used with a TLS session.
|
||||
INVALID, // Unknown.
|
||||
};
|
||||
static_assert(static_cast<size_t>(ServiceType::INVALID) ==
|
||||
arraysize(kValidIceServiceTypes),
|
||||
"kValidIceServiceTypes must have as many strings as ServiceType "
|
||||
"has values.");
|
||||
|
||||
// `in_str` should follow of RFC 7064/7065 syntax, but with an optional
|
||||
// "?transport=" already stripped. I.e.,
|
||||
// stunURI = scheme ":" host [ ":" port ]
|
||||
// scheme = "stun" / "stuns" / "turn" / "turns"
|
||||
// host = IP-literal / IPv4address / reg-name
|
||||
// port = *DIGIT
|
||||
|
||||
// Return tuple is service_type, host, with service_type == ServiceType::INVALID
|
||||
// on failure.
|
||||
std::tuple<ServiceType, absl::string_view> GetServiceTypeAndHostnameFromUri(
|
||||
absl::string_view in_str) {
|
||||
const auto colonpos = in_str.find(':');
|
||||
if (colonpos == absl::string_view::npos) {
|
||||
RTC_LOG(LS_WARNING) << "Missing ':' in ICE URI: " << in_str;
|
||||
return {ServiceType::INVALID, ""};
|
||||
}
|
||||
if ((colonpos + 1) == in_str.length()) {
|
||||
RTC_LOG(LS_WARNING) << "Empty hostname in ICE URI: " << in_str;
|
||||
return {ServiceType::INVALID, ""};
|
||||
}
|
||||
for (size_t i = 0; i < arraysize(kValidIceServiceTypes); ++i) {
|
||||
if (in_str.compare(0, colonpos, kValidIceServiceTypes[i]) == 0) {
|
||||
return {static_cast<ServiceType>(i), in_str.substr(colonpos + 1)};
|
||||
}
|
||||
}
|
||||
return {ServiceType::INVALID, ""};
|
||||
}
|
||||
|
||||
absl::optional<int> ParsePort(absl::string_view in_str) {
|
||||
// Make sure port only contains digits. StringToNumber doesn't check this.
|
||||
for (const char& c : in_str) {
|
||||
if (!std::isdigit(static_cast<unsigned char>(c))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return rtc::StringToNumber<int>(in_str);
|
||||
}
|
||||
|
||||
// This method parses IPv6 and IPv4 literal strings, along with hostnames in
|
||||
// standard hostname:port format.
|
||||
// Consider following formats as correct.
|
||||
// `hostname:port`, |[IPV6 address]:port|, |IPv4 address|:port,
|
||||
// `hostname`, |[IPv6 address]|, |IPv4 address|.
|
||||
|
||||
// Return tuple is success, host, port.
|
||||
std::tuple<bool, absl::string_view, int> ParseHostnameAndPortFromString(
|
||||
absl::string_view in_str,
|
||||
int default_port) {
|
||||
if (in_str.empty()) {
|
||||
return {false, "", 0};
|
||||
}
|
||||
absl::string_view host;
|
||||
int port = default_port;
|
||||
|
||||
if (in_str.at(0) == '[') {
|
||||
// IP_literal syntax
|
||||
auto closebracket = in_str.rfind(']');
|
||||
if (closebracket == absl::string_view::npos) {
|
||||
return {false, "", 0};
|
||||
}
|
||||
auto colonpos = in_str.find(':', closebracket);
|
||||
if (absl::string_view::npos != colonpos) {
|
||||
if (absl::optional<int> opt_port =
|
||||
ParsePort(in_str.substr(closebracket + 2))) {
|
||||
port = *opt_port;
|
||||
} else {
|
||||
return {false, "", 0};
|
||||
}
|
||||
}
|
||||
host = in_str.substr(1, closebracket - 1);
|
||||
} else {
|
||||
// IPv4address or reg-name syntax
|
||||
auto colonpos = in_str.find(':');
|
||||
if (absl::string_view::npos != colonpos) {
|
||||
if (absl::optional<int> opt_port =
|
||||
ParsePort(in_str.substr(colonpos + 1))) {
|
||||
port = *opt_port;
|
||||
} else {
|
||||
return {false, "", 0};
|
||||
}
|
||||
host = in_str.substr(0, colonpos);
|
||||
} else {
|
||||
host = in_str;
|
||||
}
|
||||
// RFC 3986 section 3.2.2 and Appendix A - "reg-name" syntax
|
||||
if (host.find_first_not_of(kRegNameCharacters) != absl::string_view::npos) {
|
||||
return {false, "", 0};
|
||||
}
|
||||
}
|
||||
return {!host.empty(), host, port};
|
||||
}
|
||||
|
||||
// Adds a STUN or TURN server to the appropriate list,
|
||||
// by parsing `url` and using the username/password in `server`.
|
||||
RTCError ParseIceServerUrl(
|
||||
const PeerConnectionInterface::IceServer& server,
|
||||
absl::string_view url,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
// RFC 7064
|
||||
// stunURI = scheme ":" host [ ":" port ]
|
||||
// scheme = "stun" / "stuns"
|
||||
|
||||
// RFC 7065
|
||||
// turnURI = scheme ":" host [ ":" port ]
|
||||
// [ "?transport=" transport ]
|
||||
// scheme = "turn" / "turns"
|
||||
// transport = "udp" / "tcp" / transport-ext
|
||||
// transport-ext = 1*unreserved
|
||||
|
||||
// RFC 3986
|
||||
// host = IP-literal / IPv4address / reg-name
|
||||
// port = *DIGIT
|
||||
|
||||
RTC_DCHECK(stun_servers != nullptr);
|
||||
RTC_DCHECK(turn_servers != nullptr);
|
||||
cricket::ProtocolType turn_transport_type = cricket::PROTO_UDP;
|
||||
RTC_DCHECK(!url.empty());
|
||||
std::vector<absl::string_view> tokens = rtc::split(url, '?');
|
||||
absl::string_view uri_without_transport = tokens[0];
|
||||
// Let's look into transport= param, if it exists.
|
||||
if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present.
|
||||
std::vector<absl::string_view> transport_tokens =
|
||||
rtc::split(tokens[1], '=');
|
||||
if (transport_tokens[0] != kTransport) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid transport parameter key.");
|
||||
}
|
||||
if (transport_tokens.size() < 2) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Transport parameter missing value.");
|
||||
}
|
||||
|
||||
absl::optional<cricket::ProtocolType> proto =
|
||||
cricket::StringToProto(transport_tokens[1]);
|
||||
if (!proto ||
|
||||
(*proto != cricket::PROTO_UDP && *proto != cricket::PROTO_TCP)) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Transport parameter should "
|
||||
"always be udp or tcp.");
|
||||
}
|
||||
turn_transport_type = *proto;
|
||||
}
|
||||
|
||||
auto [service_type, hoststring] =
|
||||
GetServiceTypeAndHostnameFromUri(uri_without_transport);
|
||||
if (service_type == ServiceType::INVALID) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid transport parameter in ICE URI: " << url;
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid transport parameter in ICE URI");
|
||||
}
|
||||
|
||||
// GetServiceTypeAndHostnameFromUri should never give an empty hoststring
|
||||
RTC_DCHECK(!hoststring.empty());
|
||||
|
||||
// stun with ?transport (or any ?) is not valid.
|
||||
if ((service_type == ServiceType::STUN ||
|
||||
service_type == ServiceType::STUNS) &&
|
||||
tokens.size() > 1) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid stun url with query parameters");
|
||||
}
|
||||
|
||||
int default_port = kDefaultStunPort;
|
||||
if (service_type == ServiceType::TURNS) {
|
||||
default_port = kDefaultStunTlsPort;
|
||||
turn_transport_type = cricket::PROTO_TLS;
|
||||
}
|
||||
|
||||
if (hoststring.find('@') != absl::string_view::npos) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid url with long deprecated user@host syntax: "
|
||||
<< uri_without_transport;
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid url with long "
|
||||
"deprecated user@host syntax");
|
||||
}
|
||||
|
||||
auto [success, address, port] =
|
||||
ParseHostnameAndPortFromString(hoststring, default_port);
|
||||
if (!success) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid hostname format: " << uri_without_transport;
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid hostname format");
|
||||
}
|
||||
|
||||
if (port <= 0 || port > 0xffff) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid port: " << port;
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Invalid port");
|
||||
}
|
||||
|
||||
switch (service_type) {
|
||||
case ServiceType::STUN:
|
||||
case ServiceType::STUNS:
|
||||
stun_servers->insert(rtc::SocketAddress(address, port));
|
||||
break;
|
||||
case ServiceType::TURN:
|
||||
case ServiceType::TURNS: {
|
||||
if (server.username.empty() || server.password.empty()) {
|
||||
// The WebRTC spec requires throwing an InvalidAccessError when username
|
||||
// or credential are ommitted; this is the native equivalent.
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::INVALID_PARAMETER,
|
||||
"ICE server parsing failed: TURN server with empty "
|
||||
"username or password");
|
||||
}
|
||||
// If the hostname field is not empty, then the server address must be
|
||||
// the resolved IP for that host, the hostname is needed later for TLS
|
||||
// handshake (SNI and Certificate verification).
|
||||
absl::string_view hostname =
|
||||
server.hostname.empty() ? address : server.hostname;
|
||||
rtc::SocketAddress socket_address(hostname, port);
|
||||
if (!server.hostname.empty()) {
|
||||
rtc::IPAddress ip;
|
||||
if (!IPFromString(address, &ip)) {
|
||||
// When hostname is set, the server address must be a
|
||||
// resolved ip address.
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::INVALID_PARAMETER,
|
||||
"ICE server parsing failed: "
|
||||
"IceServer has hostname field set, but URI does not "
|
||||
"contain an IP address.");
|
||||
}
|
||||
socket_address.SetResolvedIP(ip);
|
||||
}
|
||||
cricket::RelayServerConfig config =
|
||||
cricket::RelayServerConfig(socket_address, server.username,
|
||||
server.password, turn_transport_type);
|
||||
if (server.tls_cert_policy ==
|
||||
PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck) {
|
||||
config.tls_cert_policy =
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK;
|
||||
}
|
||||
config.tls_alpn_protocols = server.tls_alpn_protocols;
|
||||
config.tls_elliptic_curves = server.tls_elliptic_curves;
|
||||
|
||||
turn_servers->push_back(config);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// We shouldn't get to this point with an invalid service_type, we should
|
||||
// have returned an error already.
|
||||
LOG_AND_RETURN_ERROR(
|
||||
RTCErrorType::INTERNAL_ERROR,
|
||||
"ICE server parsing failed: Unexpected service type");
|
||||
}
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RTCError ParseIceServersOrError(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
for (const PeerConnectionInterface::IceServer& server : servers) {
|
||||
if (!server.urls.empty()) {
|
||||
for (const std::string& url : server.urls) {
|
||||
if (url.empty()) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Empty uri.");
|
||||
}
|
||||
RTCError err =
|
||||
ParseIceServerUrl(server, url, stun_servers, turn_servers);
|
||||
if (!err.ok()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (!server.uri.empty()) {
|
||||
// Fallback to old .uri if new .urls isn't present.
|
||||
RTCError err =
|
||||
ParseIceServerUrl(server, server.uri, stun_servers, turn_servers);
|
||||
|
||||
if (!err.ok()) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE server parsing failed: Empty uri.");
|
||||
}
|
||||
}
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
RTCErrorType ParseIceServers(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
return ParseIceServersOrError(servers, stun_servers, turn_servers).type();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
42
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing.h
Normal file
42
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_ICE_SERVER_PARSING_H_
|
||||
#define PC_ICE_SERVER_PARSING_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Parses the URLs for each server in `servers` to build `stun_servers` and
|
||||
// `turn_servers`. Can return SYNTAX_ERROR if the URL is malformed, or
|
||||
// INVALID_PARAMETER if a TURN server is missing `username` or `password`.
|
||||
//
|
||||
// Intended to be used to convert/validate the servers passed into a
|
||||
// PeerConnection through RTCConfiguration.
|
||||
RTC_EXPORT RTCError
|
||||
ParseIceServersOrError(const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers);
|
||||
|
||||
[[deprecated("use ParseIceServersOrError")]] RTC_EXPORT RTCErrorType
|
||||
ParseIceServers(const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_ICE_SERVER_PARSING_H_
|
||||
239
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing_unittest.cc
Normal file
239
TMessagesProj/jni/voip/webrtc/pc/ice_server_parsing_unittest.cc
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright 2012 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 "pc/ice_server_parsing.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class IceServerParsingTest : public ::testing::Test {
|
||||
public:
|
||||
// Convenience functions for parsing a single URL. Result is stored in
|
||||
// `stun_servers_` and `turn_servers_`.
|
||||
bool ParseUrl(const std::string& url) {
|
||||
return ParseUrl(url, std::string(), std::string());
|
||||
}
|
||||
|
||||
bool ParseTurnUrl(const std::string& url) {
|
||||
return ParseUrl(url, "username", "password");
|
||||
}
|
||||
|
||||
bool ParseUrl(const std::string& url,
|
||||
const std::string& username,
|
||||
const std::string& password) {
|
||||
return ParseUrl(
|
||||
url, username, password,
|
||||
PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicySecure);
|
||||
}
|
||||
|
||||
bool ParseUrl(const std::string& url,
|
||||
const std::string& username,
|
||||
const std::string& password,
|
||||
PeerConnectionInterface::TlsCertPolicy tls_certificate_policy) {
|
||||
return ParseUrl(url, username, password, tls_certificate_policy, "");
|
||||
}
|
||||
|
||||
bool ParseUrl(const std::string& url,
|
||||
const std::string& username,
|
||||
const std::string& password,
|
||||
PeerConnectionInterface::TlsCertPolicy tls_certificate_policy,
|
||||
const std::string& hostname) {
|
||||
stun_servers_.clear();
|
||||
turn_servers_.clear();
|
||||
PeerConnectionInterface::IceServers servers;
|
||||
PeerConnectionInterface::IceServer server;
|
||||
server.urls.push_back(url);
|
||||
server.username = username;
|
||||
server.password = password;
|
||||
server.tls_cert_policy = tls_certificate_policy;
|
||||
server.hostname = hostname;
|
||||
servers.push_back(server);
|
||||
return ParseIceServersOrError(servers, &stun_servers_, &turn_servers_).ok();
|
||||
}
|
||||
|
||||
protected:
|
||||
cricket::ServerAddresses stun_servers_;
|
||||
std::vector<cricket::RelayServerConfig> turn_servers_;
|
||||
};
|
||||
|
||||
// Make sure all STUN/TURN prefixes are parsed correctly.
|
||||
TEST_F(IceServerParsingTest, ParseStunPrefixes) {
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(0U, turn_servers_.size());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stuns:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(0U, turn_servers_.size());
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname"));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turns:hostname"));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
EXPECT_TRUE(turn_servers_[0].tls_cert_policy ==
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_SECURE);
|
||||
|
||||
EXPECT_TRUE(ParseUrl(
|
||||
"turns:hostname", "username", "password",
|
||||
PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicyInsecureNoCheck));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_TRUE(turn_servers_[0].tls_cert_policy ==
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK);
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// invalid prefixes
|
||||
EXPECT_FALSE(ParseUrl("stunn:hostname"));
|
||||
EXPECT_FALSE(ParseUrl(":hostname"));
|
||||
EXPECT_FALSE(ParseUrl(":"));
|
||||
EXPECT_FALSE(ParseUrl(""));
|
||||
}
|
||||
|
||||
TEST_F(IceServerParsingTest, VerifyDefaults) {
|
||||
// TURNS defaults
|
||||
EXPECT_TRUE(ParseTurnUrl("turns:hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(5349, turn_servers_[0].ports[0].address.port());
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// TURN defaults
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(3478, turn_servers_[0].ports[0].address.port());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// STUN defaults
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
}
|
||||
|
||||
// Check that the 6 combinations of IPv4/IPv6/hostname and with/without port
|
||||
// can be parsed correctly.
|
||||
TEST_F(IceServerParsingTest, ParseHostnameAndPort) {
|
||||
EXPECT_TRUE(ParseUrl("stun:1.2.3.4:1234"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1.2.3.4", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(1234, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:[1:2:3:4:5:6:7:8]:4321"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1:2:3:4:5:6:7:8", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(4321, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname:9999"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("hostname", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(9999, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:1.2.3.4"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1.2.3.4", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:[1:2:3:4:5:6:7:8]"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1:2:3:4:5:6:7:8", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("hostname", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
// Both TURN IP and host exist
|
||||
EXPECT_TRUE(
|
||||
ParseUrl("turn:1.2.3.4:1234", "username", "password",
|
||||
PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicySecure,
|
||||
"hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
rtc::SocketAddress address = turn_servers_[0].ports[0].address;
|
||||
EXPECT_EQ("hostname", address.hostname());
|
||||
EXPECT_EQ(1234, address.port());
|
||||
EXPECT_FALSE(address.IsUnresolvedIP());
|
||||
EXPECT_EQ("1.2.3.4", address.ipaddr().ToString());
|
||||
|
||||
// Try some invalid hostname:port strings.
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:99a99"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:-1"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:port:more"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:port more"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:"));
|
||||
EXPECT_FALSE(ParseUrl("stun:[1:2:3:4:5:6:7:8]junk:1000"));
|
||||
EXPECT_FALSE(ParseUrl("stun::5555"));
|
||||
EXPECT_FALSE(ParseUrl("stun:"));
|
||||
// Test illegal URLs according to RFC 3986 (URI generic syntax)
|
||||
// and RFC 7064 (URI schemes for STUN and TURN)
|
||||
EXPECT_FALSE(ParseUrl("stun:/hostname")); // / is not allowed
|
||||
EXPECT_FALSE(ParseUrl("stun:?hostname")); // ? is not allowed
|
||||
EXPECT_FALSE(ParseUrl("stun:#hostname")); // # is not allowed
|
||||
// STUN explicitly forbids query parameters.
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname?transport=udp"));
|
||||
}
|
||||
|
||||
// Test parsing the "?transport=xxx" part of the URL.
|
||||
TEST_F(IceServerParsingTest, ParseTransport) {
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname:1234?transport=tcp"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_TCP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname?transport=udp"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?transport=invalid"));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?transport="));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?="));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?"));
|
||||
EXPECT_FALSE(ParseTurnUrl("?"));
|
||||
}
|
||||
|
||||
// Reject pre-RFC 7065 syntax with ICE username contained in URL.
|
||||
TEST_F(IceServerParsingTest, ParseRejectsUsername) {
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:user@hostname"));
|
||||
}
|
||||
|
||||
// Test that username and password from IceServer is copied into the resulting
|
||||
// RelayServerConfig.
|
||||
TEST_F(IceServerParsingTest, CopyUsernameAndPasswordFromIceServer) {
|
||||
EXPECT_TRUE(ParseUrl("turn:hostname", "username", "password"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ("username", turn_servers_[0].credentials.username);
|
||||
EXPECT_EQ("password", turn_servers_[0].credentials.password);
|
||||
}
|
||||
|
||||
// Ensure that if a server has multiple URLs, each one is parsed.
|
||||
TEST_F(IceServerParsingTest, ParseMultipleUrls) {
|
||||
PeerConnectionInterface::IceServers servers;
|
||||
PeerConnectionInterface::IceServer server;
|
||||
server.urls.push_back("stun:hostname");
|
||||
server.urls.push_back("turn:hostname");
|
||||
server.username = "foo";
|
||||
server.password = "bar";
|
||||
servers.push_back(server);
|
||||
EXPECT_TRUE(
|
||||
ParseIceServersOrError(servers, &stun_servers_, &turn_servers_).ok());
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
36
TMessagesProj/jni/voip/webrtc/pc/ice_transport.cc
Normal file
36
TMessagesProj/jni/voip/webrtc/pc/ice_transport.cc
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/ice_transport.h"
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
IceTransportWithPointer::~IceTransportWithPointer() {
|
||||
// We depend on the networking thread to call Clear() before dropping
|
||||
// its last reference to this object; if the destructor is called
|
||||
// on the networking thread, it's OK to not have called Clear().
|
||||
if (internal_) {
|
||||
RTC_DCHECK_RUN_ON(creator_thread_);
|
||||
}
|
||||
}
|
||||
|
||||
cricket::IceTransportInternal* IceTransportWithPointer::internal() {
|
||||
RTC_DCHECK_RUN_ON(creator_thread_);
|
||||
return internal_;
|
||||
}
|
||||
|
||||
void IceTransportWithPointer::Clear() {
|
||||
RTC_DCHECK_RUN_ON(creator_thread_);
|
||||
internal_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
52
TMessagesProj/jni/voip/webrtc/pc/ice_transport.h
Normal file
52
TMessagesProj/jni/voip/webrtc/pc/ice_transport.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_ICE_TRANSPORT_H_
|
||||
#define PC_ICE_TRANSPORT_H_
|
||||
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Implementation of IceTransportInterface that does not take ownership
|
||||
// of its underlying IceTransport. It depends on its creator class to
|
||||
// ensure that Clear() is called before the underlying IceTransport
|
||||
// is deallocated.
|
||||
class IceTransportWithPointer : public IceTransportInterface {
|
||||
public:
|
||||
explicit IceTransportWithPointer(cricket::IceTransportInternal* internal)
|
||||
: creator_thread_(rtc::Thread::Current()), internal_(internal) {
|
||||
RTC_DCHECK(internal_);
|
||||
}
|
||||
|
||||
IceTransportWithPointer() = delete;
|
||||
IceTransportWithPointer(const IceTransportWithPointer&) = delete;
|
||||
IceTransportWithPointer& operator=(const IceTransportWithPointer&) = delete;
|
||||
|
||||
cricket::IceTransportInternal* internal() override;
|
||||
// This call will ensure that the pointer passed at construction is
|
||||
// no longer in use by this object. Later calls to internal() will return
|
||||
// null.
|
||||
void Clear();
|
||||
|
||||
protected:
|
||||
~IceTransportWithPointer() override;
|
||||
|
||||
private:
|
||||
const rtc::Thread* creator_thread_;
|
||||
cricket::IceTransportInternal* internal_ RTC_GUARDED_BY(creator_thread_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_ICE_TRANSPORT_H_
|
||||
64
TMessagesProj/jni/voip/webrtc/pc/ice_transport_unittest.cc
Normal file
64
TMessagesProj/jni/voip/webrtc/pc/ice_transport_unittest.cc
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/ice_transport.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/ice_transport_factory.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "p2p/base/fake_ice_transport.h"
|
||||
#include "p2p/base/fake_port_allocator.h"
|
||||
#include "rtc_base/internal/default_socket_server.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class IceTransportTest : public ::testing::Test {
|
||||
protected:
|
||||
IceTransportTest()
|
||||
: socket_server_(rtc::CreateDefaultSocketServer()),
|
||||
main_thread_(socket_server_.get()) {}
|
||||
|
||||
rtc::SocketServer* socket_server() const { return socket_server_.get(); }
|
||||
|
||||
test::ScopedKeyValueConfig field_trials_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<rtc::SocketServer> socket_server_;
|
||||
rtc::AutoSocketServerThread main_thread_;
|
||||
};
|
||||
|
||||
TEST_F(IceTransportTest, CreateNonSelfDeletingTransport) {
|
||||
auto cricket_transport =
|
||||
std::make_unique<cricket::FakeIceTransport>("name", 0, nullptr);
|
||||
auto ice_transport =
|
||||
rtc::make_ref_counted<IceTransportWithPointer>(cricket_transport.get());
|
||||
EXPECT_EQ(ice_transport->internal(), cricket_transport.get());
|
||||
ice_transport->Clear();
|
||||
EXPECT_NE(ice_transport->internal(), cricket_transport.get());
|
||||
}
|
||||
|
||||
TEST_F(IceTransportTest, CreateSelfDeletingTransport) {
|
||||
std::unique_ptr<cricket::FakePortAllocator> port_allocator(
|
||||
std::make_unique<cricket::FakePortAllocator>(
|
||||
nullptr,
|
||||
std::make_unique<rtc::BasicPacketSocketFactory>(socket_server()),
|
||||
&field_trials_));
|
||||
IceTransportInit init;
|
||||
init.set_port_allocator(port_allocator.get());
|
||||
auto ice_transport = CreateIceTransport(std::move(init));
|
||||
EXPECT_NE(nullptr, ice_transport->internal());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
38
TMessagesProj/jni/voip/webrtc/pc/jitter_buffer_delay.cc
Normal file
38
TMessagesProj/jni/voip/webrtc/pc/jitter_buffer_delay.cc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/jitter_buffer_delay.h"
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "rtc_base/numerics/safe_minmax.h"
|
||||
|
||||
namespace {
|
||||
constexpr int kDefaultDelay = 0;
|
||||
constexpr int kMaximumDelayMs = 10000;
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
void JitterBufferDelay::Set(absl::optional<double> delay_seconds) {
|
||||
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
|
||||
cached_delay_seconds_ = delay_seconds;
|
||||
}
|
||||
|
||||
int JitterBufferDelay::GetMs() const {
|
||||
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
|
||||
return rtc::SafeClamp(
|
||||
rtc::saturated_cast<int>(cached_delay_seconds_.value_or(kDefaultDelay) *
|
||||
1000),
|
||||
0, kMaximumDelayMs);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
42
TMessagesProj/jni/voip/webrtc/pc/jitter_buffer_delay.h
Normal file
42
TMessagesProj/jni/voip/webrtc/pc/jitter_buffer_delay.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JITTER_BUFFER_DELAY_H_
|
||||
#define PC_JITTER_BUFFER_DELAY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// JitterBufferDelay converts delay from seconds to milliseconds for the
|
||||
// underlying media channel. It also handles cases when user sets delay before
|
||||
// the start of media_channel by caching its request.
|
||||
class JitterBufferDelay {
|
||||
public:
|
||||
JitterBufferDelay() = default;
|
||||
|
||||
void Set(absl::optional<double> delay_seconds);
|
||||
int GetMs() const;
|
||||
|
||||
private:
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_thread_checker_{
|
||||
SequenceChecker::kDetached};
|
||||
absl::optional<double> cached_delay_seconds_
|
||||
RTC_GUARDED_BY(&worker_thread_checker_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_JITTER_BUFFER_DELAY_H_
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/jitter_buffer_delay.h"
|
||||
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class JitterBufferDelayTest : public ::testing::Test {
|
||||
public:
|
||||
JitterBufferDelayTest() {}
|
||||
|
||||
protected:
|
||||
JitterBufferDelay delay_;
|
||||
};
|
||||
|
||||
TEST_F(JitterBufferDelayTest, Set) {
|
||||
// Delay in seconds.
|
||||
delay_.Set(3.0);
|
||||
EXPECT_EQ(delay_.GetMs(), 3000);
|
||||
}
|
||||
|
||||
TEST_F(JitterBufferDelayTest, DefaultValue) {
|
||||
EXPECT_EQ(delay_.GetMs(), 0); // Default value is 0ms.
|
||||
}
|
||||
|
||||
TEST_F(JitterBufferDelayTest, Clamping) {
|
||||
// In current Jitter Buffer implementation (Audio or Video) maximum supported
|
||||
// value is 10000 milliseconds.
|
||||
delay_.Set(10.5);
|
||||
EXPECT_EQ(delay_.GetMs(), 10000);
|
||||
|
||||
// Test int overflow.
|
||||
delay_.Set(21474836470.0);
|
||||
EXPECT_EQ(delay_.GetMs(), 10000);
|
||||
|
||||
delay_.Set(-21474836470.0);
|
||||
EXPECT_EQ(delay_.GetMs(), 0);
|
||||
|
||||
// Boundary value in seconds to milliseconds conversion.
|
||||
delay_.Set(0.0009);
|
||||
EXPECT_EQ(delay_.GetMs(), 0);
|
||||
|
||||
delay_.Set(-2.0);
|
||||
EXPECT_EQ(delay_.GetMs(), 0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
76
TMessagesProj/jni/voip/webrtc/pc/jsep_ice_candidate.cc
Normal file
76
TMessagesProj/jni/voip/webrtc/pc/jsep_ice_candidate.cc
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2012 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 "api/jsep_ice_candidate.h"
|
||||
|
||||
#include "pc/webrtc_sdp.h"
|
||||
|
||||
// This file contains JsepIceCandidate-related functions that are not
|
||||
// included in api/jsep_ice_candidate.cc. Some of these link to SDP
|
||||
// parsing/serializing functions, which some users may not want.
|
||||
// TODO(bugs.webrtc.org/12330): Merge the two .cc files somehow.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
IceCandidateInterface* CreateIceCandidate(const std::string& sdp_mid,
|
||||
int sdp_mline_index,
|
||||
const std::string& sdp,
|
||||
SdpParseError* error) {
|
||||
JsepIceCandidate* jsep_ice = new JsepIceCandidate(sdp_mid, sdp_mline_index);
|
||||
if (!jsep_ice->Initialize(sdp, error)) {
|
||||
delete jsep_ice;
|
||||
return NULL;
|
||||
}
|
||||
return jsep_ice;
|
||||
}
|
||||
|
||||
std::unique_ptr<IceCandidateInterface> CreateIceCandidate(
|
||||
const std::string& sdp_mid,
|
||||
int sdp_mline_index,
|
||||
const cricket::Candidate& candidate) {
|
||||
return std::make_unique<JsepIceCandidate>(sdp_mid, sdp_mline_index,
|
||||
candidate);
|
||||
}
|
||||
|
||||
JsepIceCandidate::JsepIceCandidate(const std::string& sdp_mid,
|
||||
int sdp_mline_index)
|
||||
: sdp_mid_(sdp_mid), sdp_mline_index_(sdp_mline_index) {}
|
||||
|
||||
JsepIceCandidate::JsepIceCandidate(const std::string& sdp_mid,
|
||||
int sdp_mline_index,
|
||||
const cricket::Candidate& candidate)
|
||||
: sdp_mid_(sdp_mid),
|
||||
sdp_mline_index_(sdp_mline_index),
|
||||
candidate_(candidate) {}
|
||||
|
||||
JsepIceCandidate::~JsepIceCandidate() {}
|
||||
|
||||
JsepCandidateCollection JsepCandidateCollection::Clone() const {
|
||||
JsepCandidateCollection new_collection;
|
||||
for (const auto& candidate : candidates_) {
|
||||
new_collection.candidates_.push_back(std::make_unique<JsepIceCandidate>(
|
||||
candidate->sdp_mid(), candidate->sdp_mline_index(),
|
||||
candidate->candidate()));
|
||||
}
|
||||
return new_collection;
|
||||
}
|
||||
|
||||
bool JsepIceCandidate::Initialize(const std::string& sdp, SdpParseError* err) {
|
||||
return SdpDeserializeCandidate(sdp, this, err);
|
||||
}
|
||||
|
||||
bool JsepIceCandidate::ToString(std::string* out) const {
|
||||
if (!out)
|
||||
return false;
|
||||
*out = SdpSerializeCandidate(*this);
|
||||
return !out->empty();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
343
TMessagesProj/jni/voip/webrtc/pc/jsep_session_description.cc
Normal file
343
TMessagesProj/jni/voip/webrtc/pc/jsep_session_description.cc
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright 2012 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 "api/jsep_session_description.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/media_session.h" // IWYU pragma: keep
|
||||
#include "pc/webrtc_sdp.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/net_helper.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
|
||||
using cricket::Candidate;
|
||||
using cricket::SessionDescription;
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
constexpr char kDummyAddress[] = "0.0.0.0";
|
||||
constexpr int kDummyPort = 9;
|
||||
|
||||
// Update the connection address for the MediaContentDescription based on the
|
||||
// candidates.
|
||||
void UpdateConnectionAddress(
|
||||
const JsepCandidateCollection& candidate_collection,
|
||||
cricket::MediaContentDescription* media_desc) {
|
||||
int port = kDummyPort;
|
||||
std::string ip = kDummyAddress;
|
||||
std::string hostname;
|
||||
int current_preference = 0; // Start with lowest preference.
|
||||
int current_family = AF_UNSPEC;
|
||||
for (size_t i = 0; i < candidate_collection.count(); ++i) {
|
||||
const IceCandidateInterface* jsep_candidate = candidate_collection.at(i);
|
||||
if (jsep_candidate->candidate().component() !=
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP) {
|
||||
continue;
|
||||
}
|
||||
// Default destination should be UDP only.
|
||||
if (jsep_candidate->candidate().protocol() != cricket::UDP_PROTOCOL_NAME) {
|
||||
continue;
|
||||
}
|
||||
const int preference = jsep_candidate->candidate().type_preference();
|
||||
const int family = jsep_candidate->candidate().address().ipaddr().family();
|
||||
// See if this candidate is more preferable then the current one if it's the
|
||||
// same family. Or if the current family is IPv4 already so we could safely
|
||||
// ignore all IPv6 ones. WebRTC bug 4269.
|
||||
// http://code.google.com/p/webrtc/issues/detail?id=4269
|
||||
if ((preference <= current_preference && current_family == family) ||
|
||||
(current_family == AF_INET && family == AF_INET6)) {
|
||||
continue;
|
||||
}
|
||||
current_preference = preference;
|
||||
current_family = family;
|
||||
const rtc::SocketAddress& candidate_addr =
|
||||
jsep_candidate->candidate().address();
|
||||
port = candidate_addr.port();
|
||||
ip = candidate_addr.ipaddr().ToString();
|
||||
hostname = candidate_addr.hostname();
|
||||
}
|
||||
rtc::SocketAddress connection_addr(ip, port);
|
||||
if (rtc::IPIsUnspec(connection_addr.ipaddr()) && !hostname.empty()) {
|
||||
// When a hostname candidate becomes the (default) connection address,
|
||||
// we use the dummy address 0.0.0.0 and port 9 in the c= and the m= lines.
|
||||
//
|
||||
// We have observed in deployment that with a FQDN in a c= line, SDP parsing
|
||||
// could fail in other JSEP implementations. We note that the wildcard
|
||||
// addresses (0.0.0.0 or ::) with port 9 are given the exception as the
|
||||
// connection address that will not result in an ICE mismatch
|
||||
// (draft-ietf-mmusic-ice-sip-sdp). Also, 0.0.0.0 or :: can be used as the
|
||||
// connection address in the initial offer or answer with trickle ICE
|
||||
// if the offerer or answerer does not want to include the host IP address
|
||||
// (draft-ietf-mmusic-trickle-ice-sip), and in particular 0.0.0.0 has been
|
||||
// widely deployed for this use without outstanding compatibility issues.
|
||||
// Combining the above considerations, we use 0.0.0.0 with port 9 to
|
||||
// populate the c= and the m= lines. See `BuildMediaDescription` in
|
||||
// webrtc_sdp.cc for the SDP generation with
|
||||
// `media_desc->connection_address()`.
|
||||
connection_addr = rtc::SocketAddress(kDummyAddress, kDummyPort);
|
||||
}
|
||||
media_desc->set_connection_address(connection_addr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(steveanton): Remove this default implementation once Chromium has been
|
||||
// updated.
|
||||
SdpType SessionDescriptionInterface::GetType() const {
|
||||
absl::optional<SdpType> maybe_type = SdpTypeFromString(type());
|
||||
if (maybe_type) {
|
||||
return *maybe_type;
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << "Default implementation of "
|
||||
"SessionDescriptionInterface::GetType does not "
|
||||
"recognize the result from type(), returning "
|
||||
"kOffer.";
|
||||
return SdpType::kOffer;
|
||||
}
|
||||
}
|
||||
|
||||
SessionDescriptionInterface* CreateSessionDescription(const std::string& type,
|
||||
const std::string& sdp,
|
||||
SdpParseError* error) {
|
||||
absl::optional<SdpType> maybe_type = SdpTypeFromString(type);
|
||||
if (!maybe_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateSessionDescription(*maybe_type, sdp, error).release();
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
|
||||
SdpType type,
|
||||
const std::string& sdp) {
|
||||
return CreateSessionDescription(type, sdp, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
|
||||
SdpType type,
|
||||
const std::string& sdp,
|
||||
SdpParseError* error_out) {
|
||||
auto jsep_desc = std::make_unique<JsepSessionDescription>(type);
|
||||
if (type != SdpType::kRollback) {
|
||||
if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return std::move(jsep_desc);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
|
||||
SdpType type,
|
||||
const std::string& session_id,
|
||||
const std::string& session_version,
|
||||
std::unique_ptr<cricket::SessionDescription> description) {
|
||||
auto jsep_description = std::make_unique<JsepSessionDescription>(type);
|
||||
bool initialize_success = jsep_description->Initialize(
|
||||
std::move(description), session_id, session_version);
|
||||
RTC_DCHECK(initialize_success);
|
||||
return std::move(jsep_description);
|
||||
}
|
||||
|
||||
JsepSessionDescription::JsepSessionDescription(SdpType type) : type_(type) {}
|
||||
|
||||
JsepSessionDescription::JsepSessionDescription(const std::string& type) {
|
||||
absl::optional<SdpType> maybe_type = SdpTypeFromString(type);
|
||||
if (maybe_type) {
|
||||
type_ = *maybe_type;
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "JsepSessionDescription constructed with invalid type string: "
|
||||
<< type << ". Assuming it is an offer.";
|
||||
type_ = SdpType::kOffer;
|
||||
}
|
||||
}
|
||||
|
||||
JsepSessionDescription::JsepSessionDescription(
|
||||
SdpType type,
|
||||
std::unique_ptr<cricket::SessionDescription> description,
|
||||
absl::string_view session_id,
|
||||
absl::string_view session_version)
|
||||
: description_(std::move(description)),
|
||||
session_id_(session_id),
|
||||
session_version_(session_version),
|
||||
type_(type) {
|
||||
RTC_DCHECK(description_);
|
||||
candidate_collection_.resize(number_of_mediasections());
|
||||
}
|
||||
|
||||
JsepSessionDescription::~JsepSessionDescription() {}
|
||||
|
||||
bool JsepSessionDescription::Initialize(
|
||||
std::unique_ptr<cricket::SessionDescription> description,
|
||||
const std::string& session_id,
|
||||
const std::string& session_version) {
|
||||
if (!description)
|
||||
return false;
|
||||
|
||||
session_id_ = session_id;
|
||||
session_version_ = session_version;
|
||||
description_ = std::move(description);
|
||||
candidate_collection_.resize(number_of_mediasections());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> JsepSessionDescription::Clone()
|
||||
const {
|
||||
auto new_description = std::make_unique<JsepSessionDescription>(type_);
|
||||
new_description->session_id_ = session_id_;
|
||||
new_description->session_version_ = session_version_;
|
||||
if (description_) {
|
||||
new_description->description_ = description_->Clone();
|
||||
}
|
||||
for (const auto& collection : candidate_collection_) {
|
||||
new_description->candidate_collection_.push_back(collection.Clone());
|
||||
}
|
||||
return new_description;
|
||||
}
|
||||
|
||||
bool JsepSessionDescription::AddCandidate(
|
||||
const IceCandidateInterface* candidate) {
|
||||
if (!candidate)
|
||||
return false;
|
||||
size_t mediasection_index = 0;
|
||||
if (!GetMediasectionIndex(candidate, &mediasection_index)) {
|
||||
return false;
|
||||
}
|
||||
if (mediasection_index >= number_of_mediasections())
|
||||
return false;
|
||||
const std::string& content_name =
|
||||
description_->contents()[mediasection_index].name;
|
||||
const cricket::TransportInfo* transport_info =
|
||||
description_->GetTransportInfoByName(content_name);
|
||||
if (!transport_info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Candidate updated_candidate = candidate->candidate();
|
||||
if (updated_candidate.username().empty()) {
|
||||
updated_candidate.set_username(transport_info->description.ice_ufrag);
|
||||
}
|
||||
if (updated_candidate.password().empty()) {
|
||||
updated_candidate.set_password(transport_info->description.ice_pwd);
|
||||
}
|
||||
|
||||
std::unique_ptr<JsepIceCandidate> updated_candidate_wrapper(
|
||||
new JsepIceCandidate(candidate->sdp_mid(),
|
||||
static_cast<int>(mediasection_index),
|
||||
updated_candidate));
|
||||
if (!candidate_collection_[mediasection_index].HasCandidate(
|
||||
updated_candidate_wrapper.get())) {
|
||||
candidate_collection_[mediasection_index].add(
|
||||
updated_candidate_wrapper.release());
|
||||
UpdateConnectionAddress(
|
||||
candidate_collection_[mediasection_index],
|
||||
description_->contents()[mediasection_index].media_description());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t JsepSessionDescription::RemoveCandidates(
|
||||
const std::vector<Candidate>& candidates) {
|
||||
size_t num_removed = 0;
|
||||
for (auto& candidate : candidates) {
|
||||
int mediasection_index = GetMediasectionIndex(candidate);
|
||||
if (mediasection_index < 0) {
|
||||
// Not found.
|
||||
continue;
|
||||
}
|
||||
num_removed += candidate_collection_[mediasection_index].remove(candidate);
|
||||
UpdateConnectionAddress(
|
||||
candidate_collection_[mediasection_index],
|
||||
description_->contents()[mediasection_index].media_description());
|
||||
}
|
||||
return num_removed;
|
||||
}
|
||||
|
||||
size_t JsepSessionDescription::number_of_mediasections() const {
|
||||
if (!description_)
|
||||
return 0;
|
||||
return description_->contents().size();
|
||||
}
|
||||
|
||||
const IceCandidateCollection* JsepSessionDescription::candidates(
|
||||
size_t mediasection_index) const {
|
||||
if (mediasection_index >= candidate_collection_.size())
|
||||
return NULL;
|
||||
return &candidate_collection_[mediasection_index];
|
||||
}
|
||||
|
||||
bool JsepSessionDescription::ToString(std::string* out) const {
|
||||
if (!description_ || !out) {
|
||||
return false;
|
||||
}
|
||||
*out = SdpSerialize(*this);
|
||||
return !out->empty();
|
||||
}
|
||||
|
||||
bool JsepSessionDescription::GetMediasectionIndex(
|
||||
const IceCandidateInterface* candidate,
|
||||
size_t* index) {
|
||||
if (!candidate || !index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the candidate has no valid mline index or sdp_mid, it is impossible
|
||||
// to find a match.
|
||||
if (candidate->sdp_mid().empty() &&
|
||||
(candidate->sdp_mline_index() < 0 ||
|
||||
static_cast<size_t>(candidate->sdp_mline_index()) >=
|
||||
description_->contents().size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (candidate->sdp_mline_index() >= 0)
|
||||
*index = static_cast<size_t>(candidate->sdp_mline_index());
|
||||
if (description_ && !candidate->sdp_mid().empty()) {
|
||||
bool found = false;
|
||||
// Try to match the sdp_mid with content name.
|
||||
for (size_t i = 0; i < description_->contents().size(); ++i) {
|
||||
if (candidate->sdp_mid() == description_->contents().at(i).name) {
|
||||
*index = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// If the sdp_mid is presented but we can't find a match, we consider
|
||||
// this as an error.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int JsepSessionDescription::GetMediasectionIndex(const Candidate& candidate) {
|
||||
// Find the description with a matching transport name of the candidate.
|
||||
const std::string& transport_name = candidate.transport_name();
|
||||
for (size_t i = 0; i < description_->contents().size(); ++i) {
|
||||
if (transport_name == description_->contents().at(i).name) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* Copyright 2012 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 "api/jsep_session_description.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/candidate.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/jsep_ice_candidate.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/webrtc_sdp.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/net_helper.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using cricket::MediaProtocolType;
|
||||
using ::testing::Values;
|
||||
using webrtc::IceCandidateCollection;
|
||||
using webrtc::IceCandidateInterface;
|
||||
using webrtc::JsepIceCandidate;
|
||||
using webrtc::JsepSessionDescription;
|
||||
using webrtc::SdpType;
|
||||
using webrtc::SessionDescriptionInterface;
|
||||
|
||||
static const char kCandidateUfrag[] = "ufrag";
|
||||
static const char kCandidatePwd[] = "pwd";
|
||||
static const char kCandidateUfragVoice[] = "ufrag_voice";
|
||||
static const char kCandidatePwdVoice[] = "pwd_voice";
|
||||
static const char kCandidateUfragVideo[] = "ufrag_video";
|
||||
static const char kCandidatePwdVideo[] = "pwd_video";
|
||||
static const char kCandidateFoundation[] = "a0+B/1";
|
||||
static const uint32_t kCandidatePriority = 2130706432U; // pref = 1.0
|
||||
static const uint32_t kCandidateGeneration = 2;
|
||||
|
||||
// This creates a session description with both audio and video media contents.
|
||||
// In SDP this is described by two m lines, one audio and one video.
|
||||
static std::unique_ptr<cricket::SessionDescription>
|
||||
CreateCricketSessionDescription() {
|
||||
auto desc = std::make_unique<cricket::SessionDescription>();
|
||||
|
||||
// AudioContentDescription
|
||||
auto audio = std::make_unique<cricket::AudioContentDescription>();
|
||||
// VideoContentDescription
|
||||
auto video = std::make_unique<cricket::VideoContentDescription>();
|
||||
|
||||
audio->AddCodec(cricket::CreateAudioCodec(103, "ISAC", 16000, 0));
|
||||
desc->AddContent(cricket::CN_AUDIO, MediaProtocolType::kRtp,
|
||||
std::move(audio));
|
||||
|
||||
video->AddCodec(cricket::CreateVideoCodec(120, "VP8"));
|
||||
desc->AddContent(cricket::CN_VIDEO, MediaProtocolType::kRtp,
|
||||
std::move(video));
|
||||
|
||||
desc->AddTransportInfo(cricket::TransportInfo(
|
||||
cricket::CN_AUDIO,
|
||||
cricket::TransportDescription(
|
||||
std::vector<std::string>(), kCandidateUfragVoice, kCandidatePwdVoice,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)));
|
||||
desc->AddTransportInfo(cricket::TransportInfo(
|
||||
cricket::CN_VIDEO,
|
||||
cricket::TransportDescription(
|
||||
std::vector<std::string>(), kCandidateUfragVideo, kCandidatePwdVideo,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)));
|
||||
return desc;
|
||||
}
|
||||
|
||||
class JsepSessionDescriptionTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
int port = 1234;
|
||||
rtc::SocketAddress address("127.0.0.1", port++);
|
||||
cricket::Candidate candidate(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
address, 1, "", "", "local", 0, "1");
|
||||
candidate_ = candidate;
|
||||
const std::string session_id = rtc::ToString(rtc::CreateRandomId64());
|
||||
const std::string session_version = rtc::ToString(rtc::CreateRandomId());
|
||||
jsep_desc_ = std::make_unique<JsepSessionDescription>(SdpType::kOffer);
|
||||
ASSERT_TRUE(jsep_desc_->Initialize(CreateCricketSessionDescription(),
|
||||
session_id, session_version));
|
||||
}
|
||||
|
||||
std::string Serialize(const SessionDescriptionInterface* desc) {
|
||||
std::string sdp;
|
||||
EXPECT_TRUE(desc->ToString(&sdp));
|
||||
EXPECT_FALSE(sdp.empty());
|
||||
return sdp;
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> DeSerialize(
|
||||
const std::string& sdp) {
|
||||
auto jsep_desc = std::make_unique<JsepSessionDescription>(SdpType::kOffer);
|
||||
EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jsep_desc.get(), nullptr));
|
||||
return std::move(jsep_desc);
|
||||
}
|
||||
|
||||
cricket::Candidate candidate_;
|
||||
std::unique_ptr<JsepSessionDescription> jsep_desc_;
|
||||
};
|
||||
|
||||
TEST_F(JsepSessionDescriptionTest, CloneDefault) {
|
||||
auto new_desc = jsep_desc_->Clone();
|
||||
EXPECT_EQ(jsep_desc_->type(), new_desc->type());
|
||||
std::string old_desc_string;
|
||||
std::string new_desc_string;
|
||||
EXPECT_TRUE(jsep_desc_->ToString(&old_desc_string));
|
||||
EXPECT_TRUE(new_desc->ToString(&new_desc_string));
|
||||
EXPECT_EQ(old_desc_string, new_desc_string);
|
||||
EXPECT_EQ(jsep_desc_->session_id(), new_desc->session_id());
|
||||
EXPECT_EQ(jsep_desc_->session_version(), new_desc->session_version());
|
||||
}
|
||||
|
||||
TEST_F(JsepSessionDescriptionTest, CloneRollback) {
|
||||
auto jsep_desc = std::make_unique<JsepSessionDescription>(SdpType::kRollback);
|
||||
auto new_desc = jsep_desc->Clone();
|
||||
EXPECT_EQ(jsep_desc->type(), new_desc->type());
|
||||
}
|
||||
|
||||
TEST_F(JsepSessionDescriptionTest, CloneWithCandidates) {
|
||||
cricket::Candidate candidate_v4(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate_v6(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice_v4("audio", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6("audio", 0, candidate_v6);
|
||||
JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
|
||||
auto new_desc = jsep_desc_->Clone();
|
||||
EXPECT_EQ(jsep_desc_->type(), new_desc->type());
|
||||
std::string old_desc_string;
|
||||
std::string new_desc_string;
|
||||
EXPECT_TRUE(jsep_desc_->ToString(&old_desc_string));
|
||||
EXPECT_TRUE(new_desc->ToString(&new_desc_string));
|
||||
EXPECT_EQ(old_desc_string, new_desc_string);
|
||||
}
|
||||
|
||||
// Test that number_of_mediasections() returns the number of media contents in
|
||||
// a session description.
|
||||
TEST_F(JsepSessionDescriptionTest, CheckSessionDescription) {
|
||||
EXPECT_EQ(2u, jsep_desc_->number_of_mediasections());
|
||||
}
|
||||
|
||||
// Test that we can add a candidate to a session description without MID.
|
||||
TEST_F(JsepSessionDescriptionTest, AddCandidateWithoutMid) {
|
||||
JsepIceCandidate jsep_candidate("", 0, candidate_);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
|
||||
ASSERT_TRUE(ice_candidates != NULL);
|
||||
EXPECT_EQ(1u, ice_candidates->count());
|
||||
const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
|
||||
ASSERT_TRUE(ice_candidate != NULL);
|
||||
candidate_.set_username(kCandidateUfragVoice);
|
||||
candidate_.set_password(kCandidatePwdVoice);
|
||||
EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
|
||||
EXPECT_EQ(0, ice_candidate->sdp_mline_index());
|
||||
EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
|
||||
}
|
||||
|
||||
// Test that we can add and remove candidates to a session description with
|
||||
// MID. Removing candidates requires MID (transport_name).
|
||||
TEST_F(JsepSessionDescriptionTest, AddAndRemoveCandidatesWithMid) {
|
||||
// mid and m-line index don't match, in this case mid is preferred.
|
||||
std::string mid = "video";
|
||||
JsepIceCandidate jsep_candidate(mid, 0, candidate_);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
|
||||
const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(1);
|
||||
ASSERT_TRUE(ice_candidates != NULL);
|
||||
EXPECT_EQ(1u, ice_candidates->count());
|
||||
const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
|
||||
ASSERT_TRUE(ice_candidate != NULL);
|
||||
candidate_.set_username(kCandidateUfragVideo);
|
||||
candidate_.set_password(kCandidatePwdVideo);
|
||||
EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
|
||||
// The mline index should have been updated according to mid.
|
||||
EXPECT_EQ(1, ice_candidate->sdp_mline_index());
|
||||
|
||||
std::vector<cricket::Candidate> candidates(1, candidate_);
|
||||
candidates[0].set_transport_name(mid);
|
||||
EXPECT_EQ(1u, jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
|
||||
EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
|
||||
}
|
||||
|
||||
TEST_F(JsepSessionDescriptionTest, AddCandidateAlreadyHasUfrag) {
|
||||
candidate_.set_username(kCandidateUfrag);
|
||||
candidate_.set_password(kCandidatePwd);
|
||||
JsepIceCandidate jsep_candidate("audio", 0, candidate_);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
|
||||
ASSERT_TRUE(ice_candidates != NULL);
|
||||
EXPECT_EQ(1u, ice_candidates->count());
|
||||
const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
|
||||
ASSERT_TRUE(ice_candidate != NULL);
|
||||
candidate_.set_username(kCandidateUfrag);
|
||||
candidate_.set_password(kCandidatePwd);
|
||||
EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
|
||||
|
||||
EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
|
||||
}
|
||||
|
||||
// Test that we can not add a candidate if there is no corresponding media
|
||||
// content in the session description.
|
||||
TEST_F(JsepSessionDescriptionTest, AddBadCandidate) {
|
||||
JsepIceCandidate bad_candidate1("", 55, candidate_);
|
||||
EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate1));
|
||||
|
||||
JsepIceCandidate bad_candidate2("some weird mid", 0, candidate_);
|
||||
EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2));
|
||||
}
|
||||
|
||||
// Tests that repeatedly adding the same candidate, with or without credentials,
|
||||
// does not increase the number of candidates in the description.
|
||||
TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) {
|
||||
JsepIceCandidate jsep_candidate("", 0, candidate_);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
|
||||
|
||||
// Add the same candidate again. It should be ignored.
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
|
||||
|
||||
// Create a new candidate, identical except that the ufrag and pwd are now
|
||||
// populated.
|
||||
candidate_.set_username(kCandidateUfragVoice);
|
||||
candidate_.set_password(kCandidatePwdVoice);
|
||||
JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_);
|
||||
|
||||
// This should also be identified as redundant and ignored.
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials));
|
||||
EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
|
||||
}
|
||||
|
||||
// Test that the connection address is set to a hostname address after adding a
|
||||
// hostname candidate.
|
||||
TEST_F(JsepSessionDescriptionTest, AddHostnameCandidate) {
|
||||
cricket::Candidate c;
|
||||
c.set_component(cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
|
||||
c.set_address(rtc::SocketAddress("example.local", 1234));
|
||||
c.set_type(cricket::LOCAL_PORT_TYPE);
|
||||
const size_t audio_index = 0;
|
||||
JsepIceCandidate hostname_candidate("audio", audio_index, c);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate));
|
||||
|
||||
ASSERT_NE(nullptr, jsep_desc_->description());
|
||||
ASSERT_EQ(2u, jsep_desc_->description()->contents().size());
|
||||
const auto& content = jsep_desc_->description()->contents()[audio_index];
|
||||
EXPECT_EQ("0.0.0.0:9",
|
||||
content.media_description()->connection_address().ToString());
|
||||
}
|
||||
|
||||
// Test that we can serialize a JsepSessionDescription and deserialize it again.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) {
|
||||
std::string sdp = Serialize(jsep_desc_.get());
|
||||
|
||||
auto parsed_jsep_desc = DeSerialize(sdp);
|
||||
EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
|
||||
|
||||
std::string parsed_sdp = Serialize(parsed_jsep_desc.get());
|
||||
EXPECT_EQ(sdp, parsed_sdp);
|
||||
}
|
||||
|
||||
// Test that we can serialize a JsepSessionDescription when a hostname candidate
|
||||
// is the default destination and deserialize it again. The connection address
|
||||
// in the deserialized description should be the dummy address 0.0.0.0:9.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithHostnameCandidate) {
|
||||
cricket::Candidate c;
|
||||
c.set_component(cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
|
||||
c.set_address(rtc::SocketAddress("example.local", 1234));
|
||||
c.set_type(cricket::LOCAL_PORT_TYPE);
|
||||
const size_t audio_index = 0;
|
||||
const size_t video_index = 1;
|
||||
JsepIceCandidate hostname_candidate_audio("audio", audio_index, c);
|
||||
JsepIceCandidate hostname_candidate_video("video", video_index, c);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_audio));
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_video));
|
||||
|
||||
std::string sdp = Serialize(jsep_desc_.get());
|
||||
|
||||
auto parsed_jsep_desc = DeSerialize(sdp);
|
||||
EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
|
||||
|
||||
ASSERT_NE(nullptr, parsed_jsep_desc->description());
|
||||
ASSERT_EQ(2u, parsed_jsep_desc->description()->contents().size());
|
||||
const auto& audio_content =
|
||||
parsed_jsep_desc->description()->contents()[audio_index];
|
||||
const auto& video_content =
|
||||
parsed_jsep_desc->description()->contents()[video_index];
|
||||
EXPECT_EQ("0.0.0.0:9",
|
||||
audio_content.media_description()->connection_address().ToString());
|
||||
EXPECT_EQ("0.0.0.0:9",
|
||||
video_content.media_description()->connection_address().ToString());
|
||||
}
|
||||
|
||||
// Tests that we can serialize and deserialize a JsepSesssionDescription
|
||||
// with candidates.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) {
|
||||
std::string sdp = Serialize(jsep_desc_.get());
|
||||
|
||||
// Add a candidate and check that the serialized result is different.
|
||||
JsepIceCandidate jsep_candidate("audio", 0, candidate_);
|
||||
EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
|
||||
std::string sdp_with_candidate = Serialize(jsep_desc_.get());
|
||||
EXPECT_NE(sdp, sdp_with_candidate);
|
||||
|
||||
auto parsed_jsep_desc = DeSerialize(sdp_with_candidate);
|
||||
std::string parsed_sdp_with_candidate = Serialize(parsed_jsep_desc.get());
|
||||
|
||||
EXPECT_EQ(sdp_with_candidate, parsed_sdp_with_candidate);
|
||||
}
|
||||
|
||||
// TODO(zhihuang): Modify these tests. These are used to verify that after
|
||||
// adding the candidates, the connection_address field is set correctly. Modify
|
||||
// those so that the "connection address" is tested directly.
|
||||
// Tests serialization of SDP with only IPv6 candidates and verifies that IPv6
|
||||
// is used as default address in c line according to preference.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithIPv6Only) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with both IPv4 and IPv6 candidates and
|
||||
// verifies that IPv4 is used as default address in c line even if the
|
||||
// preference of IPv4 is lower.
|
||||
TEST_F(JsepSessionDescriptionTest,
|
||||
SerializeSessionDescriptionWithBothIPFamilies) {
|
||||
cricket::Candidate candidate_v4(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate_v6(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice_v4("audio", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6("audio", 0, candidate_v6);
|
||||
JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos);
|
||||
// Shouldn't have a IP6 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP6"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with both UDP and TCP candidates and
|
||||
// verifies that UDP is used as default address in c line even if the
|
||||
// preference of UDP is lower.
|
||||
TEST_F(JsepSessionDescriptionTest,
|
||||
SerializeSessionDescriptionWithBothProtocols) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("fe80::1234:5678:abcd:ef12", 1235), kCandidatePriority,
|
||||
"", "", cricket::LOCAL_PORT_TYPE, kCandidateGeneration,
|
||||
kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"),
|
||||
std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with only TCP candidates and verifies that
|
||||
// null IPv4 is used as default address in c line.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithTCPOnly) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
EXPECT_EQ(message.find("c=IN IP6 ::3"), std::string::npos);
|
||||
// Should have a c line like this one when no any default exists.
|
||||
EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests that the connection address will be correctly set when the Candidate is
|
||||
// removed.
|
||||
TEST_F(JsepSessionDescriptionTest, RemoveCandidateAndSetConnectionAddress) {
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate1.set_transport_name("audio");
|
||||
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate2.set_transport_name("audio");
|
||||
|
||||
cricket::Candidate candidate3(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.1", 1236), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate3.set_transport_name("audio");
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("audio", 0, candidate3);
|
||||
|
||||
size_t audio_index = 0;
|
||||
auto media_desc =
|
||||
jsep_desc_->description()->contents()[audio_index].media_description();
|
||||
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
EXPECT_EQ("192.168.1.1:1236", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.push_back(candidate3);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.clear();
|
||||
candidates.push_back(candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.clear();
|
||||
candidates.push_back(candidate1);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("0.0.0.0:9", media_desc->connection_address().ToString());
|
||||
}
|
||||
|
||||
class EnumerateAllSdpTypesTest : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<SdpType> {
|
||||
};
|
||||
|
||||
TEST_P(EnumerateAllSdpTypesTest, TestIdentity) {
|
||||
SdpType type = GetParam();
|
||||
|
||||
const char* str = webrtc::SdpTypeToString(type);
|
||||
EXPECT_EQ(type, webrtc::SdpTypeFromString(str));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(JsepSessionDescriptionTest,
|
||||
EnumerateAllSdpTypesTest,
|
||||
Values(SdpType::kOffer,
|
||||
SdpType::kPrAnswer,
|
||||
SdpType::kAnswer));
|
||||
642
TMessagesProj/jni/voip/webrtc/pc/jsep_transport.cc
Normal file
642
TMessagesProj/jni/voip/webrtc/pc/jsep_transport.cc
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
/*
|
||||
* Copyright 2018 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 "pc/jsep_transport.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/candidate.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/p2p_transport_channel.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
using webrtc::SdpType;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription() {}
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
int rtp_abs_sendtime_extn_id,
|
||||
const TransportDescription& transport_desc)
|
||||
: rtcp_mux_enabled(rtcp_mux_enabled),
|
||||
encrypted_header_extension_ids(encrypted_header_extension_ids),
|
||||
rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id),
|
||||
transport_desc(transport_desc) {}
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription(
|
||||
const JsepTransportDescription& from)
|
||||
: rtcp_mux_enabled(from.rtcp_mux_enabled),
|
||||
encrypted_header_extension_ids(from.encrypted_header_extension_ids),
|
||||
rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id),
|
||||
transport_desc(from.transport_desc) {}
|
||||
|
||||
JsepTransportDescription::~JsepTransportDescription() = default;
|
||||
|
||||
JsepTransportDescription& JsepTransportDescription::operator=(
|
||||
const JsepTransportDescription& from) {
|
||||
if (this == &from) {
|
||||
return *this;
|
||||
}
|
||||
rtcp_mux_enabled = from.rtcp_mux_enabled;
|
||||
encrypted_header_extension_ids = from.encrypted_header_extension_ids;
|
||||
rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id;
|
||||
transport_desc = from.transport_desc;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsepTransport::JsepTransport(
|
||||
const std::string& mid,
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate,
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport,
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice_transport,
|
||||
std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport,
|
||||
std::unique_ptr<webrtc::SrtpTransport> sdes_transport,
|
||||
std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport,
|
||||
std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport,
|
||||
std::unique_ptr<DtlsTransportInternal> rtcp_dtls_transport,
|
||||
std::unique_ptr<SctpTransportInternal> sctp_transport,
|
||||
std::function<void()> rtcp_mux_active_callback)
|
||||
: network_thread_(rtc::Thread::Current()),
|
||||
mid_(mid),
|
||||
local_certificate_(local_certificate),
|
||||
ice_transport_(std::move(ice_transport)),
|
||||
rtcp_ice_transport_(std::move(rtcp_ice_transport)),
|
||||
unencrypted_rtp_transport_(std::move(unencrypted_rtp_transport)),
|
||||
sdes_transport_(std::move(sdes_transport)),
|
||||
dtls_srtp_transport_(std::move(dtls_srtp_transport)),
|
||||
rtp_dtls_transport_(rtp_dtls_transport
|
||||
? rtc::make_ref_counted<webrtc::DtlsTransport>(
|
||||
std::move(rtp_dtls_transport))
|
||||
: nullptr),
|
||||
rtcp_dtls_transport_(rtcp_dtls_transport
|
||||
? rtc::make_ref_counted<webrtc::DtlsTransport>(
|
||||
std::move(rtcp_dtls_transport))
|
||||
: nullptr),
|
||||
sctp_transport_(sctp_transport
|
||||
? rtc::make_ref_counted<webrtc::SctpTransport>(
|
||||
std::move(sctp_transport))
|
||||
: nullptr),
|
||||
rtcp_mux_active_callback_(std::move(rtcp_mux_active_callback)) {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::JsepTransport");
|
||||
RTC_DCHECK(ice_transport_);
|
||||
RTC_DCHECK(rtp_dtls_transport_);
|
||||
// `rtcp_ice_transport_` must be present iff `rtcp_dtls_transport_` is
|
||||
// present.
|
||||
RTC_DCHECK_EQ((rtcp_ice_transport_ != nullptr),
|
||||
(rtcp_dtls_transport_ != nullptr));
|
||||
// Verify the "only one out of these three can be set" invariant.
|
||||
if (unencrypted_rtp_transport_) {
|
||||
RTC_DCHECK(!sdes_transport);
|
||||
RTC_DCHECK(!dtls_srtp_transport);
|
||||
} else if (sdes_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport);
|
||||
RTC_DCHECK(!dtls_srtp_transport);
|
||||
} else {
|
||||
RTC_DCHECK(dtls_srtp_transport_);
|
||||
RTC_DCHECK(!unencrypted_rtp_transport);
|
||||
RTC_DCHECK(!sdes_transport);
|
||||
}
|
||||
|
||||
if (sctp_transport_) {
|
||||
sctp_transport_->SetDtlsTransport(rtp_dtls_transport_);
|
||||
}
|
||||
}
|
||||
|
||||
JsepTransport::~JsepTransport() {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::~JsepTransport");
|
||||
if (sctp_transport_) {
|
||||
sctp_transport_->Clear();
|
||||
}
|
||||
|
||||
// Clear all DtlsTransports. There may be pointers to these from
|
||||
// other places, so we can't assume they'll be deleted by the destructor.
|
||||
rtp_dtls_transport_->Clear();
|
||||
if (rtcp_dtls_transport_) {
|
||||
rtcp_dtls_transport_->Clear();
|
||||
}
|
||||
|
||||
// ICE will be the last transport to be deleted.
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
SdpType type) {
|
||||
webrtc::RTCError error;
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::SetLocalJsepTransportDescription");
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
IceParameters ice_parameters =
|
||||
jsep_description.transport_desc.GetIceParameters();
|
||||
webrtc::RTCError ice_parameters_result = ice_parameters.Validate();
|
||||
if (!ice_parameters_result.ok()) {
|
||||
rtc::StringBuilder sb;
|
||||
sb << "Invalid ICE parameters: " << ice_parameters_result.message();
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
sb.Release());
|
||||
}
|
||||
|
||||
if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
|
||||
ContentSource::CS_LOCAL)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to setup RTCP mux.");
|
||||
}
|
||||
|
||||
if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
|
||||
jsep_description.encrypted_header_extension_ids);
|
||||
}
|
||||
bool ice_restarting =
|
||||
local_description_ != nullptr &&
|
||||
IceCredentialsChanged(local_description_->transport_desc.ice_ufrag,
|
||||
local_description_->transport_desc.ice_pwd,
|
||||
ice_parameters.ufrag, ice_parameters.pwd);
|
||||
local_description_.reset(new JsepTransportDescription(jsep_description));
|
||||
|
||||
rtc::SSLFingerprint* local_fp =
|
||||
local_description_->transport_desc.identity_fingerprint.get();
|
||||
|
||||
if (!local_fp) {
|
||||
local_certificate_ = nullptr;
|
||||
} else {
|
||||
error = VerifyCertificateFingerprint(local_certificate_.get(), local_fp);
|
||||
if (!error.ok()) {
|
||||
local_description_.reset();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
RTC_DCHECK(rtp_dtls_transport_->internal());
|
||||
rtp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
|
||||
ice_parameters);
|
||||
|
||||
if (rtcp_dtls_transport_) {
|
||||
RTC_DCHECK(rtcp_dtls_transport_->internal());
|
||||
rtcp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
|
||||
ice_parameters);
|
||||
}
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
|
||||
error = NegotiateAndSetDtlsParameters(type);
|
||||
}
|
||||
if (!error.ok()) {
|
||||
local_description_.reset();
|
||||
return error;
|
||||
}
|
||||
|
||||
if (needs_ice_restart_ && ice_restarting) {
|
||||
needs_ice_restart_ = false;
|
||||
RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag cleared for transport "
|
||||
<< mid();
|
||||
}
|
||||
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::SetRemoteJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type) {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::SetLocalJsepTransportDescription");
|
||||
webrtc::RTCError error;
|
||||
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
IceParameters ice_parameters =
|
||||
jsep_description.transport_desc.GetIceParameters();
|
||||
webrtc::RTCError ice_parameters_result = ice_parameters.Validate();
|
||||
if (!ice_parameters_result.ok()) {
|
||||
remote_description_.reset();
|
||||
rtc::StringBuilder sb;
|
||||
sb << "Invalid ICE parameters: " << ice_parameters_result.message();
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
sb.Release());
|
||||
}
|
||||
|
||||
if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
|
||||
ContentSource::CS_REMOTE)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to setup RTCP mux.");
|
||||
}
|
||||
|
||||
if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
|
||||
jsep_description.encrypted_header_extension_ids);
|
||||
dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
|
||||
jsep_description.rtp_abs_sendtime_extn_id);
|
||||
}
|
||||
|
||||
remote_description_.reset(new JsepTransportDescription(jsep_description));
|
||||
RTC_DCHECK(rtp_dtls_transport());
|
||||
SetRemoteIceParameters(ice_parameters, rtp_dtls_transport()->ice_transport());
|
||||
|
||||
if (rtcp_dtls_transport()) {
|
||||
SetRemoteIceParameters(ice_parameters,
|
||||
rtcp_dtls_transport()->ice_transport());
|
||||
}
|
||||
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
|
||||
error = NegotiateAndSetDtlsParameters(SdpType::kOffer);
|
||||
}
|
||||
if (!error.ok()) {
|
||||
remote_description_.reset();
|
||||
return error;
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::AddRemoteCandidates(
|
||||
const Candidates& candidates) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!local_description_ || !remote_description_) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE,
|
||||
mid() +
|
||||
" is not ready to use the remote candidate "
|
||||
"because the local or remote description is "
|
||||
"not set.");
|
||||
}
|
||||
|
||||
for (const cricket::Candidate& candidate : candidates) {
|
||||
auto transport =
|
||||
candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
|
||||
? rtp_dtls_transport_
|
||||
: rtcp_dtls_transport_;
|
||||
if (!transport) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Candidate has an unknown component: " +
|
||||
candidate.ToSensitiveString() + " for mid " +
|
||||
mid());
|
||||
}
|
||||
RTC_DCHECK(transport->internal() && transport->internal()->ice_transport());
|
||||
transport->internal()->ice_transport()->AddRemoteCandidate(candidate);
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
void JsepTransport::SetNeedsIceRestartFlag() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!needs_ice_restart_) {
|
||||
needs_ice_restart_ = true;
|
||||
RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid();
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<rtc::SSLRole> JsepTransport::GetDtlsRole() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
RTC_DCHECK(rtp_dtls_transport_);
|
||||
RTC_DCHECK(rtp_dtls_transport_->internal());
|
||||
rtc::SSLRole dtls_role;
|
||||
if (!rtp_dtls_transport_->internal()->GetDtlsRole(&dtls_role)) {
|
||||
return absl::optional<rtc::SSLRole>();
|
||||
}
|
||||
|
||||
return absl::optional<rtc::SSLRole>(dtls_role);
|
||||
}
|
||||
|
||||
bool JsepTransport::GetStats(TransportStats* stats) {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::GetStats");
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
stats->transport_name = mid();
|
||||
stats->channel_stats.clear();
|
||||
RTC_DCHECK(rtp_dtls_transport_->internal());
|
||||
bool ret = GetTransportStats(rtp_dtls_transport_->internal(),
|
||||
ICE_CANDIDATE_COMPONENT_RTP, stats);
|
||||
|
||||
if (rtcp_dtls_transport_) {
|
||||
RTC_DCHECK(rtcp_dtls_transport_->internal());
|
||||
ret &= GetTransportStats(rtcp_dtls_transport_->internal(),
|
||||
ICE_CANDIDATE_COMPONENT_RTCP, stats);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::VerifyCertificateFingerprint(
|
||||
const rtc::RTCCertificate* certificate,
|
||||
const rtc::SSLFingerprint* fingerprint) const {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::VerifyCertificateFingerprint");
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!fingerprint) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"No fingerprint");
|
||||
}
|
||||
if (!certificate) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Fingerprint provided but no identity available.");
|
||||
}
|
||||
std::unique_ptr<rtc::SSLFingerprint> fp_tmp =
|
||||
rtc::SSLFingerprint::CreateUnique(fingerprint->algorithm,
|
||||
*certificate->identity());
|
||||
RTC_DCHECK(fp_tmp.get() != NULL);
|
||||
if (*fp_tmp == *fingerprint) {
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
char ss_buf[1024];
|
||||
rtc::SimpleStringBuilder desc(ss_buf);
|
||||
desc << "Local fingerprint does not match identity. Expected: ";
|
||||
desc << fp_tmp->ToString();
|
||||
desc << " Got: " << fingerprint->ToString();
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
std::string(desc.str()));
|
||||
}
|
||||
|
||||
void JsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (dtls_srtp_transport_) {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Setting active_reset_srtp_params of DtlsSrtpTransport to: "
|
||||
<< active_reset_srtp_params;
|
||||
dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params);
|
||||
}
|
||||
}
|
||||
|
||||
void JsepTransport::SetRemoteIceParameters(
|
||||
const IceParameters& ice_parameters,
|
||||
IceTransportInternal* ice_transport) {
|
||||
TRACE_EVENT0("webrtc", "JsepTransport::SetRemoteIceParameters");
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
RTC_DCHECK(ice_transport);
|
||||
RTC_DCHECK(remote_description_);
|
||||
ice_transport->SetRemoteIceParameters(ice_parameters);
|
||||
ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode);
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::SetNegotiatedDtlsParameters(
|
||||
DtlsTransportInternal* dtls_transport,
|
||||
absl::optional<rtc::SSLRole> dtls_role,
|
||||
rtc::SSLFingerprint* remote_fingerprint) {
|
||||
RTC_DCHECK(dtls_transport);
|
||||
return dtls_transport->SetRemoteParameters(
|
||||
remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(),
|
||||
remote_fingerprint->digest.size(), dtls_role);
|
||||
}
|
||||
|
||||
bool JsepTransport::SetRtcpMux(bool enable,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
bool ret = false;
|
||||
switch (type) {
|
||||
case SdpType::kOffer:
|
||||
ret = rtcp_mux_negotiator_.SetOffer(enable, source);
|
||||
break;
|
||||
case SdpType::kPrAnswer:
|
||||
// This may activate RTCP muxing, but we don't yet destroy the transport
|
||||
// because the final answer may deactivate it.
|
||||
ret = rtcp_mux_negotiator_.SetProvisionalAnswer(enable, source);
|
||||
break;
|
||||
case SdpType::kAnswer:
|
||||
ret = rtcp_mux_negotiator_.SetAnswer(enable, source);
|
||||
if (ret && rtcp_mux_negotiator_.IsActive()) {
|
||||
ActivateRtcpMux();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto transport = rtp_transport();
|
||||
transport->SetRtcpMuxEnabled(rtcp_mux_negotiator_.IsActive());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JsepTransport::ActivateRtcpMux() {
|
||||
if (unencrypted_rtp_transport_) {
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr);
|
||||
} else if (sdes_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
sdes_transport_->SetRtcpPacketTransport(nullptr);
|
||||
} else if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(dtls_srtp_transport_);
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(),
|
||||
/*rtcp_dtls_transport=*/nullptr);
|
||||
}
|
||||
rtcp_dtls_transport_ = nullptr; // Destroy this reference.
|
||||
// Notify the JsepTransportController to update the aggregate states.
|
||||
rtcp_mux_active_callback_();
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::NegotiateAndSetDtlsParameters(
|
||||
SdpType local_description_type) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!local_description_ || !remote_description_) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE,
|
||||
"Applying an answer transport description "
|
||||
"without applying any offer.");
|
||||
}
|
||||
std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint;
|
||||
absl::optional<rtc::SSLRole> negotiated_dtls_role;
|
||||
|
||||
rtc::SSLFingerprint* local_fp =
|
||||
local_description_->transport_desc.identity_fingerprint.get();
|
||||
rtc::SSLFingerprint* remote_fp =
|
||||
remote_description_->transport_desc.identity_fingerprint.get();
|
||||
if (remote_fp && local_fp) {
|
||||
remote_fingerprint = std::make_unique<rtc::SSLFingerprint>(*remote_fp);
|
||||
webrtc::RTCError error =
|
||||
NegotiateDtlsRole(local_description_type,
|
||||
local_description_->transport_desc.connection_role,
|
||||
remote_description_->transport_desc.connection_role,
|
||||
&negotiated_dtls_role);
|
||||
if (!error.ok()) {
|
||||
return error;
|
||||
}
|
||||
} else if (local_fp && (local_description_type == SdpType::kAnswer)) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Local fingerprint supplied when caller didn't offer DTLS.");
|
||||
} else {
|
||||
// We are not doing DTLS
|
||||
remote_fingerprint = std::make_unique<rtc::SSLFingerprint>(
|
||||
"", rtc::ArrayView<const uint8_t>());
|
||||
}
|
||||
// Now that we have negotiated everything, push it downward.
|
||||
// Note that we cache the result so that if we have race conditions
|
||||
// between future SetRemote/SetLocal invocations and new transport
|
||||
// creation, we have the negotiation state saved until a new
|
||||
// negotiation happens.
|
||||
RTC_DCHECK(rtp_dtls_transport());
|
||||
webrtc::RTCError error = SetNegotiatedDtlsParameters(
|
||||
rtp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get());
|
||||
if (!error.ok()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (rtcp_dtls_transport()) {
|
||||
error = SetNegotiatedDtlsParameters(
|
||||
rtcp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::NegotiateDtlsRole(
|
||||
SdpType local_description_type,
|
||||
ConnectionRole local_connection_role,
|
||||
ConnectionRole remote_connection_role,
|
||||
absl::optional<rtc::SSLRole>* negotiated_dtls_role) {
|
||||
// From RFC 4145, section-4.1, The following are the values that the
|
||||
// 'setup' attribute can take in an offer/answer exchange:
|
||||
// Offer Answer
|
||||
// ________________
|
||||
// active passive / holdconn
|
||||
// passive active / holdconn
|
||||
// actpass active / passive / holdconn
|
||||
// holdconn holdconn
|
||||
//
|
||||
// Set the role that is most conformant with RFC 5763, Section 5, bullet 1
|
||||
// The endpoint MUST use the setup attribute defined in [RFC4145].
|
||||
// The endpoint that is the offerer MUST use the setup attribute
|
||||
// value of setup:actpass and be prepared to receive a client_hello
|
||||
// before it receives the answer. The answerer MUST use either a
|
||||
// setup attribute value of setup:active or setup:passive. Note that
|
||||
// if the answerer uses setup:passive, then the DTLS handshake will
|
||||
// not begin until the answerer is received, which adds additional
|
||||
// latency. setup:active allows the answer and the DTLS handshake to
|
||||
// occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
|
||||
// party is active MUST initiate a DTLS handshake by sending a
|
||||
// ClientHello over each flow (host/port quartet).
|
||||
// IOW - actpass and passive modes should be treated as server and
|
||||
// active as client.
|
||||
// RFC 8842 section 5.3 updates this text, so that it is mandated
|
||||
// for the responder to handle offers with "active" and "passive"
|
||||
// as well as "actpass"
|
||||
bool is_remote_server = false;
|
||||
if (local_description_type == SdpType::kOffer) {
|
||||
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Offerer must use actpass value for setup attribute.");
|
||||
}
|
||||
|
||||
if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
|
||||
remote_connection_role == CONNECTIONROLE_PASSIVE ||
|
||||
remote_connection_role == CONNECTIONROLE_NONE) {
|
||||
is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
|
||||
} else {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must use either active or passive value "
|
||||
"for setup attribute.");
|
||||
}
|
||||
// If remote is NONE or ACTIVE it will act as client.
|
||||
} else {
|
||||
if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
|
||||
remote_connection_role != CONNECTIONROLE_NONE) {
|
||||
// Accept a remote role attribute that's not "actpass", but matches the
|
||||
// current negotiated role. This is allowed by dtls-sdp, though our
|
||||
// implementation will never generate such an offer as it's not
|
||||
// recommended.
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp,
|
||||
// section 5.5.
|
||||
auto current_dtls_role = GetDtlsRole();
|
||||
if (!current_dtls_role) {
|
||||
// Role not assigned yet. Verify that local role fits with remote role.
|
||||
switch (remote_connection_role) {
|
||||
case CONNECTIONROLE_ACTIVE:
|
||||
if (local_connection_role != CONNECTIONROLE_PASSIVE) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must be passive when offerer is active");
|
||||
}
|
||||
break;
|
||||
case CONNECTIONROLE_PASSIVE:
|
||||
if (local_connection_role != CONNECTIONROLE_ACTIVE) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must be active when offerer is passive");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((*current_dtls_role == rtc::SSL_CLIENT &&
|
||||
remote_connection_role == CONNECTIONROLE_ACTIVE) ||
|
||||
(*current_dtls_role == rtc::SSL_SERVER &&
|
||||
remote_connection_role == CONNECTIONROLE_PASSIVE)) {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Offerer must use current negotiated role for "
|
||||
"setup attribute.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (local_connection_role == CONNECTIONROLE_ACTIVE ||
|
||||
local_connection_role == CONNECTIONROLE_PASSIVE) {
|
||||
is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
|
||||
} else {
|
||||
return webrtc::RTCError(
|
||||
webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Answerer must use either active or passive value "
|
||||
"for setup attribute.");
|
||||
}
|
||||
|
||||
// If local is passive, local will act as server.
|
||||
}
|
||||
|
||||
*negotiated_dtls_role =
|
||||
(is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER);
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
|
||||
int component,
|
||||
TransportStats* stats) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
RTC_DCHECK(dtls_transport);
|
||||
TransportChannelStats substats;
|
||||
substats.component = component;
|
||||
dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes);
|
||||
dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
|
||||
dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
|
||||
substats.dtls_state = dtls_transport->dtls_state();
|
||||
rtc::SSLRole dtls_role;
|
||||
if (dtls_transport->GetDtlsRole(&dtls_role)) {
|
||||
substats.dtls_role = dtls_role;
|
||||
}
|
||||
if (!dtls_transport->ice_transport()->GetStats(
|
||||
&substats.ice_transport_stats)) {
|
||||
return false;
|
||||
}
|
||||
substats.ssl_peer_signature_algorithm =
|
||||
dtls_transport->GetSslPeerSignatureAlgorithm();
|
||||
stats->channel_stats.push_back(substats);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
320
TMessagesProj/jni/voip/webrtc/pc/jsep_transport.h
Normal file
320
TMessagesProj/jni/voip/webrtc/pc/jsep_transport.h
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JSEP_TRANSPORT_H_
|
||||
#define PC_JSEP_TRANSPORT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/data_channel_transport_interface.h"
|
||||
#include "media/sctp/sctp_transport_internal.h"
|
||||
#include "p2p/base/dtls_transport.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/dtls_srtp_transport.h"
|
||||
#include "pc/dtls_transport.h"
|
||||
#include "pc/rtcp_mux_filter.h"
|
||||
#include "pc/rtp_transport.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/sctp_transport.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/srtp_transport.h"
|
||||
#include "pc/transport_stats.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class DtlsTransportInternal;
|
||||
|
||||
struct JsepTransportDescription {
|
||||
public:
|
||||
JsepTransportDescription();
|
||||
JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
int rtp_abs_sendtime_extn_id,
|
||||
const TransportDescription& transport_description);
|
||||
JsepTransportDescription(const JsepTransportDescription& from);
|
||||
~JsepTransportDescription();
|
||||
|
||||
JsepTransportDescription& operator=(const JsepTransportDescription& from);
|
||||
|
||||
bool rtcp_mux_enabled = true;
|
||||
std::vector<int> encrypted_header_extension_ids;
|
||||
int rtp_abs_sendtime_extn_id = -1;
|
||||
// TODO(zhihuang): Add the ICE and DTLS related variables and methods from
|
||||
// TransportDescription and remove this extra layer of abstraction.
|
||||
TransportDescription transport_desc;
|
||||
};
|
||||
|
||||
// Helper class used by JsepTransportController that processes
|
||||
// TransportDescriptions. A TransportDescription represents the
|
||||
// transport-specific properties of an SDP m= section, processed according to
|
||||
// JSEP. Each transport consists of DTLS and ICE transport channels for RTP
|
||||
// (and possibly RTCP, if rtcp-mux isn't used).
|
||||
//
|
||||
// On Threading: JsepTransport performs work solely on the network thread, and
|
||||
// so its methods should only be called on the network thread.
|
||||
class JsepTransport {
|
||||
public:
|
||||
// `mid` is just used for log statements in order to identify the Transport.
|
||||
// Note that `local_certificate` is allowed to be null since a remote
|
||||
// description may be set before a local certificate is generated.
|
||||
JsepTransport(
|
||||
const std::string& mid,
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate,
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport,
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice_transport,
|
||||
std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport,
|
||||
std::unique_ptr<webrtc::SrtpTransport> sdes_transport,
|
||||
std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport,
|
||||
std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport,
|
||||
std::unique_ptr<DtlsTransportInternal> rtcp_dtls_transport,
|
||||
std::unique_ptr<SctpTransportInternal> sctp_transport,
|
||||
std::function<void()> rtcp_mux_active_callback);
|
||||
|
||||
~JsepTransport();
|
||||
|
||||
JsepTransport(const JsepTransport&) = delete;
|
||||
JsepTransport& operator=(const JsepTransport&) = delete;
|
||||
|
||||
// Returns the MID of this transport. This is only used for logging.
|
||||
const std::string& mid() const { return mid_; }
|
||||
|
||||
// Must be called before applying local session description.
|
||||
// Needed in order to verify the local fingerprint.
|
||||
void SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& local_certificate) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
local_certificate_ = local_certificate;
|
||||
}
|
||||
|
||||
// Return the local certificate provided by SetLocalCertificate.
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return local_certificate_;
|
||||
}
|
||||
|
||||
webrtc::RTCError SetLocalJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type);
|
||||
|
||||
// Set the remote TransportDescription to be used by DTLS and ICE channels
|
||||
// that are part of this Transport.
|
||||
webrtc::RTCError SetRemoteJsepTransportDescription(
|
||||
const JsepTransportDescription& jsep_description,
|
||||
webrtc::SdpType type);
|
||||
webrtc::RTCError AddRemoteCandidates(const Candidates& candidates);
|
||||
|
||||
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
|
||||
// set, offers should generate new ufrags/passwords until an ICE restart
|
||||
// occurs.
|
||||
//
|
||||
// This and `needs_ice_restart()` must be called on the network thread.
|
||||
void SetNeedsIceRestartFlag();
|
||||
|
||||
// Returns true if the ICE restart flag above was set, and no ICE restart has
|
||||
// occurred yet for this transport (by applying a local description with
|
||||
// changed ufrag/password).
|
||||
bool needs_ice_restart() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return needs_ice_restart_;
|
||||
}
|
||||
|
||||
// Returns role if negotiated, or empty absl::optional if it hasn't been
|
||||
// negotiated yet.
|
||||
absl::optional<rtc::SSLRole> GetDtlsRole() const;
|
||||
|
||||
// TODO(deadbeef): Make this const. See comment in transportcontroller.h.
|
||||
bool GetStats(TransportStats* stats);
|
||||
|
||||
const JsepTransportDescription* local_description() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return local_description_.get();
|
||||
}
|
||||
|
||||
const JsepTransportDescription* remote_description() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_description_.get();
|
||||
}
|
||||
|
||||
// Returns the rtp transport, if any.
|
||||
webrtc::RtpTransportInternal* rtp_transport() const {
|
||||
if (dtls_srtp_transport_) {
|
||||
return dtls_srtp_transport_.get();
|
||||
}
|
||||
if (sdes_transport_) {
|
||||
return sdes_transport_.get();
|
||||
}
|
||||
if (unencrypted_rtp_transport_) {
|
||||
return unencrypted_rtp_transport_.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DtlsTransportInternal* rtp_dtls_transport() const {
|
||||
if (rtp_dtls_transport_) {
|
||||
return rtp_dtls_transport_->internal();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DtlsTransportInternal* rtp_dtls_transport() {
|
||||
if (rtp_dtls_transport_) {
|
||||
return rtp_dtls_transport_->internal();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DtlsTransportInternal* rtcp_dtls_transport() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (rtcp_dtls_transport_) {
|
||||
return rtcp_dtls_transport_->internal();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DtlsTransportInternal* rtcp_dtls_transport() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (rtcp_dtls_transport_) {
|
||||
return rtcp_dtls_transport_->internal();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::DtlsTransport> RtpDtlsTransport() {
|
||||
return rtp_dtls_transport_;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::SctpTransport> SctpTransport() const {
|
||||
return sctp_transport_;
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/9719): Delete method, update callers to use
|
||||
// SctpTransport() instead.
|
||||
webrtc::DataChannelTransportInterface* data_channel_transport() const {
|
||||
return sctp_transport_.get();
|
||||
}
|
||||
|
||||
// TODO(deadbeef): The methods below are only public for testing. Should make
|
||||
// them utility functions or objects so they can be tested independently from
|
||||
// this class.
|
||||
|
||||
// Returns an error if the certificate's identity does not match the
|
||||
// fingerprint, or either is NULL.
|
||||
webrtc::RTCError VerifyCertificateFingerprint(
|
||||
const rtc::RTCCertificate* certificate,
|
||||
const rtc::SSLFingerprint* fingerprint) const;
|
||||
|
||||
void SetActiveResetSrtpParams(bool active_reset_srtp_params);
|
||||
|
||||
private:
|
||||
bool SetRtcpMux(bool enable, webrtc::SdpType type, ContentSource source);
|
||||
|
||||
void ActivateRtcpMux() RTC_RUN_ON(network_thread_);
|
||||
|
||||
// Negotiates and sets the DTLS parameters based on the current local and
|
||||
// remote transport description, such as the DTLS role to use, and whether
|
||||
// DTLS should be activated.
|
||||
//
|
||||
// Called when an answer TransportDescription is applied.
|
||||
webrtc::RTCError NegotiateAndSetDtlsParameters(
|
||||
webrtc::SdpType local_description_type);
|
||||
|
||||
// Negotiates the DTLS role based off the offer and answer as specified by
|
||||
// RFC 4145, section-4.1. Returns an RTCError if role cannot be determined
|
||||
// from the local description and remote description.
|
||||
webrtc::RTCError NegotiateDtlsRole(
|
||||
webrtc::SdpType local_description_type,
|
||||
ConnectionRole local_connection_role,
|
||||
ConnectionRole remote_connection_role,
|
||||
absl::optional<rtc::SSLRole>* negotiated_dtls_role);
|
||||
|
||||
// Pushes down the ICE parameters from the remote description.
|
||||
void SetRemoteIceParameters(const IceParameters& ice_parameters,
|
||||
IceTransportInternal* ice);
|
||||
|
||||
// Pushes down the DTLS parameters obtained via negotiation.
|
||||
static webrtc::RTCError SetNegotiatedDtlsParameters(
|
||||
DtlsTransportInternal* dtls_transport,
|
||||
absl::optional<rtc::SSLRole> dtls_role,
|
||||
rtc::SSLFingerprint* remote_fingerprint);
|
||||
|
||||
bool GetTransportStats(DtlsTransportInternal* dtls_transport,
|
||||
int component,
|
||||
TransportStats* stats);
|
||||
|
||||
// Owning thread, for safety checks
|
||||
const rtc::Thread* const network_thread_;
|
||||
const std::string mid_;
|
||||
// needs-ice-restart bit as described in JSEP.
|
||||
bool needs_ice_restart_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
std::unique_ptr<JsepTransportDescription> local_description_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
std::unique_ptr<JsepTransportDescription> remote_description_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// Ice transport which may be used by any of upper-layer transports (below).
|
||||
// Owned by JsepTransport and guaranteed to outlive the transports below.
|
||||
const rtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport_;
|
||||
const rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice_transport_;
|
||||
|
||||
// To avoid downcasting and make it type safe, keep three unique pointers for
|
||||
// different SRTP mode and only one of these is non-nullptr.
|
||||
const std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport_;
|
||||
const std::unique_ptr<webrtc::SrtpTransport> sdes_transport_;
|
||||
const std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport_;
|
||||
|
||||
const rtc::scoped_refptr<webrtc::DtlsTransport> rtp_dtls_transport_;
|
||||
// The RTCP transport is const for all usages, except that it is cleared
|
||||
// when RTCP multiplexing is turned on; this happens on the network thread.
|
||||
rtc::scoped_refptr<webrtc::DtlsTransport> rtcp_dtls_transport_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
const rtc::scoped_refptr<webrtc::SctpTransport> sctp_transport_;
|
||||
|
||||
RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// Cache the encrypted header extension IDs for SDES negoitation.
|
||||
absl::optional<std::vector<int>> send_extension_ids_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
absl::optional<std::vector<int>> recv_extension_ids_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// This is invoked when RTCP-mux becomes active and
|
||||
// `rtcp_dtls_transport_` is destroyed. The JsepTransportController will
|
||||
// receive the callback and update the aggregate transport states.
|
||||
std::function<void()> rtcp_mux_active_callback_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_JSEP_TRANSPORT_H_
|
||||
390
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_collection.cc
Normal file
390
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_collection.cc
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Copyright 2021 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 "pc/jsep_transport_collection.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
void BundleManager::Update(const cricket::SessionDescription* description,
|
||||
SdpType type) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// Rollbacks should call Rollback, not Update.
|
||||
RTC_DCHECK(type != SdpType::kRollback);
|
||||
bool bundle_groups_changed = false;
|
||||
// TODO(bugs.webrtc.org/3349): Do this for kPrAnswer as well. To make this
|
||||
// work, we also need to make sure PRANSWERs don't call
|
||||
// MaybeDestroyJsepTransport, because the final answer may need the destroyed
|
||||
// transport if it changes the BUNDLE group.
|
||||
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle ||
|
||||
type == SdpType::kAnswer) {
|
||||
// If our policy is "max-bundle" or this is an answer, update all bundle
|
||||
// groups.
|
||||
bundle_groups_changed = true;
|
||||
bundle_groups_.clear();
|
||||
for (const cricket::ContentGroup* new_bundle_group :
|
||||
description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
|
||||
bundle_groups_.push_back(
|
||||
std::make_unique<cricket::ContentGroup>(*new_bundle_group));
|
||||
RTC_DLOG(LS_VERBOSE) << "Establishing bundle group "
|
||||
<< new_bundle_group->ToString();
|
||||
}
|
||||
} else if (type == SdpType::kOffer) {
|
||||
// If this is an offer, update existing bundle groups.
|
||||
// We do this because as per RFC 8843, section 7.3.2, the answerer cannot
|
||||
// remove an m= section from an existing BUNDLE group without rejecting it.
|
||||
// Thus any m= sections added to a BUNDLE group in this offer can
|
||||
// preemptively start using the bundled transport, as there is no possible
|
||||
// non-bundled fallback.
|
||||
for (const cricket::ContentGroup* new_bundle_group :
|
||||
description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
|
||||
// Attempt to find a matching existing group.
|
||||
for (const std::string& mid : new_bundle_group->content_names()) {
|
||||
auto it = established_bundle_groups_by_mid_.find(mid);
|
||||
if (it != established_bundle_groups_by_mid_.end()) {
|
||||
*it->second = *new_bundle_group;
|
||||
bundle_groups_changed = true;
|
||||
RTC_DLOG(LS_VERBOSE)
|
||||
<< "Establishing bundle group " << new_bundle_group->ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bundle_groups_changed) {
|
||||
RefreshEstablishedBundleGroupsByMid();
|
||||
}
|
||||
}
|
||||
|
||||
const cricket::ContentGroup* BundleManager::LookupGroupByMid(
|
||||
const std::string& mid) const {
|
||||
auto it = established_bundle_groups_by_mid_.find(mid);
|
||||
return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr;
|
||||
}
|
||||
bool BundleManager::IsFirstMidInGroup(const std::string& mid) const {
|
||||
auto group = LookupGroupByMid(mid);
|
||||
if (!group) {
|
||||
return true; // Unbundled MIDs are considered group leaders
|
||||
}
|
||||
return mid == *(group->FirstContentName());
|
||||
}
|
||||
|
||||
cricket::ContentGroup* BundleManager::LookupGroupByMid(const std::string& mid) {
|
||||
auto it = established_bundle_groups_by_mid_.find(mid);
|
||||
return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void BundleManager::DeleteMid(const cricket::ContentGroup* bundle_group,
|
||||
const std::string& mid) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_LOG(LS_VERBOSE) << "Deleting mid " << mid << " from bundle group "
|
||||
<< bundle_group->ToString();
|
||||
// Remove the rejected content from the `bundle_group`.
|
||||
// The const pointer arg is used to identify the group, we verify
|
||||
// it before we use it to make a modification.
|
||||
auto bundle_group_it = std::find_if(
|
||||
bundle_groups_.begin(), bundle_groups_.end(),
|
||||
[bundle_group](std::unique_ptr<cricket::ContentGroup>& group) {
|
||||
return bundle_group == group.get();
|
||||
});
|
||||
RTC_DCHECK(bundle_group_it != bundle_groups_.end());
|
||||
(*bundle_group_it)->RemoveContentName(mid);
|
||||
established_bundle_groups_by_mid_.erase(
|
||||
established_bundle_groups_by_mid_.find(mid));
|
||||
}
|
||||
|
||||
void BundleManager::DeleteGroup(const cricket::ContentGroup* bundle_group) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DLOG(LS_VERBOSE) << "Deleting bundle group " << bundle_group->ToString();
|
||||
|
||||
auto bundle_group_it = std::find_if(
|
||||
bundle_groups_.begin(), bundle_groups_.end(),
|
||||
[bundle_group](std::unique_ptr<cricket::ContentGroup>& group) {
|
||||
return bundle_group == group.get();
|
||||
});
|
||||
RTC_DCHECK(bundle_group_it != bundle_groups_.end());
|
||||
auto mid_list = (*bundle_group_it)->content_names();
|
||||
for (const auto& content_name : mid_list) {
|
||||
DeleteMid(bundle_group, content_name);
|
||||
}
|
||||
bundle_groups_.erase(bundle_group_it);
|
||||
}
|
||||
|
||||
void BundleManager::Rollback() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
bundle_groups_.clear();
|
||||
for (const auto& bundle_group : stable_bundle_groups_) {
|
||||
bundle_groups_.push_back(
|
||||
std::make_unique<cricket::ContentGroup>(*bundle_group));
|
||||
}
|
||||
RefreshEstablishedBundleGroupsByMid();
|
||||
}
|
||||
|
||||
void BundleManager::Commit() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
stable_bundle_groups_.clear();
|
||||
for (const auto& bundle_group : bundle_groups_) {
|
||||
stable_bundle_groups_.push_back(
|
||||
std::make_unique<cricket::ContentGroup>(*bundle_group));
|
||||
}
|
||||
}
|
||||
|
||||
void BundleManager::RefreshEstablishedBundleGroupsByMid() {
|
||||
established_bundle_groups_by_mid_.clear();
|
||||
for (const auto& bundle_group : bundle_groups_) {
|
||||
for (const std::string& content_name : bundle_group->content_names()) {
|
||||
established_bundle_groups_by_mid_[content_name] = bundle_group.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JsepTransportCollection::RegisterTransport(
|
||||
const std::string& mid,
|
||||
std::unique_ptr<cricket::JsepTransport> transport) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
SetTransportForMid(mid, transport.get());
|
||||
jsep_transports_by_name_[mid] = std::move(transport);
|
||||
RTC_DCHECK(IsConsistent());
|
||||
}
|
||||
|
||||
std::vector<cricket::JsepTransport*> JsepTransportCollection::Transports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
std::vector<cricket::JsepTransport*> result;
|
||||
for (auto& kv : jsep_transports_by_name_) {
|
||||
result.push_back(kv.second.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<cricket::JsepTransport*>
|
||||
JsepTransportCollection::ActiveTransports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
std::set<cricket::JsepTransport*> transports;
|
||||
for (const auto& kv : mid_to_transport_) {
|
||||
transports.insert(kv.second);
|
||||
}
|
||||
return std::vector<cricket::JsepTransport*>(transports.begin(),
|
||||
transports.end());
|
||||
}
|
||||
|
||||
void JsepTransportCollection::DestroyAllTransports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
for (const auto& jsep_transport : jsep_transports_by_name_) {
|
||||
map_change_callback_(jsep_transport.first, nullptr);
|
||||
}
|
||||
jsep_transports_by_name_.clear();
|
||||
RTC_DCHECK(IsConsistent());
|
||||
}
|
||||
|
||||
const cricket::JsepTransport* JsepTransportCollection::GetTransportByName(
|
||||
const std::string& transport_name) const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = jsep_transports_by_name_.find(transport_name);
|
||||
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
cricket::JsepTransport* JsepTransportCollection::GetTransportByName(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = jsep_transports_by_name_.find(transport_name);
|
||||
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
|
||||
const std::string& mid) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = mid_to_transport_.find(mid);
|
||||
return it == mid_to_transport_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
|
||||
const std::string& mid) const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = mid_to_transport_.find(mid);
|
||||
return it == mid_to_transport_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
|
||||
absl::string_view mid) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// TODO(hta): should be a better way.
|
||||
auto it = mid_to_transport_.find(std::string(mid));
|
||||
return it == mid_to_transport_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
|
||||
absl::string_view mid) const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// TODO(hta): Should be a better way
|
||||
auto it = mid_to_transport_.find(std::string(mid));
|
||||
return it == mid_to_transport_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
bool JsepTransportCollection::SetTransportForMid(
|
||||
const std::string& mid,
|
||||
cricket::JsepTransport* jsep_transport) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DCHECK(jsep_transport);
|
||||
|
||||
auto it = mid_to_transport_.find(mid);
|
||||
if (it != mid_to_transport_.end() && it->second == jsep_transport)
|
||||
return true;
|
||||
|
||||
// The map_change_callback must be called before destroying the
|
||||
// transport, because it removes references to the transport
|
||||
// in the RTP demuxer.
|
||||
bool result = map_change_callback_(mid, jsep_transport);
|
||||
|
||||
if (it == mid_to_transport_.end()) {
|
||||
mid_to_transport_.insert(std::make_pair(mid, jsep_transport));
|
||||
} else {
|
||||
auto old_transport = it->second;
|
||||
it->second = jsep_transport;
|
||||
MaybeDestroyJsepTransport(old_transport);
|
||||
}
|
||||
RTC_DCHECK(IsConsistent());
|
||||
return result;
|
||||
}
|
||||
|
||||
void JsepTransportCollection::RemoveTransportForMid(const std::string& mid) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DCHECK(IsConsistent());
|
||||
bool ret = map_change_callback_(mid, nullptr);
|
||||
// Calling OnTransportChanged with nullptr should always succeed, since it is
|
||||
// only expected to fail when adding media to a transport (not removing).
|
||||
RTC_DCHECK(ret);
|
||||
|
||||
auto old_transport = GetTransportForMid(mid);
|
||||
if (old_transport) {
|
||||
mid_to_transport_.erase(mid);
|
||||
MaybeDestroyJsepTransport(old_transport);
|
||||
}
|
||||
RTC_DCHECK(IsConsistent());
|
||||
}
|
||||
|
||||
bool JsepTransportCollection::RollbackTransports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
bool ret = true;
|
||||
// First, remove any new mid->transport mappings.
|
||||
for (const auto& kv : mid_to_transport_) {
|
||||
if (stable_mid_to_transport_.count(kv.first) == 0) {
|
||||
ret = ret && map_change_callback_(kv.first, nullptr);
|
||||
}
|
||||
}
|
||||
// Next, restore old mappings.
|
||||
for (const auto& kv : stable_mid_to_transport_) {
|
||||
auto it = mid_to_transport_.find(kv.first);
|
||||
if (it == mid_to_transport_.end() || it->second != kv.second) {
|
||||
ret = ret && map_change_callback_(kv.first, kv.second);
|
||||
}
|
||||
}
|
||||
mid_to_transport_ = stable_mid_to_transport_;
|
||||
// Moving a transport back to mid_to_transport_ means it's now included in
|
||||
// the aggregate state if it wasn't previously.
|
||||
state_change_callback_();
|
||||
DestroyUnusedTransports();
|
||||
RTC_DCHECK(IsConsistent());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JsepTransportCollection::CommitTransports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
stable_mid_to_transport_ = mid_to_transport_;
|
||||
DestroyUnusedTransports();
|
||||
RTC_DCHECK(IsConsistent());
|
||||
}
|
||||
|
||||
bool JsepTransportCollection::TransportInUse(
|
||||
cricket::JsepTransport* jsep_transport) const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
for (const auto& kv : mid_to_transport_) {
|
||||
if (kv.second == jsep_transport) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool JsepTransportCollection::TransportNeededForRollback(
|
||||
cricket::JsepTransport* jsep_transport) const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
for (const auto& kv : stable_mid_to_transport_) {
|
||||
if (kv.second == jsep_transport) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JsepTransportCollection::MaybeDestroyJsepTransport(
|
||||
cricket::JsepTransport* transport) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// Don't destroy the JsepTransport if there are still media sections referring
|
||||
// to it, or if it will be needed in case of rollback.
|
||||
if (TransportInUse(transport)) {
|
||||
return;
|
||||
}
|
||||
// If this transport is needed for rollback, don't destroy it yet, but make
|
||||
// sure the aggregate state is updated since this transport is no longer
|
||||
// included in it.
|
||||
if (TransportNeededForRollback(transport)) {
|
||||
state_change_callback_();
|
||||
return;
|
||||
}
|
||||
for (const auto& it : jsep_transports_by_name_) {
|
||||
if (it.second.get() == transport) {
|
||||
jsep_transports_by_name_.erase(it.first);
|
||||
state_change_callback_();
|
||||
break;
|
||||
}
|
||||
}
|
||||
RTC_DCHECK(IsConsistent());
|
||||
}
|
||||
|
||||
void JsepTransportCollection::DestroyUnusedTransports() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
bool need_state_change_callback = false;
|
||||
auto it = jsep_transports_by_name_.begin();
|
||||
while (it != jsep_transports_by_name_.end()) {
|
||||
if (TransportInUse(it->second.get()) ||
|
||||
TransportNeededForRollback(it->second.get())) {
|
||||
++it;
|
||||
} else {
|
||||
it = jsep_transports_by_name_.erase(it);
|
||||
need_state_change_callback = true;
|
||||
}
|
||||
}
|
||||
if (need_state_change_callback) {
|
||||
state_change_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
bool JsepTransportCollection::IsConsistent() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
for (const auto& it : jsep_transports_by_name_) {
|
||||
if (!TransportInUse(it.second.get()) &&
|
||||
!TransportNeededForRollback(it.second.get())) {
|
||||
RTC_LOG(LS_ERROR) << "Transport registered with mid " << it.first
|
||||
<< " is not in use, transport " << it.second.get();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
173
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_collection.h
Normal file
173
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_collection.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JSEP_TRANSPORT_COLLECTION_H_
|
||||
#define PC_JSEP_TRANSPORT_COLLECTION_H_
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/jsep.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "pc/jsep_transport.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This class manages information about RFC 8843 BUNDLE bundles
|
||||
// in SDP descriptions.
|
||||
|
||||
// This is a work-in-progress. Planned steps:
|
||||
// 1) Move all Bundle-related data structures from JsepTransport
|
||||
// into this class.
|
||||
// 2) Move all Bundle-related functions into this class.
|
||||
// 3) Move remaining Bundle-related logic into this class.
|
||||
// Make data members private.
|
||||
// 4) Refine interface to have comprehensible semantics.
|
||||
// 5) Add unit tests.
|
||||
// 6) Change the logic to do what's right.
|
||||
class BundleManager {
|
||||
public:
|
||||
explicit BundleManager(PeerConnectionInterface::BundlePolicy bundle_policy)
|
||||
: bundle_policy_(bundle_policy) {}
|
||||
const std::vector<std::unique_ptr<cricket::ContentGroup>>& bundle_groups()
|
||||
const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
return bundle_groups_;
|
||||
}
|
||||
// Lookup a bundle group by a member mid name.
|
||||
const cricket::ContentGroup* LookupGroupByMid(const std::string& mid) const;
|
||||
cricket::ContentGroup* LookupGroupByMid(const std::string& mid);
|
||||
// Returns true if the MID is the first item of a group, or if
|
||||
// the MID is not a member of a group.
|
||||
bool IsFirstMidInGroup(const std::string& mid) const;
|
||||
// Update the groups description. This completely replaces the group
|
||||
// description with the one from the SessionDescription.
|
||||
void Update(const cricket::SessionDescription* description, SdpType type);
|
||||
// Delete a MID from the group that contains it.
|
||||
void DeleteMid(const cricket::ContentGroup* bundle_group,
|
||||
const std::string& mid);
|
||||
// Delete a group.
|
||||
void DeleteGroup(const cricket::ContentGroup* bundle_group);
|
||||
// Roll back to previous stable state.
|
||||
void Rollback();
|
||||
// Commit current bundle groups.
|
||||
void Commit();
|
||||
|
||||
private:
|
||||
// Recalculate established_bundle_groups_by_mid_ from bundle_groups_.
|
||||
void RefreshEstablishedBundleGroupsByMid() RTC_RUN_ON(sequence_checker_);
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_{
|
||||
SequenceChecker::kDetached};
|
||||
PeerConnectionInterface::BundlePolicy bundle_policy_;
|
||||
std::vector<std::unique_ptr<cricket::ContentGroup>> bundle_groups_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
std::vector<std::unique_ptr<cricket::ContentGroup>> stable_bundle_groups_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
std::map<std::string, cricket::ContentGroup*>
|
||||
established_bundle_groups_by_mid_;
|
||||
};
|
||||
|
||||
// This class keeps the mapping of MIDs to transports.
|
||||
// It is pulled out here because a lot of the code that deals with
|
||||
// bundles end up modifying this map, and the two need to be consistent;
|
||||
// the managers may merge.
|
||||
class JsepTransportCollection {
|
||||
public:
|
||||
JsepTransportCollection(std::function<bool(const std::string& mid,
|
||||
cricket::JsepTransport* transport)>
|
||||
map_change_callback,
|
||||
std::function<void()> state_change_callback)
|
||||
: map_change_callback_(map_change_callback),
|
||||
state_change_callback_(state_change_callback) {}
|
||||
|
||||
void RegisterTransport(const std::string& mid,
|
||||
std::unique_ptr<cricket::JsepTransport> transport);
|
||||
// Returns all transports, including those not currently mapped to any MID
|
||||
// because they're being kept alive in case of rollback.
|
||||
std::vector<cricket::JsepTransport*> Transports();
|
||||
// Only returns transports currently mapped to a MID.
|
||||
std::vector<cricket::JsepTransport*> ActiveTransports();
|
||||
void DestroyAllTransports();
|
||||
// Lookup a JsepTransport by the MID that was used to register it.
|
||||
cricket::JsepTransport* GetTransportByName(const std::string& mid);
|
||||
const cricket::JsepTransport* GetTransportByName(
|
||||
const std::string& mid) const;
|
||||
// Lookup a JsepTransport by any MID that refers to it.
|
||||
cricket::JsepTransport* GetTransportForMid(const std::string& mid);
|
||||
const cricket::JsepTransport* GetTransportForMid(
|
||||
const std::string& mid) const;
|
||||
cricket::JsepTransport* GetTransportForMid(absl::string_view mid);
|
||||
const cricket::JsepTransport* GetTransportForMid(absl::string_view mid) const;
|
||||
// Set transport for a MID. This may destroy a transport if it is no
|
||||
// longer in use.
|
||||
bool SetTransportForMid(const std::string& mid,
|
||||
cricket::JsepTransport* jsep_transport);
|
||||
// Remove a transport for a MID. This may destroy a transport if it is
|
||||
// no longer in use.
|
||||
void RemoveTransportForMid(const std::string& mid);
|
||||
// Roll back to previous stable mid-to-transport mappings.
|
||||
bool RollbackTransports();
|
||||
// Commit pending mid-transport mappings (rollback is no longer possible),
|
||||
// and destroy unused transports because we know now we'll never need them
|
||||
// again.
|
||||
void CommitTransports();
|
||||
|
||||
private:
|
||||
// Returns true if any mid currently maps to this transport.
|
||||
bool TransportInUse(cricket::JsepTransport* jsep_transport) const;
|
||||
|
||||
// Returns true if any mid in the last stable mapping maps to this transport,
|
||||
// meaning it should be kept alive in case of rollback.
|
||||
bool TransportNeededForRollback(cricket::JsepTransport* jsep_transport) const;
|
||||
|
||||
// Destroy a transport if it's no longer in use. This includes whether it
|
||||
// will be needed in case of rollback.
|
||||
void MaybeDestroyJsepTransport(cricket::JsepTransport* transport);
|
||||
|
||||
// Destroys all transports that are no longer in use.
|
||||
void DestroyUnusedTransports();
|
||||
|
||||
bool IsConsistent(); // For testing only: Verify internal structure.
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_{
|
||||
SequenceChecker::kDetached};
|
||||
// This member owns the JSEP transports.
|
||||
std::map<std::string, std::unique_ptr<cricket::JsepTransport>>
|
||||
jsep_transports_by_name_ RTC_GUARDED_BY(sequence_checker_);
|
||||
|
||||
// This keeps track of the mapping between media section
|
||||
// (BaseChannel/SctpTransport) and the JsepTransport underneath.
|
||||
std::map<std::string, cricket::JsepTransport*> mid_to_transport_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
// A snapshot of mid_to_transport_ at the last stable state. Used for
|
||||
// rollback.
|
||||
std::map<std::string, cricket::JsepTransport*> stable_mid_to_transport_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
// Callback used to inform subscribers of altered transports.
|
||||
const std::function<bool(const std::string& mid,
|
||||
cricket::JsepTransport* transport)>
|
||||
map_change_callback_;
|
||||
// Callback used to inform subscribers of possibly altered state.
|
||||
const std::function<void()> state_change_callback_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_JSEP_TRANSPORT_COLLECTION_H_
|
||||
1465
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_controller.cc
Normal file
1465
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_controller.cc
Normal file
File diff suppressed because it is too large
Load diff
518
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_controller.h
Normal file
518
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_controller.h
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_JSEP_TRANSPORT_CONTROLLER_H_
|
||||
#define PC_JSEP_TRANSPORT_CONTROLLER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/ice_transport_factory.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/data_channel_transport_interface.h"
|
||||
#include "api/transport/sctp_transport_factory_interface.h"
|
||||
#include "media/sctp/sctp_transport_internal.h"
|
||||
#include "p2p/base/dtls_transport.h"
|
||||
#include "p2p/base/dtls_transport_factory.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/p2p_transport_channel.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/dtls_srtp_transport.h"
|
||||
#include "pc/dtls_transport.h"
|
||||
#include "pc/jsep_transport.h"
|
||||
#include "pc/jsep_transport_collection.h"
|
||||
#include "pc/rtp_transport.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/sctp_transport.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/srtp_transport.h"
|
||||
#include "pc/transport_stats.h"
|
||||
#include "rtc_base/callback_list.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace rtc {
|
||||
class Thread;
|
||||
class PacketTransportInternal;
|
||||
} // namespace rtc
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class JsepTransportController : public sigslot::has_slots<> {
|
||||
public:
|
||||
// Used when the RtpTransport/DtlsTransport of the m= section is changed
|
||||
// because the section is rejected or BUNDLE is enabled.
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() {}
|
||||
|
||||
// Returns true if media associated with `mid` was successfully set up to be
|
||||
// demultiplexed on `rtp_transport`. Could return false if two bundled m=
|
||||
// sections use the same SSRC, for example.
|
||||
//
|
||||
// If a data channel transport must be negotiated, `data_channel_transport`
|
||||
// and `negotiation_state` indicate negotiation status. If
|
||||
// `data_channel_transport` is null, the data channel transport should not
|
||||
// be used. Otherwise, the value is a pointer to the transport to be used
|
||||
// for data channels on `mid`, if any.
|
||||
//
|
||||
// The observer should not send data on `data_channel_transport` until
|
||||
// `negotiation_state` is provisional or final. It should not delete
|
||||
// `data_channel_transport` or any fallback transport until
|
||||
// `negotiation_state` is final.
|
||||
virtual bool OnTransportChanged(
|
||||
const std::string& mid,
|
||||
RtpTransportInternal* rtp_transport,
|
||||
rtc::scoped_refptr<DtlsTransport> dtls_transport,
|
||||
DataChannelTransportInterface* data_channel_transport) = 0;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
// If `redetermine_role_on_ice_restart` is true, ICE role is redetermined
|
||||
// upon setting a local transport description that indicates an ICE
|
||||
// restart.
|
||||
bool redetermine_role_on_ice_restart = true;
|
||||
rtc::SSLProtocolVersion ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
// `crypto_options` is used to determine if created DTLS transports
|
||||
// negotiate GCM crypto suites or not.
|
||||
CryptoOptions crypto_options;
|
||||
PeerConnectionInterface::BundlePolicy bundle_policy =
|
||||
PeerConnectionInterface::kBundlePolicyBalanced;
|
||||
PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy =
|
||||
PeerConnectionInterface::kRtcpMuxPolicyRequire;
|
||||
bool disable_encryption = false;
|
||||
bool enable_external_auth = false;
|
||||
// Used to inject the ICE/DTLS transports created externally.
|
||||
IceTransportFactory* ice_transport_factory = nullptr;
|
||||
cricket::DtlsTransportFactory* dtls_transport_factory = nullptr;
|
||||
Observer* transport_observer = nullptr;
|
||||
// Must be provided and valid for the lifetime of the
|
||||
// JsepTransportController instance.
|
||||
absl::AnyInvocable<void(const rtc::CopyOnWriteBuffer& packet,
|
||||
int64_t packet_time_us) const>
|
||||
rtcp_handler;
|
||||
absl::AnyInvocable<void(const RtpPacketReceived& parsed_packet) const>
|
||||
un_demuxable_packet_handler;
|
||||
// Initial value for whether DtlsTransport reset causes a reset
|
||||
// of SRTP parameters.
|
||||
bool active_reset_srtp_params = false;
|
||||
RtcEventLog* event_log = nullptr;
|
||||
|
||||
// Factory for SCTP transports.
|
||||
SctpTransportFactoryInterface* sctp_factory = nullptr;
|
||||
std::function<void(rtc::SSLHandshakeError)> on_dtls_handshake_error_;
|
||||
|
||||
// Field trials.
|
||||
const FieldTrialsView* field_trials;
|
||||
};
|
||||
|
||||
// The ICE related events are fired on the `network_thread`.
|
||||
// All the transport related methods are called on the `network_thread`
|
||||
// and destruction of the JsepTransportController must occur on the
|
||||
// `network_thread`.
|
||||
JsepTransportController(
|
||||
rtc::Thread* network_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
AsyncDnsResolverFactoryInterface* async_dns_resolver_factory,
|
||||
Config config);
|
||||
virtual ~JsepTransportController();
|
||||
|
||||
JsepTransportController(const JsepTransportController&) = delete;
|
||||
JsepTransportController& operator=(const JsepTransportController&) = delete;
|
||||
|
||||
// The main method to be called; applies a description at the transport
|
||||
// level, creating/destroying transport objects as needed and updating their
|
||||
// properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not
|
||||
// yet? May make sense to in the future.
|
||||
//
|
||||
// `local_desc` must always be valid. If a remote description has previously
|
||||
// been set via a call to `SetRemoteDescription()` then `remote_desc` should
|
||||
// point to that description object in order to keep the current local and
|
||||
// remote session descriptions in sync.
|
||||
RTCError SetLocalDescription(SdpType type,
|
||||
const cricket::SessionDescription* local_desc,
|
||||
const cricket::SessionDescription* remote_desc);
|
||||
|
||||
// Call to apply a remote description (See `SetLocalDescription()` for local).
|
||||
//
|
||||
// `remote_desc` must always be valid. If a local description has previously
|
||||
// been set via a call to `SetLocalDescription()` then `local_desc` should
|
||||
// point to that description object in order to keep the current local and
|
||||
// remote session descriptions in sync.
|
||||
RTCError SetRemoteDescription(SdpType type,
|
||||
const cricket::SessionDescription* local_desc,
|
||||
const cricket::SessionDescription* remote_desc);
|
||||
|
||||
// Get transports to be used for the provided `mid`. If bundling is enabled,
|
||||
// calling GetRtpTransport for multiple MIDs may yield the same object.
|
||||
RtpTransportInternal* GetRtpTransport(absl::string_view mid) const;
|
||||
cricket::DtlsTransportInternal* GetDtlsTransport(const std::string& mid);
|
||||
const cricket::DtlsTransportInternal* GetRtcpDtlsTransport(
|
||||
const std::string& mid) const;
|
||||
// Gets the externally sharable version of the DtlsTransport.
|
||||
rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMid(
|
||||
const std::string& mid);
|
||||
rtc::scoped_refptr<SctpTransport> GetSctpTransport(
|
||||
const std::string& mid) const;
|
||||
|
||||
DataChannelTransportInterface* GetDataChannelTransport(
|
||||
const std::string& mid) const;
|
||||
|
||||
/*********************
|
||||
* ICE-related methods
|
||||
********************/
|
||||
// This method is public to allow PeerConnection to update it from
|
||||
// SetConfiguration.
|
||||
void SetIceConfig(const cricket::IceConfig& config);
|
||||
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
|
||||
// set, offers should generate new ufrags/passwords until an ICE restart
|
||||
// occurs.
|
||||
void SetNeedsIceRestartFlag();
|
||||
// Returns true if the ICE restart flag above was set, and no ICE restart has
|
||||
// occurred yet for this transport (by applying a local description with
|
||||
// changed ufrag/password). If the transport has been deleted as a result of
|
||||
// bundling, returns false.
|
||||
bool NeedsIceRestart(const std::string& mid) const;
|
||||
// Start gathering candidates for any new transports, or transports doing an
|
||||
// ICE restart.
|
||||
void MaybeStartGathering();
|
||||
RTCError AddRemoteCandidates(
|
||||
const std::string& mid,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
RTCError RemoveRemoteCandidates(
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
|
||||
/**********************
|
||||
* DTLS-related methods
|
||||
*********************/
|
||||
// Specifies the identity to use in this session.
|
||||
// Can only be called once.
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate(
|
||||
const std::string& mid) const;
|
||||
// Caller owns returned certificate chain. This method mainly exists for
|
||||
// stats reporting.
|
||||
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain(
|
||||
const std::string& mid) const;
|
||||
// Get negotiated role, if one has been negotiated.
|
||||
absl::optional<rtc::SSLRole> GetDtlsRole(const std::string& mid) const;
|
||||
|
||||
// TODO(deadbeef): GetStats isn't const because all the way down to
|
||||
// OpenSSLStreamAdapter, GetSslCipherSuite and GetDtlsSrtpCryptoSuite are not
|
||||
// const. Fix this.
|
||||
bool GetStats(const std::string& mid, cricket::TransportStats* stats);
|
||||
|
||||
bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; }
|
||||
|
||||
void SetActiveResetSrtpParams(bool active_reset_srtp_params);
|
||||
|
||||
RTCError RollbackTransports();
|
||||
|
||||
// F: void(const std::string&, const std::vector<cricket::Candidate>&)
|
||||
template <typename F>
|
||||
void SubscribeIceCandidateGathered(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_candidates_gathered_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(cricket::IceConnectionState)
|
||||
template <typename F>
|
||||
void SubscribeIceConnectionState(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_connection_state_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(PeerConnectionInterface::PeerConnectionState)
|
||||
template <typename F>
|
||||
void SubscribeConnectionState(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_connection_state_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(PeerConnectionInterface::IceConnectionState)
|
||||
template <typename F>
|
||||
void SubscribeStandardizedIceConnectionState(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_standardized_ice_connection_state_.AddReceiver(
|
||||
std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(cricket::IceGatheringState)
|
||||
template <typename F>
|
||||
void SubscribeIceGatheringState(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_gathering_state_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(const cricket::IceCandidateErrorEvent&)
|
||||
template <typename F>
|
||||
void SubscribeIceCandidateError(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_candidate_error_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(const std::vector<cricket::Candidate>&)
|
||||
template <typename F>
|
||||
void SubscribeIceCandidatesRemoved(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_candidates_removed_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
// F: void(const cricket::CandidatePairChangeEvent&)
|
||||
template <typename F>
|
||||
void SubscribeIceCandidatePairChanged(F&& callback) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
signal_ice_candidate_pair_changed_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
private:
|
||||
// All of these callbacks are fired on the network thread.
|
||||
|
||||
// If any transport failed => failed,
|
||||
// Else if all completed => completed,
|
||||
// Else if all connected => connected,
|
||||
// Else => connecting
|
||||
CallbackList<cricket::IceConnectionState> signal_ice_connection_state_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
CallbackList<PeerConnectionInterface::PeerConnectionState>
|
||||
signal_connection_state_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
CallbackList<PeerConnectionInterface::IceConnectionState>
|
||||
signal_standardized_ice_connection_state_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// If all transports done gathering => complete,
|
||||
// Else if any are gathering => gathering,
|
||||
// Else => new
|
||||
CallbackList<cricket::IceGatheringState> signal_ice_gathering_state_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// [mid, candidates]
|
||||
CallbackList<const std::string&, const std::vector<cricket::Candidate>&>
|
||||
signal_ice_candidates_gathered_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
CallbackList<const cricket::IceCandidateErrorEvent&>
|
||||
signal_ice_candidate_error_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
CallbackList<const std::vector<cricket::Candidate>&>
|
||||
signal_ice_candidates_removed_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
CallbackList<const cricket::CandidatePairChangeEvent&>
|
||||
signal_ice_candidate_pair_changed_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// Called from SetLocalDescription and SetRemoteDescription.
|
||||
// When `local` is true, local_desc must be valid. Similarly when
|
||||
// `local` is false, remote_desc must be valid. The description counterpart
|
||||
// to the one that's being applied, may be nullptr but when it's supplied
|
||||
// the counterpart description's content groups will be kept up to date for
|
||||
// `type == SdpType::kAnswer`.
|
||||
RTCError ApplyDescription_n(bool local,
|
||||
SdpType type,
|
||||
const cricket::SessionDescription* local_desc,
|
||||
const cricket::SessionDescription* remote_desc)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
RTCError ValidateAndMaybeUpdateBundleGroups(
|
||||
bool local,
|
||||
SdpType type,
|
||||
const cricket::SessionDescription* local_desc,
|
||||
const cricket::SessionDescription* remote_desc)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
RTCError ValidateContent(const cricket::ContentInfo& content_info);
|
||||
|
||||
void HandleRejectedContent(const cricket::ContentInfo& content_info)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
bool HandleBundledContent(const cricket::ContentInfo& content_info,
|
||||
const cricket::ContentGroup& bundle_group)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
cricket::JsepTransportDescription CreateJsepTransportDescription(
|
||||
const cricket::ContentInfo& content_info,
|
||||
const cricket::TransportInfo& transport_info,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
int rtp_abs_sendtime_extn_id);
|
||||
|
||||
std::map<const cricket::ContentGroup*, std::vector<int>>
|
||||
MergeEncryptedHeaderExtensionIdsForBundles(
|
||||
const cricket::SessionDescription* description);
|
||||
std::vector<int> GetEncryptedHeaderExtensionIds(
|
||||
const cricket::ContentInfo& content_info);
|
||||
|
||||
int GetRtpAbsSendTimeHeaderExtensionId(
|
||||
const cricket::ContentInfo& content_info);
|
||||
|
||||
// This method takes the BUNDLE group into account. If the JsepTransport is
|
||||
// destroyed because of BUNDLE, it would return the transport which other
|
||||
// transports are bundled on (In current implementation, it is the first
|
||||
// content in the BUNDLE group).
|
||||
const cricket::JsepTransport* GetJsepTransportForMid(
|
||||
const std::string& mid) const RTC_RUN_ON(network_thread_);
|
||||
cricket::JsepTransport* GetJsepTransportForMid(const std::string& mid)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
const cricket::JsepTransport* GetJsepTransportForMid(
|
||||
absl::string_view mid) const RTC_RUN_ON(network_thread_);
|
||||
cricket::JsepTransport* GetJsepTransportForMid(absl::string_view mid)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
// Get the JsepTransport without considering the BUNDLE group. Return nullptr
|
||||
// if the JsepTransport is destroyed.
|
||||
const cricket::JsepTransport* GetJsepTransportByName(
|
||||
const std::string& transport_name) const RTC_RUN_ON(network_thread_);
|
||||
cricket::JsepTransport* GetJsepTransportByName(
|
||||
const std::string& transport_name) RTC_RUN_ON(network_thread_);
|
||||
|
||||
// Creates jsep transport. Noop if transport is already created.
|
||||
// Transport is created either during SetLocalDescription (`local` == true) or
|
||||
// during SetRemoteDescription (`local` == false). Passing `local` helps to
|
||||
// differentiate initiator (caller) from answerer (callee).
|
||||
RTCError MaybeCreateJsepTransport(
|
||||
bool local,
|
||||
const cricket::ContentInfo& content_info,
|
||||
const cricket::SessionDescription& description)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
void DestroyAllJsepTransports_n() RTC_RUN_ON(network_thread_);
|
||||
|
||||
void SetIceRole_n(cricket::IceRole ice_role) RTC_RUN_ON(network_thread_);
|
||||
|
||||
cricket::IceRole DetermineIceRole(
|
||||
cricket::JsepTransport* jsep_transport,
|
||||
const cricket::TransportInfo& transport_info,
|
||||
SdpType type,
|
||||
bool local);
|
||||
|
||||
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
|
||||
const cricket::ContentInfo& content_info,
|
||||
cricket::IceTransportInternal* ice);
|
||||
rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
|
||||
const std::string& transport_name,
|
||||
bool rtcp);
|
||||
|
||||
std::unique_ptr<RtpTransport> CreateUnencryptedRtpTransport(
|
||||
const std::string& transport_name,
|
||||
rtc::PacketTransportInternal* rtp_packet_transport,
|
||||
rtc::PacketTransportInternal* rtcp_packet_transport);
|
||||
std::unique_ptr<SrtpTransport> CreateSdesTransport(
|
||||
const std::string& transport_name,
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport,
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport);
|
||||
std::unique_ptr<DtlsSrtpTransport> CreateDtlsSrtpTransport(
|
||||
const std::string& transport_name,
|
||||
cricket::DtlsTransportInternal* rtp_dtls_transport,
|
||||
cricket::DtlsTransportInternal* rtcp_dtls_transport);
|
||||
|
||||
// Collect all the DtlsTransports, including RTP and RTCP, from the
|
||||
// JsepTransports, including those not mapped to a MID because they are being
|
||||
// kept alive in case of rollback.
|
||||
std::vector<cricket::DtlsTransportInternal*> GetDtlsTransports();
|
||||
// Same as the above, but doesn't include rollback transports.
|
||||
// JsepTransportController can iterate all the DtlsTransports and update the
|
||||
// aggregate states.
|
||||
std::vector<cricket::DtlsTransportInternal*> GetActiveDtlsTransports();
|
||||
|
||||
// Handlers for signals from Transport.
|
||||
void OnTransportWritableState_n(rtc::PacketTransportInternal* transport)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportReceivingState_n(rtc::PacketTransportInternal* transport)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportGatheringState_n(cricket::IceTransportInternal* transport)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportCandidateGathered_n(cricket::IceTransportInternal* transport,
|
||||
const cricket::Candidate& candidate)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportCandidateError_n(cricket::IceTransportInternal* transport,
|
||||
const cricket::IceCandidateErrorEvent& event)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportCandidatesRemoved_n(cricket::IceTransportInternal* transport,
|
||||
const cricket::Candidates& candidates)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportRoleConflict_n(cricket::IceTransportInternal* transport)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportStateChanged_n(cricket::IceTransportInternal* transport)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnTransportCandidatePairChanged_n(
|
||||
const cricket::CandidatePairChangeEvent& event)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void UpdateAggregateStates_n() RTC_RUN_ON(network_thread_);
|
||||
|
||||
void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer* packet,
|
||||
int64_t packet_time_us)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnUnDemuxableRtpPacketReceived_n(const RtpPacketReceived& packet)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
|
||||
|
||||
bool OnTransportChanged(const std::string& mid,
|
||||
cricket::JsepTransport* transport);
|
||||
|
||||
rtc::Thread* const network_thread_ = nullptr;
|
||||
cricket::PortAllocator* const port_allocator_ = nullptr;
|
||||
AsyncDnsResolverFactoryInterface* const async_dns_resolver_factory_ = nullptr;
|
||||
|
||||
JsepTransportCollection transports_ RTC_GUARDED_BY(network_thread_);
|
||||
// Aggregate states for Transports.
|
||||
// standardized_ice_connection_state_ is intended to replace
|
||||
// ice_connection_state, see bugs.webrtc.org/9308
|
||||
cricket::IceConnectionState ice_connection_state_ =
|
||||
cricket::kIceConnectionConnecting;
|
||||
PeerConnectionInterface::IceConnectionState
|
||||
standardized_ice_connection_state_ =
|
||||
PeerConnectionInterface::kIceConnectionNew;
|
||||
PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
|
||||
PeerConnectionInterface::PeerConnectionState::kNew;
|
||||
cricket::IceGatheringState ice_gathering_state_ = cricket::kIceGatheringNew;
|
||||
|
||||
const Config config_;
|
||||
bool active_reset_srtp_params_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
absl::optional<bool> initial_offerer_;
|
||||
|
||||
cricket::IceConfig ice_config_;
|
||||
cricket::IceRole ice_role_ = cricket::ICEROLE_CONTROLLING;
|
||||
uint64_t ice_tiebreaker_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
|
||||
BundleManager bundles_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_JSEP_TRANSPORT_CONTROLLER_H_
|
||||
File diff suppressed because it is too large
Load diff
1267
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_unittest.cc
Normal file
1267
TMessagesProj/jni/voip/webrtc/pc/jsep_transport_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
1409
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector.cc
Normal file
1409
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector.cc
Normal file
File diff suppressed because it is too large
Load diff
219
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector.h
Normal file
219
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector.h
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
// This file contains a class used for gathering statistics from an ongoing
|
||||
// libjingle PeerConnection.
|
||||
|
||||
#ifndef PC_LEGACY_STATS_COLLECTOR_H_
|
||||
#define PC_LEGACY_STATS_COLLECTOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/legacy_stats_types.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "p2p/base/connection_info.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "pc/legacy_stats_collector_interface.h"
|
||||
#include "pc/peer_connection_internal.h"
|
||||
#include "pc/rtp_transceiver.h"
|
||||
#include "pc/transport_stats.h"
|
||||
#include "rtc_base/network_constants.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Conversion function to convert candidate type string to the corresponding one
|
||||
// from enum RTCStatsIceCandidateType.
|
||||
const char* IceCandidateTypeToStatsType(const cricket::Candidate& candidate);
|
||||
|
||||
// Conversion function to convert adapter type to report string which are more
|
||||
// fitting to the general style of http://w3c.github.io/webrtc-stats. This is
|
||||
// only used by stats collector.
|
||||
const char* AdapterTypeToStatsType(rtc::AdapterType type);
|
||||
|
||||
// A mapping between track ids and their StatsReport.
|
||||
typedef std::map<std::string, StatsReport*> TrackIdMap;
|
||||
|
||||
class LegacyStatsCollector : public LegacyStatsCollectorInterface {
|
||||
public:
|
||||
// The caller is responsible for ensuring that the pc outlives the
|
||||
// LegacyStatsCollector instance.
|
||||
explicit LegacyStatsCollector(PeerConnectionInternal* pc);
|
||||
virtual ~LegacyStatsCollector();
|
||||
|
||||
// Adds a MediaStream with tracks that can be used as a `selector` in a call
|
||||
// to GetStats.
|
||||
void AddStream(MediaStreamInterface* stream);
|
||||
void AddTrack(MediaStreamTrackInterface* track);
|
||||
|
||||
// Adds a local audio track that is used for getting some voice statistics.
|
||||
void AddLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
uint32_t ssrc) override;
|
||||
|
||||
// Removes a local audio tracks that is used for getting some voice
|
||||
// statistics.
|
||||
void RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
uint32_t ssrc) override;
|
||||
|
||||
// Gather statistics from the session and store them for future use.
|
||||
void UpdateStats(PeerConnectionInterface::StatsOutputLevel level);
|
||||
|
||||
// Gets a StatsReports of the last collected stats. Note that UpdateStats must
|
||||
// be called before this function to get the most recent stats. `selector` is
|
||||
// a track label or empty string. The most recent reports are stored in
|
||||
// `reports`.
|
||||
// TODO(tommi): Change this contract to accept a callback object instead
|
||||
// of filling in `reports`. As is, there's a requirement that the caller
|
||||
// uses `reports` immediately without allowing any async activity on
|
||||
// the thread (message handling etc) and then discard the results.
|
||||
void GetStats(MediaStreamTrackInterface* track,
|
||||
StatsReports* reports) override;
|
||||
|
||||
// Prepare a local or remote SSRC report for the given ssrc. Used internally
|
||||
// in the ExtractStatsFromList template.
|
||||
StatsReport* PrepareReport(bool local,
|
||||
uint32_t ssrc,
|
||||
const std::string& track_id,
|
||||
const StatsReport::Id& transport_id,
|
||||
StatsReport::Direction direction);
|
||||
|
||||
StatsReport* PrepareADMReport();
|
||||
|
||||
// A track is invalid if there is no report data for it.
|
||||
bool IsValidTrack(const std::string& track_id);
|
||||
|
||||
// Reset the internal cache timestamp to force an update of the stats next
|
||||
// time UpdateStats() is called. This call needs to be made on the signaling
|
||||
// thread and should be made every time configuration changes that affect
|
||||
// stats have been made.
|
||||
void InvalidateCache();
|
||||
|
||||
bool UseStandardBytesStats() const { return use_standard_bytes_stats_; }
|
||||
|
||||
private:
|
||||
friend class LegacyStatsCollectorTest;
|
||||
|
||||
// Struct that's populated on the network thread and carries the values to
|
||||
// the signaling thread where the stats are added to the stats reports.
|
||||
struct TransportStats {
|
||||
TransportStats() = default;
|
||||
TransportStats(std::string transport_name,
|
||||
cricket::TransportStats transport_stats)
|
||||
: name(std::move(transport_name)), stats(std::move(transport_stats)) {}
|
||||
TransportStats(TransportStats&&) = default;
|
||||
TransportStats(const TransportStats&) = delete;
|
||||
|
||||
std::string name;
|
||||
cricket::TransportStats stats;
|
||||
std::unique_ptr<rtc::SSLCertificateStats> local_cert_stats;
|
||||
std::unique_ptr<rtc::SSLCertificateStats> remote_cert_stats;
|
||||
};
|
||||
|
||||
struct SessionStats {
|
||||
SessionStats() = default;
|
||||
SessionStats(SessionStats&&) = default;
|
||||
SessionStats(const SessionStats&) = delete;
|
||||
|
||||
SessionStats& operator=(SessionStats&&) = default;
|
||||
SessionStats& operator=(SessionStats&) = delete;
|
||||
|
||||
cricket::CandidateStatsList candidate_stats;
|
||||
std::vector<TransportStats> transport_stats;
|
||||
std::map<std::string, std::string> transport_names_by_mid;
|
||||
};
|
||||
|
||||
// Overridden in unit tests to fake timing.
|
||||
virtual double GetTimeNow();
|
||||
|
||||
bool CopySelectedReports(const std::string& selector, StatsReports* reports);
|
||||
|
||||
// Helper method for creating IceCandidate report. `is_local` indicates
|
||||
// whether this candidate is local or remote.
|
||||
StatsReport* AddCandidateReport(
|
||||
const cricket::CandidateStats& candidate_stats,
|
||||
bool local);
|
||||
|
||||
// Adds a report for this certificate and every certificate in its chain, and
|
||||
// returns the leaf certificate's report (`cert_stats`'s report).
|
||||
StatsReport* AddCertificateReports(
|
||||
std::unique_ptr<rtc::SSLCertificateStats> cert_stats);
|
||||
|
||||
StatsReport* AddConnectionInfoReport(const std::string& content_name,
|
||||
int component,
|
||||
int connection_id,
|
||||
const StatsReport::Id& channel_report_id,
|
||||
const cricket::ConnectionInfo& info);
|
||||
|
||||
void ExtractDataInfo_n(StatsCollection* reports);
|
||||
|
||||
// Returns the `transport_names_by_mid` member from the SessionStats as
|
||||
// gathered and used to populate the stats. Contains one synchronous hop
|
||||
// to the network thread to get this information along with querying data
|
||||
// channel stats at the same time and populating `reports_`.
|
||||
std::map<std::string, std::string> ExtractSessionAndDataInfo();
|
||||
|
||||
void ExtractBweInfo();
|
||||
void ExtractMediaInfo(
|
||||
const std::map<std::string, std::string>& transport_names_by_mid);
|
||||
void ExtractSenderInfo();
|
||||
StatsReport* GetReport(const StatsReport::StatsType& type,
|
||||
const std::string& id,
|
||||
StatsReport::Direction direction);
|
||||
|
||||
// Helper method to get stats from the local audio tracks.
|
||||
void UpdateStatsFromExistingLocalAudioTracks(bool has_remote_tracks);
|
||||
void UpdateReportFromAudioTrack(AudioTrackInterface* track,
|
||||
StatsReport* report,
|
||||
bool has_remote_tracks);
|
||||
|
||||
// Helper method to update the timestamp of track records.
|
||||
void UpdateTrackReports();
|
||||
|
||||
SessionStats ExtractSessionInfo_n(
|
||||
const std::vector<rtc::scoped_refptr<
|
||||
RtpTransceiverProxyWithInternal<RtpTransceiver>>>& transceivers,
|
||||
absl::optional<std::string> sctp_transport_name,
|
||||
absl::optional<std::string> sctp_mid);
|
||||
void ExtractSessionInfo_s(SessionStats& session_stats);
|
||||
|
||||
// A collection for all of our stats reports.
|
||||
StatsCollection reports_;
|
||||
TrackIdMap track_ids_;
|
||||
// Raw pointer to the peer connection the statistics are gathered from.
|
||||
PeerConnectionInternal* const pc_;
|
||||
int64_t cache_timestamp_ms_ RTC_GUARDED_BY(pc_->signaling_thread()) = 0;
|
||||
double stats_gathering_started_;
|
||||
const bool use_standard_bytes_stats_;
|
||||
|
||||
// TODO(tommi): We appear to be holding on to raw pointers to reference
|
||||
// counted objects? We should be using scoped_refptr here.
|
||||
typedef std::vector<std::pair<AudioTrackInterface*, uint32_t>>
|
||||
LocalAudioTrackVector;
|
||||
LocalAudioTrackVector local_audio_tracks_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_LEGACY_STATS_COLLECTOR_H_
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
// This file contains an interface for the (obsolete) StatsCollector class that
|
||||
// is used by compilation units that do not wish to depend on the StatsCollector
|
||||
// implementation.
|
||||
|
||||
#ifndef PC_LEGACY_STATS_COLLECTOR_INTERFACE_H_
|
||||
#define PC_LEGACY_STATS_COLLECTOR_INTERFACE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/legacy_stats_types.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class LegacyStatsCollectorInterface {
|
||||
public:
|
||||
virtual ~LegacyStatsCollectorInterface() {}
|
||||
|
||||
// Adds a local audio track that is used for getting some voice statistics.
|
||||
virtual void AddLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
uint32_t ssrc) = 0;
|
||||
|
||||
// Removes a local audio tracks that is used for getting some voice
|
||||
// statistics.
|
||||
virtual void RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
|
||||
uint32_t ssrc) = 0;
|
||||
virtual void GetStats(MediaStreamTrackInterface* track,
|
||||
StatsReports* reports) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_LEGACY_STATS_COLLECTOR_INTERFACE_H_
|
||||
1964
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector_unittest.cc
Normal file
1964
TMessagesProj/jni/voip/webrtc/pc/legacy_stats_collector_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
31
TMessagesProj/jni/voip/webrtc/pc/local_audio_source.cc
Normal file
31
TMessagesProj/jni/voip/webrtc/pc/local_audio_source.cc
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2013 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 "pc/local_audio_source.h"
|
||||
|
||||
using webrtc::MediaSourceInterface;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
rtc::scoped_refptr<LocalAudioSource> LocalAudioSource::Create(
|
||||
const cricket::AudioOptions* audio_options) {
|
||||
auto source = rtc::make_ref_counted<LocalAudioSource>();
|
||||
source->Initialize(audio_options);
|
||||
return source;
|
||||
}
|
||||
|
||||
void LocalAudioSource::Initialize(const cricket::AudioOptions* audio_options) {
|
||||
if (!audio_options)
|
||||
return;
|
||||
|
||||
options_ = *audio_options;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
50
TMessagesProj/jni/voip/webrtc/pc/local_audio_source.h
Normal file
50
TMessagesProj/jni/voip/webrtc/pc/local_audio_source.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_LOCAL_AUDIO_SOURCE_H_
|
||||
#define PC_LOCAL_AUDIO_SOURCE_H_
|
||||
|
||||
#include "api/audio_options.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/notifier.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
|
||||
// LocalAudioSource implements AudioSourceInterface.
|
||||
// This contains settings for switching audio processing on and off.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class LocalAudioSource : public Notifier<AudioSourceInterface> {
|
||||
public:
|
||||
// Creates an instance of LocalAudioSource.
|
||||
static rtc::scoped_refptr<LocalAudioSource> Create(
|
||||
const cricket::AudioOptions* audio_options);
|
||||
|
||||
SourceState state() const override { return kLive; }
|
||||
bool remote() const override { return false; }
|
||||
|
||||
const cricket::AudioOptions options() const override { return options_; }
|
||||
|
||||
void AddSink(AudioTrackSinkInterface* sink) override {}
|
||||
void RemoveSink(AudioTrackSinkInterface* sink) override {}
|
||||
|
||||
protected:
|
||||
LocalAudioSource() {}
|
||||
~LocalAudioSource() override {}
|
||||
|
||||
private:
|
||||
void Initialize(const cricket::AudioOptions* audio_options);
|
||||
|
||||
cricket::AudioOptions options_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_LOCAL_AUDIO_SOURCE_H_
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2013 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 "pc/local_audio_source.h"
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using webrtc::LocalAudioSource;
|
||||
|
||||
TEST(LocalAudioSourceTest, InitWithAudioOptions) {
|
||||
cricket::AudioOptions audio_options;
|
||||
audio_options.highpass_filter = true;
|
||||
rtc::scoped_refptr<LocalAudioSource> source =
|
||||
LocalAudioSource::Create(&audio_options);
|
||||
EXPECT_EQ(true, source->options().highpass_filter);
|
||||
}
|
||||
|
||||
TEST(LocalAudioSourceTest, InitWithNoOptions) {
|
||||
rtc::scoped_refptr<LocalAudioSource> source =
|
||||
LocalAudioSource::Create(nullptr);
|
||||
EXPECT_EQ(absl::nullopt, source->options().highpass_filter);
|
||||
}
|
||||
45
TMessagesProj/jni/voip/webrtc/pc/media_factory.h
Normal file
45
TMessagesProj/jni/voip/webrtc/pc/media_factory.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_MEDIA_FACTORY_H_
|
||||
#define PC_MEDIA_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "call/call.h"
|
||||
#include "call/call_config.h"
|
||||
#include "media/base/media_engine.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// PeerConnectionFactoryDependencies is forward declared because of circular
|
||||
// dependency between MediaFactory and PeerConnectionFactoryDependencies:
|
||||
// PeerConnectionFactoryDependencies keeps an instance of MediaFactory and thus
|
||||
// needs to know how to destroy it.
|
||||
// MediaFactory mentions PeerConnectionFactoryDependencies in api, but does not
|
||||
// need its full definition.
|
||||
struct PeerConnectionFactoryDependencies;
|
||||
|
||||
// Interface repsponsible for constructing media specific classes for
|
||||
// PeerConnectionFactory and PeerConnection.
|
||||
class MediaFactory {
|
||||
public:
|
||||
virtual ~MediaFactory() = default;
|
||||
|
||||
virtual std::unique_ptr<Call> CreateCall(const CallConfig& config) = 0;
|
||||
virtual std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine(
|
||||
const Environment& env,
|
||||
PeerConnectionFactoryDependencies& dependencies) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_MEDIA_FACTORY_H_
|
||||
105
TMessagesProj/jni/voip/webrtc/pc/media_protocol_names.cc
Normal file
105
TMessagesProj/jni/voip/webrtc/pc/media_protocol_names.cc
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2019 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 "pc/media_protocol_names.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// The official registry of RTP parameters is at
|
||||
// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
|
||||
// The UDP/DTLS and TCP/DTLS prefixes are not registered there.
|
||||
|
||||
// There are multiple variants of the RTP protocol stack, including
|
||||
// UDP/TLS/RTP/SAVPF (WebRTC default), RTP/AVP, RTP/AVPF, RTP/SAVPF,
|
||||
// TCP/DTLS/RTP/SAVPF and so on. We accept anything that has RTP/
|
||||
// embedded in it somewhere as being an RTP protocol.
|
||||
const char kMediaProtocolRtpPrefix[] = "RTP/";
|
||||
|
||||
// Protocol names generated by WebRTC
|
||||
const char kMediaProtocolSctp[] = "SCTP";
|
||||
const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
|
||||
const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
|
||||
const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
|
||||
// RFC5124
|
||||
const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
|
||||
const char kMediaProtocolSavpf[] = "RTP/SAVPF";
|
||||
const char kMediaProtocolAvpf[] = "RTP/AVPF";
|
||||
|
||||
namespace {
|
||||
|
||||
// Protocol names that we tolerate, but do not generate.
|
||||
// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
|
||||
// but we tolerate "RTP/SAVPF" and "RTP/SAVP" and the "UDP/TLS" and "TCP/TLS"
|
||||
// prefixes in offers we receive, for compatibility.
|
||||
// RFC4585
|
||||
const char kMediaProtocolSavp[] = "RTP/SAVP";
|
||||
const char kMediaProtocolAvp[] = "RTP/AVP";
|
||||
|
||||
const char kMediaProtocolTcpTlsSavpf[] = "TCP/TLS/RTP/SAVPF";
|
||||
const char kMediaProtocolUdpTlsSavpf[] = "UDP/TLS/RTP/SAVPF";
|
||||
const char kMediaProtocolTcpTlsSavp[] = "TCP/TLS/RTP/SAVP";
|
||||
const char kMediaProtocolUdpTlsSavp[] = "UDP/TLS/RTP/SAVP";
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsDtlsSctp(absl::string_view protocol) {
|
||||
return protocol == kMediaProtocolDtlsSctp ||
|
||||
protocol == kMediaProtocolUdpDtlsSctp ||
|
||||
protocol == kMediaProtocolTcpDtlsSctp;
|
||||
}
|
||||
|
||||
bool IsPlainSctp(absl::string_view protocol) {
|
||||
return protocol == kMediaProtocolSctp;
|
||||
}
|
||||
|
||||
bool IsSctpProtocol(absl::string_view protocol) {
|
||||
return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
|
||||
}
|
||||
|
||||
bool IsRtpProtocol(absl::string_view protocol) {
|
||||
if (protocol.empty()) {
|
||||
return true;
|
||||
}
|
||||
size_t pos = protocol.find(cricket::kMediaProtocolRtpPrefix);
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
// RTP must be at the beginning of a string or not preceded by alpha
|
||||
if (pos == 0 || !isalpha(static_cast<unsigned char>(protocol[pos - 1]))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note that the below functions support some protocol strings purely for
|
||||
// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
|
||||
// and Interoperability.
|
||||
|
||||
bool IsDtlsRtp(absl::string_view protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == kMediaProtocolDtlsSavpf ||
|
||||
protocol == kMediaProtocolTcpTlsSavpf ||
|
||||
protocol == kMediaProtocolUdpTlsSavpf ||
|
||||
protocol == kMediaProtocolUdpTlsSavp ||
|
||||
protocol == kMediaProtocolTcpTlsSavp;
|
||||
}
|
||||
|
||||
bool IsPlainRtp(absl::string_view protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == kMediaProtocolSavpf || protocol == kMediaProtocolAvpf ||
|
||||
protocol == kMediaProtocolSavp || protocol == kMediaProtocolAvp;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
47
TMessagesProj/jni/voip/webrtc/pc/media_protocol_names.h
Normal file
47
TMessagesProj/jni/voip/webrtc/pc/media_protocol_names.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_MEDIA_PROTOCOL_NAMES_H_
|
||||
#define PC_MEDIA_PROTOCOL_NAMES_H_
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Names or name prefixes of protocols as defined by SDP specifications,
|
||||
// and generated in SDP produced by WebRTC.
|
||||
extern const char kMediaProtocolSctp[];
|
||||
extern const char kMediaProtocolUdpDtlsSctp[];
|
||||
extern const char kMediaProtocolDtlsSavpf[];
|
||||
extern const char kMediaProtocolSavpf[];
|
||||
extern const char kMediaProtocolAvpf[];
|
||||
|
||||
// Exported for testing only
|
||||
extern const char kMediaProtocolTcpDtlsSctp[];
|
||||
extern const char kMediaProtocolDtlsSctp[];
|
||||
|
||||
// Returns true if the given media section protocol indicates use of RTP.
|
||||
bool IsRtpProtocol(absl::string_view protocol);
|
||||
// Returns true if the given media section protocol indicates use of SCTP.
|
||||
bool IsSctpProtocol(absl::string_view protocol);
|
||||
|
||||
// Returns true if the given media protocol is unencrypted SCTP
|
||||
bool IsPlainSctp(absl::string_view protocol);
|
||||
// Returns true if the given media protocol is encrypted SCTP
|
||||
bool IsDtlsSctp(absl::string_view protocol);
|
||||
|
||||
// Returns true if the given media protocol is unencrypted RTP
|
||||
bool IsPlainRtp(absl::string_view protocol);
|
||||
// Returns true if the given media protocol is encrypted RTP
|
||||
bool IsDtlsRtp(absl::string_view protocol);
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_MEDIA_PROTOCOL_NAMES_H_
|
||||
2580
TMessagesProj/jni/voip/webrtc/pc/media_session.cc
Normal file
2580
TMessagesProj/jni/voip/webrtc/pc/media_session.cc
Normal file
File diff suppressed because it is too large
Load diff
366
TMessagesProj/jni/voip/webrtc/pc/media_session.h
Normal file
366
TMessagesProj/jni/voip/webrtc/pc/media_session.h
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright 2004 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.
|
||||
*/
|
||||
|
||||
// Types and classes used in media session descriptions.
|
||||
|
||||
#ifndef PC_MEDIA_SESSION_H_
|
||||
#define PC_MEDIA_SESSION_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "media/base/rid_description.h"
|
||||
#include "media/base/stream_params.h"
|
||||
#include "p2p/base/ice_credentials_iterator.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_description_factory.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/jsep_transport.h"
|
||||
#include "pc/media_protocol_names.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/simulcast_description.h"
|
||||
#include "rtc_base/memory/always_valid_pointer.h"
|
||||
#include "rtc_base/unique_id_generator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Forward declaration due to circular dependecy.
|
||||
class ConnectionContext;
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class MediaEngineInterface;
|
||||
|
||||
// Default RTCP CNAME for unit tests.
|
||||
const char kDefaultRtcpCname[] = "DefaultRtcpCname";
|
||||
|
||||
// Options for an RtpSender contained with an media description/"m=" section.
|
||||
// Note: Spec-compliant Simulcast and legacy simulcast are mutually exclusive.
|
||||
struct SenderOptions {
|
||||
std::string track_id;
|
||||
std::vector<std::string> stream_ids;
|
||||
// Use RIDs and Simulcast Layers to indicate spec-compliant Simulcast.
|
||||
std::vector<RidDescription> rids;
|
||||
SimulcastLayerList simulcast_layers;
|
||||
// Use `num_sim_layers` to indicate legacy simulcast.
|
||||
int num_sim_layers;
|
||||
};
|
||||
|
||||
// Options for an individual media description/"m=" section.
|
||||
struct MediaDescriptionOptions {
|
||||
MediaDescriptionOptions(MediaType type,
|
||||
const std::string& mid,
|
||||
webrtc::RtpTransceiverDirection direction,
|
||||
bool stopped)
|
||||
: type(type), mid(mid), direction(direction), stopped(stopped) {}
|
||||
|
||||
// TODO(deadbeef): When we don't support Plan B, there will only be one
|
||||
// sender per media description and this can be simplified.
|
||||
void AddAudioSender(const std::string& track_id,
|
||||
const std::vector<std::string>& stream_ids);
|
||||
void AddVideoSender(const std::string& track_id,
|
||||
const std::vector<std::string>& stream_ids,
|
||||
const std::vector<RidDescription>& rids,
|
||||
const SimulcastLayerList& simulcast_layers,
|
||||
int num_sim_layers);
|
||||
|
||||
MediaType type;
|
||||
std::string mid;
|
||||
webrtc::RtpTransceiverDirection direction;
|
||||
bool stopped;
|
||||
TransportOptions transport_options;
|
||||
// Note: There's no equivalent "RtpReceiverOptions" because only send
|
||||
// stream information goes in the local descriptions.
|
||||
std::vector<SenderOptions> sender_options;
|
||||
std::vector<webrtc::RtpCodecCapability> codec_preferences;
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions;
|
||||
|
||||
private:
|
||||
// Doesn't DCHECK on `type`.
|
||||
void AddSenderInternal(const std::string& track_id,
|
||||
const std::vector<std::string>& stream_ids,
|
||||
const std::vector<RidDescription>& rids,
|
||||
const SimulcastLayerList& simulcast_layers,
|
||||
int num_sim_layers);
|
||||
};
|
||||
|
||||
// Provides a mechanism for describing how m= sections should be generated.
|
||||
// The m= section with index X will use media_description_options[X]. There
|
||||
// must be an option for each existing section if creating an answer, or a
|
||||
// subsequent offer.
|
||||
struct MediaSessionOptions {
|
||||
MediaSessionOptions() {}
|
||||
|
||||
bool has_audio() const { return HasMediaDescription(MEDIA_TYPE_AUDIO); }
|
||||
bool has_video() const { return HasMediaDescription(MEDIA_TYPE_VIDEO); }
|
||||
bool has_data() const { return HasMediaDescription(MEDIA_TYPE_DATA); }
|
||||
|
||||
bool HasMediaDescription(MediaType type) const;
|
||||
|
||||
bool vad_enabled = true; // When disabled, removes all CN codecs from SDP.
|
||||
bool rtcp_mux_enabled = true;
|
||||
bool bundle_enabled = false;
|
||||
bool offer_extmap_allow_mixed = false;
|
||||
bool raw_packetization_for_video = false;
|
||||
std::string rtcp_cname = kDefaultRtcpCname;
|
||||
webrtc::CryptoOptions crypto_options;
|
||||
// List of media description options in the same order that the media
|
||||
// descriptions will be generated.
|
||||
std::vector<MediaDescriptionOptions> media_description_options;
|
||||
std::vector<IceParameters> pooled_ice_credentials;
|
||||
|
||||
// Use the draft-ietf-mmusic-sctp-sdp-03 obsolete syntax for SCTP
|
||||
// datachannels.
|
||||
// Default is true for backwards compatibility with clients that use
|
||||
// this internal interface.
|
||||
bool use_obsolete_sctp_sdp = true;
|
||||
};
|
||||
|
||||
// Creates media session descriptions according to the supplied codecs and
|
||||
// other fields, as well as the supplied per-call options.
|
||||
// When creating answers, performs the appropriate negotiation
|
||||
// of the various fields to determine the proper result.
|
||||
class MediaSessionDescriptionFactory {
|
||||
public:
|
||||
// This constructor automatically sets up the factory to get its configuration
|
||||
// from the specified MediaEngine (when provided).
|
||||
// The TransportDescriptionFactory and the UniqueRandomIdGenerator are not
|
||||
// owned by MediaSessionDescriptionFactory, so they must be kept alive by the
|
||||
// user of this class.
|
||||
MediaSessionDescriptionFactory(cricket::MediaEngineInterface* media_engine,
|
||||
bool rtx_enabled,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||
const TransportDescriptionFactory* factory);
|
||||
|
||||
const AudioCodecs& audio_sendrecv_codecs() const;
|
||||
const AudioCodecs& audio_send_codecs() const;
|
||||
const AudioCodecs& audio_recv_codecs() const;
|
||||
void set_audio_codecs(const AudioCodecs& send_codecs,
|
||||
const AudioCodecs& recv_codecs);
|
||||
const VideoCodecs& video_sendrecv_codecs() const;
|
||||
const VideoCodecs& video_send_codecs() const;
|
||||
const VideoCodecs& video_recv_codecs() const;
|
||||
void set_video_codecs(const VideoCodecs& send_codecs,
|
||||
const VideoCodecs& recv_codecs);
|
||||
RtpHeaderExtensions filtered_rtp_header_extensions(
|
||||
RtpHeaderExtensions extensions) const;
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
enable_encrypted_rtp_header_extensions_ = enable;
|
||||
}
|
||||
|
||||
void set_is_unified_plan(bool is_unified_plan) {
|
||||
is_unified_plan_ = is_unified_plan;
|
||||
}
|
||||
|
||||
webrtc::RTCErrorOr<std::unique_ptr<SessionDescription>> CreateOfferOrError(
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description) const;
|
||||
webrtc::RTCErrorOr<std::unique_ptr<SessionDescription>> CreateAnswerOrError(
|
||||
const SessionDescription* offer,
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description) const;
|
||||
|
||||
private:
|
||||
struct AudioVideoRtpHeaderExtensions {
|
||||
RtpHeaderExtensions audio;
|
||||
RtpHeaderExtensions video;
|
||||
};
|
||||
|
||||
const AudioCodecs& GetAudioCodecsForOffer(
|
||||
const webrtc::RtpTransceiverDirection& direction) const;
|
||||
const AudioCodecs& GetAudioCodecsForAnswer(
|
||||
const webrtc::RtpTransceiverDirection& offer,
|
||||
const webrtc::RtpTransceiverDirection& answer) const;
|
||||
const VideoCodecs& GetVideoCodecsForOffer(
|
||||
const webrtc::RtpTransceiverDirection& direction) const;
|
||||
const VideoCodecs& GetVideoCodecsForAnswer(
|
||||
const webrtc::RtpTransceiverDirection& offer,
|
||||
const webrtc::RtpTransceiverDirection& answer) const;
|
||||
void GetCodecsForOffer(
|
||||
const std::vector<const ContentInfo*>& current_active_contents,
|
||||
AudioCodecs* audio_codecs,
|
||||
VideoCodecs* video_codecs) const;
|
||||
void GetCodecsForAnswer(
|
||||
const std::vector<const ContentInfo*>& current_active_contents,
|
||||
const SessionDescription& remote_offer,
|
||||
AudioCodecs* audio_codecs,
|
||||
VideoCodecs* video_codecs) const;
|
||||
AudioVideoRtpHeaderExtensions GetOfferedRtpHeaderExtensionsWithIds(
|
||||
const std::vector<const ContentInfo*>& current_active_contents,
|
||||
bool extmap_allow_mixed,
|
||||
const std::vector<MediaDescriptionOptions>& media_description_options)
|
||||
const;
|
||||
webrtc::RTCError AddTransportOffer(
|
||||
const std::string& content_name,
|
||||
const TransportOptions& transport_options,
|
||||
const SessionDescription* current_desc,
|
||||
SessionDescription* offer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
std::unique_ptr<TransportDescription> CreateTransportAnswer(
|
||||
const std::string& content_name,
|
||||
const SessionDescription* offer_desc,
|
||||
const TransportOptions& transport_options,
|
||||
const SessionDescription* current_desc,
|
||||
bool require_transport_attributes,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddTransportAnswer(
|
||||
const std::string& content_name,
|
||||
const TransportDescription& transport_desc,
|
||||
SessionDescription* answer_desc) const;
|
||||
|
||||
// Helpers for adding media contents to the SessionDescription.
|
||||
webrtc::RTCError AddRtpContentForOffer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const RtpHeaderExtensions& header_extensions,
|
||||
const std::vector<Codec>& codecs,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* desc,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddDataContentForOffer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* desc,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddUnsupportedContentForOffer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
SessionDescription* desc,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddRtpContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
const std::vector<Codec>& codecs,
|
||||
const RtpHeaderExtensions& header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddDataContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
webrtc::RTCError AddUnsupportedContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
void ComputeAudioCodecsIntersectionAndUnion();
|
||||
|
||||
void ComputeVideoCodecsIntersectionAndUnion();
|
||||
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator() const {
|
||||
return ssrc_generator_.get();
|
||||
}
|
||||
|
||||
bool is_unified_plan_ = false;
|
||||
AudioCodecs audio_send_codecs_;
|
||||
AudioCodecs audio_recv_codecs_;
|
||||
// Intersection of send and recv.
|
||||
AudioCodecs audio_sendrecv_codecs_;
|
||||
// Union of send and recv.
|
||||
AudioCodecs all_audio_codecs_;
|
||||
VideoCodecs video_send_codecs_;
|
||||
VideoCodecs video_recv_codecs_;
|
||||
// Intersection of send and recv.
|
||||
VideoCodecs video_sendrecv_codecs_;
|
||||
// Union of send and recv.
|
||||
VideoCodecs all_video_codecs_;
|
||||
// This object may or may not be owned by this class.
|
||||
webrtc::AlwaysValidPointer<rtc::UniqueRandomIdGenerator> const
|
||||
ssrc_generator_;
|
||||
bool enable_encrypted_rtp_header_extensions_ = false;
|
||||
const TransportDescriptionFactory* transport_desc_factory_;
|
||||
};
|
||||
|
||||
// Convenience functions.
|
||||
bool IsMediaContent(const ContentInfo* content);
|
||||
bool IsAudioContent(const ContentInfo* content);
|
||||
bool IsVideoContent(const ContentInfo* content);
|
||||
bool IsDataContent(const ContentInfo* content);
|
||||
bool IsUnsupportedContent(const ContentInfo* content);
|
||||
const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
|
||||
MediaType media_type);
|
||||
const ContentInfo* GetFirstAudioContent(const ContentInfos& contents);
|
||||
const ContentInfo* GetFirstVideoContent(const ContentInfos& contents);
|
||||
const ContentInfo* GetFirstDataContent(const ContentInfos& contents);
|
||||
const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
|
||||
MediaType media_type);
|
||||
const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc);
|
||||
const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc);
|
||||
const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc);
|
||||
const AudioContentDescription* GetFirstAudioContentDescription(
|
||||
const SessionDescription* sdesc);
|
||||
const VideoContentDescription* GetFirstVideoContentDescription(
|
||||
const SessionDescription* sdesc);
|
||||
const SctpDataContentDescription* GetFirstSctpDataContentDescription(
|
||||
const SessionDescription* sdesc);
|
||||
// Non-const versions of the above functions.
|
||||
// Useful when modifying an existing description.
|
||||
ContentInfo* GetFirstMediaContent(ContentInfos* contents, MediaType media_type);
|
||||
ContentInfo* GetFirstAudioContent(ContentInfos* contents);
|
||||
ContentInfo* GetFirstVideoContent(ContentInfos* contents);
|
||||
ContentInfo* GetFirstDataContent(ContentInfos* contents);
|
||||
ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
|
||||
MediaType media_type);
|
||||
ContentInfo* GetFirstAudioContent(SessionDescription* sdesc);
|
||||
ContentInfo* GetFirstVideoContent(SessionDescription* sdesc);
|
||||
ContentInfo* GetFirstDataContent(SessionDescription* sdesc);
|
||||
AudioContentDescription* GetFirstAudioContentDescription(
|
||||
SessionDescription* sdesc);
|
||||
VideoContentDescription* GetFirstVideoContentDescription(
|
||||
SessionDescription* sdesc);
|
||||
SctpDataContentDescription* GetFirstSctpDataContentDescription(
|
||||
SessionDescription* sdesc);
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_MEDIA_SESSION_H_
|
||||
4701
TMessagesProj/jni/voip/webrtc/pc/media_session_unittest.cc
Normal file
4701
TMessagesProj/jni/voip/webrtc/pc/media_session_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
96
TMessagesProj/jni/voip/webrtc/pc/media_stream.cc
Normal file
96
TMessagesProj/jni/voip/webrtc/pc/media_stream.cc
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2011 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 "pc/media_stream.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <class V>
|
||||
static typename V::iterator FindTrack(V* vector, const std::string& track_id) {
|
||||
typename V::iterator it = vector->begin();
|
||||
for (; it != vector->end(); ++it) {
|
||||
if ((*it)->id() == track_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<MediaStream> MediaStream::Create(const std::string& id) {
|
||||
return rtc::make_ref_counted<MediaStream>(id);
|
||||
}
|
||||
|
||||
MediaStream::MediaStream(const std::string& id) : id_(id) {}
|
||||
|
||||
bool MediaStream::AddTrack(rtc::scoped_refptr<AudioTrackInterface> track) {
|
||||
return AddTrack<AudioTrackVector, AudioTrackInterface>(&audio_tracks_, track);
|
||||
}
|
||||
|
||||
bool MediaStream::AddTrack(rtc::scoped_refptr<VideoTrackInterface> track) {
|
||||
return AddTrack<VideoTrackVector, VideoTrackInterface>(&video_tracks_, track);
|
||||
}
|
||||
|
||||
bool MediaStream::RemoveTrack(rtc::scoped_refptr<AudioTrackInterface> track) {
|
||||
return RemoveTrack<AudioTrackVector>(&audio_tracks_, track);
|
||||
}
|
||||
|
||||
bool MediaStream::RemoveTrack(rtc::scoped_refptr<VideoTrackInterface> track) {
|
||||
return RemoveTrack<VideoTrackVector>(&video_tracks_, track);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> MediaStream::FindAudioTrack(
|
||||
const std::string& track_id) {
|
||||
AudioTrackVector::iterator it = FindTrack(&audio_tracks_, track_id);
|
||||
if (it == audio_tracks_.end())
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> MediaStream::FindVideoTrack(
|
||||
const std::string& track_id) {
|
||||
VideoTrackVector::iterator it = FindTrack(&video_tracks_, track_id);
|
||||
if (it == video_tracks_.end())
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
|
||||
template <typename TrackVector, typename Track>
|
||||
bool MediaStream::AddTrack(TrackVector* tracks,
|
||||
rtc::scoped_refptr<Track> track) {
|
||||
typename TrackVector::iterator it = FindTrack(tracks, track->id());
|
||||
if (it != tracks->end())
|
||||
return false;
|
||||
tracks->emplace_back(std::move((track)));
|
||||
FireOnChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TrackVector>
|
||||
bool MediaStream::RemoveTrack(
|
||||
TrackVector* tracks,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track) {
|
||||
RTC_DCHECK(tracks != NULL);
|
||||
if (!track)
|
||||
return false;
|
||||
typename TrackVector::iterator it = FindTrack(tracks, track->id());
|
||||
if (it == tracks->end())
|
||||
return false;
|
||||
tracks->erase(it);
|
||||
FireOnChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
59
TMessagesProj/jni/voip/webrtc/pc/media_stream.h
Normal file
59
TMessagesProj/jni/voip/webrtc/pc/media_stream.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
// This file contains the implementation of MediaStreamInterface interface.
|
||||
|
||||
#ifndef PC_MEDIA_STREAM_H_
|
||||
#define PC_MEDIA_STREAM_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/notifier.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MediaStream : public Notifier<MediaStreamInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MediaStream> Create(const std::string& id);
|
||||
|
||||
std::string id() const override { return id_; }
|
||||
|
||||
bool AddTrack(rtc::scoped_refptr<AudioTrackInterface> track) override;
|
||||
bool AddTrack(rtc::scoped_refptr<VideoTrackInterface> track) override;
|
||||
bool RemoveTrack(rtc::scoped_refptr<AudioTrackInterface> track) override;
|
||||
bool RemoveTrack(rtc::scoped_refptr<VideoTrackInterface> track) override;
|
||||
rtc::scoped_refptr<AudioTrackInterface> FindAudioTrack(
|
||||
const std::string& track_id) override;
|
||||
rtc::scoped_refptr<VideoTrackInterface> FindVideoTrack(
|
||||
const std::string& track_id) override;
|
||||
|
||||
AudioTrackVector GetAudioTracks() override { return audio_tracks_; }
|
||||
VideoTrackVector GetVideoTracks() override { return video_tracks_; }
|
||||
|
||||
protected:
|
||||
explicit MediaStream(const std::string& id);
|
||||
|
||||
private:
|
||||
template <typename TrackVector, typename Track>
|
||||
bool AddTrack(TrackVector* Tracks, rtc::scoped_refptr<Track> track);
|
||||
template <typename TrackVector>
|
||||
bool RemoveTrack(TrackVector* Tracks,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track);
|
||||
|
||||
const std::string id_;
|
||||
AudioTrackVector audio_tracks_;
|
||||
VideoTrackVector video_tracks_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_MEDIA_STREAM_H_
|
||||
98
TMessagesProj/jni/voip/webrtc/pc/media_stream_observer.cc
Normal file
98
TMessagesProj/jni/voip/webrtc/pc/media_stream_observer.cc
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2015 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 "pc/media_stream_observer.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
MediaStreamObserver::MediaStreamObserver(
|
||||
MediaStreamInterface* stream,
|
||||
std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_added_callback,
|
||||
std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_removed_callback,
|
||||
std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_added_callback,
|
||||
std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_removed_callback)
|
||||
: stream_(stream),
|
||||
cached_audio_tracks_(stream->GetAudioTracks()),
|
||||
cached_video_tracks_(stream->GetVideoTracks()),
|
||||
audio_track_added_callback_(std::move(audio_track_added_callback)),
|
||||
audio_track_removed_callback_(std::move(audio_track_removed_callback)),
|
||||
video_track_added_callback_(std::move(video_track_added_callback)),
|
||||
video_track_removed_callback_(std::move(video_track_removed_callback)) {
|
||||
stream_->RegisterObserver(this);
|
||||
}
|
||||
|
||||
MediaStreamObserver::~MediaStreamObserver() {
|
||||
stream_->UnregisterObserver(this);
|
||||
}
|
||||
|
||||
void MediaStreamObserver::OnChanged() {
|
||||
AudioTrackVector new_audio_tracks = stream_->GetAudioTracks();
|
||||
VideoTrackVector new_video_tracks = stream_->GetVideoTracks();
|
||||
|
||||
// Find removed audio tracks.
|
||||
for (const auto& cached_track : cached_audio_tracks_) {
|
||||
if (absl::c_none_of(
|
||||
new_audio_tracks,
|
||||
[cached_track](const AudioTrackVector::value_type& new_track) {
|
||||
return new_track->id() == cached_track->id();
|
||||
})) {
|
||||
audio_track_removed_callback_(cached_track.get(), stream_.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Find added audio tracks.
|
||||
for (const auto& new_track : new_audio_tracks) {
|
||||
if (absl::c_none_of(
|
||||
cached_audio_tracks_,
|
||||
[new_track](const AudioTrackVector::value_type& cached_track) {
|
||||
return new_track->id() == cached_track->id();
|
||||
})) {
|
||||
audio_track_added_callback_(new_track.get(), stream_.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Find removed video tracks.
|
||||
for (const auto& cached_track : cached_video_tracks_) {
|
||||
if (absl::c_none_of(
|
||||
new_video_tracks,
|
||||
[cached_track](const VideoTrackVector::value_type& new_track) {
|
||||
return new_track->id() == cached_track->id();
|
||||
})) {
|
||||
video_track_removed_callback_(cached_track.get(), stream_.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Find added video tracks.
|
||||
for (const auto& new_track : new_video_tracks) {
|
||||
if (absl::c_none_of(
|
||||
cached_video_tracks_,
|
||||
[new_track](const VideoTrackVector::value_type& cached_track) {
|
||||
return new_track->id() == cached_track->id();
|
||||
})) {
|
||||
video_track_added_callback_(new_track.get(), stream_.get());
|
||||
}
|
||||
}
|
||||
|
||||
cached_audio_tracks_ = new_audio_tracks;
|
||||
cached_video_tracks_ = new_video_tracks;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
57
TMessagesProj/jni/voip/webrtc/pc/media_stream_observer.h
Normal file
57
TMessagesProj/jni/voip/webrtc/pc/media_stream_observer.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_MEDIA_STREAM_OBSERVER_H_
|
||||
#define PC_MEDIA_STREAM_OBSERVER_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper class which will listen for changes to a stream and emit the
|
||||
// corresponding signals.
|
||||
class MediaStreamObserver : public ObserverInterface {
|
||||
public:
|
||||
explicit MediaStreamObserver(
|
||||
MediaStreamInterface* stream,
|
||||
std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_added_callback,
|
||||
std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_removed_callback,
|
||||
std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_added_callback,
|
||||
std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_removed_callback);
|
||||
~MediaStreamObserver() override;
|
||||
|
||||
const MediaStreamInterface* stream() const { return stream_.get(); }
|
||||
|
||||
void OnChanged() override;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<MediaStreamInterface> stream_;
|
||||
AudioTrackVector cached_audio_tracks_;
|
||||
VideoTrackVector cached_video_tracks_;
|
||||
const std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_added_callback_;
|
||||
const std::function<void(AudioTrackInterface*, MediaStreamInterface*)>
|
||||
audio_track_removed_callback_;
|
||||
const std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_added_callback_;
|
||||
const std::function<void(VideoTrackInterface*, MediaStreamInterface*)>
|
||||
video_track_removed_callback_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_MEDIA_STREAM_OBSERVER_H_
|
||||
44
TMessagesProj/jni/voip/webrtc/pc/media_stream_proxy.h
Normal file
44
TMessagesProj/jni/voip/webrtc/pc/media_stream_proxy.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_MEDIA_STREAM_PROXY_H_
|
||||
#define PC_MEDIA_STREAM_PROXY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "pc/proxy.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(deadbeef): Move this to a .cc file. What threads methods are called on
|
||||
// is an implementation detail.
|
||||
BEGIN_PRIMARY_PROXY_MAP(MediaStream)
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
|
||||
PROXY_METHOD0(AudioTrackVector, GetAudioTracks)
|
||||
PROXY_METHOD0(VideoTrackVector, GetVideoTracks)
|
||||
PROXY_METHOD1(rtc::scoped_refptr<AudioTrackInterface>,
|
||||
FindAudioTrack,
|
||||
const std::string&)
|
||||
PROXY_METHOD1(rtc::scoped_refptr<VideoTrackInterface>,
|
||||
FindVideoTrack,
|
||||
const std::string&)
|
||||
PROXY_METHOD1(bool, AddTrack, rtc::scoped_refptr<AudioTrackInterface>)
|
||||
PROXY_METHOD1(bool, AddTrack, rtc::scoped_refptr<VideoTrackInterface>)
|
||||
PROXY_METHOD1(bool, RemoveTrack, rtc::scoped_refptr<AudioTrackInterface>)
|
||||
PROXY_METHOD1(bool, RemoveTrack, rtc::scoped_refptr<VideoTrackInterface>)
|
||||
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
|
||||
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
|
||||
END_PROXY_MAP(MediaStream)
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_MEDIA_STREAM_PROXY_H_
|
||||
65
TMessagesProj/jni/voip/webrtc/pc/media_stream_track_proxy.h
Normal file
65
TMessagesProj/jni/voip/webrtc/pc/media_stream_track_proxy.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
// This file includes proxy classes for tracks. The purpose is
|
||||
// to make sure tracks are only accessed from the signaling thread.
|
||||
|
||||
#ifndef PC_MEDIA_STREAM_TRACK_PROXY_H_
|
||||
#define PC_MEDIA_STREAM_TRACK_PROXY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "pc/proxy.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(deadbeef): Move this to .cc file. What threads methods are called on is
|
||||
// an implementation detail.
|
||||
BEGIN_PRIMARY_PROXY_MAP(AudioTrack)
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
BYPASS_PROXY_CONSTMETHOD0(std::string, kind)
|
||||
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
|
||||
PROXY_CONSTMETHOD0(TrackState, state)
|
||||
PROXY_CONSTMETHOD0(bool, enabled)
|
||||
BYPASS_PROXY_CONSTMETHOD0(AudioSourceInterface*, GetSource)
|
||||
PROXY_METHOD1(void, AddSink, AudioTrackSinkInterface*)
|
||||
PROXY_METHOD1(void, RemoveSink, AudioTrackSinkInterface*)
|
||||
PROXY_METHOD1(bool, GetSignalLevel, int*)
|
||||
PROXY_METHOD0(rtc::scoped_refptr<AudioProcessorInterface>, GetAudioProcessor)
|
||||
PROXY_METHOD1(bool, set_enabled, bool)
|
||||
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
|
||||
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
|
||||
END_PROXY_MAP(AudioTrack)
|
||||
|
||||
BEGIN_PROXY_MAP(VideoTrack)
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
BYPASS_PROXY_CONSTMETHOD0(std::string, kind)
|
||||
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
|
||||
PROXY_SECONDARY_CONSTMETHOD0(TrackState, state)
|
||||
PROXY_CONSTMETHOD0(bool, enabled)
|
||||
PROXY_METHOD1(bool, set_enabled, bool)
|
||||
PROXY_CONSTMETHOD0(ContentHint, content_hint)
|
||||
PROXY_METHOD1(void, set_content_hint, ContentHint)
|
||||
PROXY_SECONDARY_METHOD2(void,
|
||||
AddOrUpdateSink,
|
||||
rtc::VideoSinkInterface<VideoFrame>*,
|
||||
const rtc::VideoSinkWants&)
|
||||
PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
|
||||
PROXY_SECONDARY_METHOD0(void, RequestRefreshFrame)
|
||||
BYPASS_PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource)
|
||||
|
||||
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
|
||||
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
|
||||
END_PROXY_MAP(VideoTrack)
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_MEDIA_STREAM_TRACK_PROXY_H_
|
||||
151
TMessagesProj/jni/voip/webrtc/pc/media_stream_unittest.cc
Normal file
151
TMessagesProj/jni/voip/webrtc/pc/media_stream_unittest.cc
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2011 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 "pc/media_stream.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "pc/audio_track.h"
|
||||
#include "pc/test/fake_video_track_source.h"
|
||||
#include "pc/video_track.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
static const char kStreamId1[] = "local_stream_1";
|
||||
static const char kVideoTrackId[] = "dummy_video_cam_1";
|
||||
static const char kAudioTrackId[] = "dummy_microphone_1";
|
||||
|
||||
using rtc::scoped_refptr;
|
||||
using ::testing::Exactly;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper class to test Observer.
|
||||
class MockObserver : public ObserverInterface {
|
||||
public:
|
||||
explicit MockObserver(NotifierInterface* notifier) : notifier_(notifier) {
|
||||
notifier_->RegisterObserver(this);
|
||||
}
|
||||
|
||||
~MockObserver() { Unregister(); }
|
||||
|
||||
void Unregister() {
|
||||
if (notifier_) {
|
||||
notifier_->UnregisterObserver(this);
|
||||
notifier_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, OnChanged, (), (override));
|
||||
|
||||
private:
|
||||
NotifierInterface* notifier_;
|
||||
};
|
||||
|
||||
class MediaStreamTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
stream_ = MediaStream::Create(kStreamId1);
|
||||
ASSERT_TRUE(stream_.get() != NULL);
|
||||
|
||||
video_track_ = VideoTrack::Create(
|
||||
kVideoTrackId, FakeVideoTrackSource::Create(), rtc::Thread::Current());
|
||||
ASSERT_TRUE(video_track_.get() != NULL);
|
||||
EXPECT_EQ(MediaStreamTrackInterface::kLive, video_track_->state());
|
||||
|
||||
audio_track_ = AudioTrack::Create(kAudioTrackId, nullptr);
|
||||
|
||||
ASSERT_TRUE(audio_track_.get() != NULL);
|
||||
EXPECT_EQ(MediaStreamTrackInterface::kLive, audio_track_->state());
|
||||
|
||||
EXPECT_TRUE(stream_->AddTrack(video_track_));
|
||||
EXPECT_FALSE(stream_->AddTrack(video_track_));
|
||||
EXPECT_TRUE(stream_->AddTrack(audio_track_));
|
||||
EXPECT_FALSE(stream_->AddTrack(audio_track_));
|
||||
}
|
||||
|
||||
void ChangeTrack(MediaStreamTrackInterface* track) {
|
||||
MockObserver observer(track);
|
||||
|
||||
EXPECT_CALL(observer, OnChanged()).Times(Exactly(1));
|
||||
track->set_enabled(false);
|
||||
EXPECT_FALSE(track->enabled());
|
||||
}
|
||||
|
||||
rtc::AutoThread main_thread_;
|
||||
scoped_refptr<MediaStreamInterface> stream_;
|
||||
scoped_refptr<AudioTrackInterface> audio_track_;
|
||||
scoped_refptr<VideoTrackInterface> video_track_;
|
||||
};
|
||||
|
||||
TEST_F(MediaStreamTest, GetTrackInfo) {
|
||||
ASSERT_EQ(1u, stream_->GetVideoTracks().size());
|
||||
ASSERT_EQ(1u, stream_->GetAudioTracks().size());
|
||||
|
||||
// Verify the video track.
|
||||
scoped_refptr<MediaStreamTrackInterface> video_track(
|
||||
stream_->GetVideoTracks()[0]);
|
||||
EXPECT_EQ(0, video_track->id().compare(kVideoTrackId));
|
||||
EXPECT_TRUE(video_track->enabled());
|
||||
|
||||
ASSERT_EQ(1u, stream_->GetVideoTracks().size());
|
||||
EXPECT_TRUE(stream_->GetVideoTracks()[0].get() == video_track.get());
|
||||
EXPECT_TRUE(stream_->FindVideoTrack(video_track->id()).get() ==
|
||||
video_track.get());
|
||||
video_track = stream_->GetVideoTracks()[0];
|
||||
EXPECT_EQ(0, video_track->id().compare(kVideoTrackId));
|
||||
EXPECT_TRUE(video_track->enabled());
|
||||
|
||||
// Verify the audio track.
|
||||
scoped_refptr<MediaStreamTrackInterface> audio_track(
|
||||
stream_->GetAudioTracks()[0]);
|
||||
EXPECT_EQ(0, audio_track->id().compare(kAudioTrackId));
|
||||
EXPECT_TRUE(audio_track->enabled());
|
||||
ASSERT_EQ(1u, stream_->GetAudioTracks().size());
|
||||
EXPECT_TRUE(stream_->GetAudioTracks()[0].get() == audio_track.get());
|
||||
EXPECT_TRUE(stream_->FindAudioTrack(audio_track->id()).get() ==
|
||||
audio_track.get());
|
||||
audio_track = stream_->GetAudioTracks()[0];
|
||||
EXPECT_EQ(0, audio_track->id().compare(kAudioTrackId));
|
||||
EXPECT_TRUE(audio_track->enabled());
|
||||
}
|
||||
|
||||
TEST_F(MediaStreamTest, RemoveTrack) {
|
||||
MockObserver observer(stream_.get());
|
||||
|
||||
EXPECT_CALL(observer, OnChanged()).Times(Exactly(2));
|
||||
|
||||
EXPECT_TRUE(stream_->RemoveTrack(audio_track_));
|
||||
EXPECT_FALSE(stream_->RemoveTrack(audio_track_));
|
||||
EXPECT_EQ(0u, stream_->GetAudioTracks().size());
|
||||
EXPECT_EQ(0u, stream_->GetAudioTracks().size());
|
||||
|
||||
EXPECT_TRUE(stream_->RemoveTrack(video_track_));
|
||||
EXPECT_FALSE(stream_->RemoveTrack(video_track_));
|
||||
|
||||
EXPECT_EQ(0u, stream_->GetVideoTracks().size());
|
||||
EXPECT_EQ(0u, stream_->GetVideoTracks().size());
|
||||
|
||||
EXPECT_FALSE(stream_->RemoveTrack(rtc::scoped_refptr<AudioTrackInterface>()));
|
||||
EXPECT_FALSE(stream_->RemoveTrack(rtc::scoped_refptr<VideoTrackInterface>()));
|
||||
}
|
||||
|
||||
TEST_F(MediaStreamTest, ChangeVideoTrack) {
|
||||
scoped_refptr<VideoTrackInterface> video_track(stream_->GetVideoTracks()[0]);
|
||||
ChangeTrack(video_track.get());
|
||||
}
|
||||
|
||||
TEST_F(MediaStreamTest, ChangeAudioTrack) {
|
||||
scoped_refptr<AudioTrackInterface> audio_track(stream_->GetAudioTracks()[0]);
|
||||
ChangeTrack(audio_track.get());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
3000
TMessagesProj/jni/voip/webrtc/pc/peer_connection.cc
Normal file
3000
TMessagesProj/jni/voip/webrtc/pc/peer_connection.cc
Normal file
File diff suppressed because it is too large
Load diff
706
TMessagesProj/jni/voip/webrtc/pc/peer_connection.h
Normal file
706
TMessagesProj/jni/voip/webrtc/pc/peer_connection.h
Normal file
|
|
@ -0,0 +1,706 @@
|
|||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_H_
|
||||
#define PC_PEER_CONNECTION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/rtc_event_log_output.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "api/rtp_transceiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sctp_transport_interface.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/set_local_description_observer_interface.h"
|
||||
#include "api/set_remote_description_observer_interface.h"
|
||||
#include "api/stats/rtc_stats_collector_callback.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/transport/bitrate_settings.h"
|
||||
#include "api/transport/data_channel_transport_interface.h"
|
||||
#include "api/transport/enums.h"
|
||||
#include "api/turn_customizer.h"
|
||||
#include "call/call.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "pc/channel_interface.h"
|
||||
#include "pc/connection_context.h"
|
||||
#include "pc/data_channel_controller.h"
|
||||
#include "pc/data_channel_utils.h"
|
||||
#include "pc/dtls_transport.h"
|
||||
#include "pc/jsep_transport_controller.h"
|
||||
#include "pc/legacy_stats_collector.h"
|
||||
#include "pc/peer_connection_internal.h"
|
||||
#include "pc/peer_connection_message_handler.h"
|
||||
#include "pc/rtc_stats_collector.h"
|
||||
#include "pc/rtp_transceiver.h"
|
||||
#include "pc/rtp_transmission_manager.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "pc/sdp_offer_answer.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/transceiver_list.h"
|
||||
#include "pc/transport_stats.h"
|
||||
#include "pc/usage_pattern.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/weak_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// PeerConnection is the implementation of the PeerConnection object as defined
|
||||
// by the PeerConnectionInterface API surface.
|
||||
// The class currently is solely responsible for the following:
|
||||
// - Managing the session state machine (signaling state).
|
||||
// - Creating and initializing lower-level objects, like PortAllocator and
|
||||
// BaseChannels.
|
||||
// - Owning and managing the life cycle of the RtpSender/RtpReceiver and track
|
||||
// objects.
|
||||
// - Tracking the current and pending local/remote session descriptions.
|
||||
// The class currently is jointly responsible for the following:
|
||||
// - Parsing and interpreting SDP.
|
||||
// - Generating offers and answers based on the current state.
|
||||
// - The ICE state machine.
|
||||
// - Generating stats.
|
||||
class PeerConnection : public PeerConnectionInternal,
|
||||
public JsepTransportController::Observer {
|
||||
public:
|
||||
// Creates a PeerConnection and initializes it with the given values.
|
||||
// If the initialization fails, the function releases the PeerConnection
|
||||
// and returns nullptr.
|
||||
//
|
||||
// Note that the function takes ownership of dependencies, and will
|
||||
// either use them or release them, whether it succeeds or fails.
|
||||
static RTCErrorOr<rtc::scoped_refptr<PeerConnection>> Create(
|
||||
const Environment& env,
|
||||
rtc::scoped_refptr<ConnectionContext> context,
|
||||
const PeerConnectionFactoryInterface::Options& options,
|
||||
std::unique_ptr<Call> call,
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration,
|
||||
PeerConnectionDependencies dependencies);
|
||||
|
||||
rtc::scoped_refptr<StreamCollectionInterface> local_streams() override;
|
||||
rtc::scoped_refptr<StreamCollectionInterface> remote_streams() override;
|
||||
bool AddStream(MediaStreamInterface* local_stream) override;
|
||||
void RemoveStream(MediaStreamInterface* local_stream) override;
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> AddTrack(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids) override;
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> AddTrack(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids,
|
||||
const std::vector<RtpEncodingParameters>& init_send_encodings) override;
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> AddTrack(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids,
|
||||
const std::vector<RtpEncodingParameters>* init_send_encodings);
|
||||
RTCError RemoveTrackOrError(
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender) override;
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track) override;
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const RtpTransceiverInit& init) override;
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
|
||||
cricket::MediaType media_type) override;
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
|
||||
cricket::MediaType media_type,
|
||||
const RtpTransceiverInit& init) override;
|
||||
|
||||
rtc::scoped_refptr<RtpSenderInterface> CreateSender(
|
||||
const std::string& kind,
|
||||
const std::string& stream_id) override;
|
||||
|
||||
std::vector<rtc::scoped_refptr<RtpSenderInterface>> GetSenders()
|
||||
const override;
|
||||
std::vector<rtc::scoped_refptr<RtpReceiverInterface>> GetReceivers()
|
||||
const override;
|
||||
std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> GetTransceivers()
|
||||
const override;
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>> CreateDataChannelOrError(
|
||||
const std::string& label,
|
||||
const DataChannelInit* config) override;
|
||||
// WARNING: LEGACY. See peerconnectioninterface.h
|
||||
bool GetStats(StatsObserver* observer,
|
||||
MediaStreamTrackInterface* track,
|
||||
StatsOutputLevel level) override;
|
||||
// Spec-complaint GetStats(). See peerconnectioninterface.h
|
||||
void GetStats(RTCStatsCollectorCallback* callback) override;
|
||||
void GetStats(
|
||||
rtc::scoped_refptr<RtpSenderInterface> selector,
|
||||
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) override;
|
||||
void GetStats(
|
||||
rtc::scoped_refptr<RtpReceiverInterface> selector,
|
||||
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) override;
|
||||
void ClearStatsCache() override;
|
||||
|
||||
SignalingState signaling_state() override;
|
||||
|
||||
IceConnectionState ice_connection_state() override;
|
||||
IceConnectionState ice_connection_state_internal() override {
|
||||
return ice_connection_state();
|
||||
}
|
||||
IceConnectionState standardized_ice_connection_state() override;
|
||||
PeerConnectionState peer_connection_state() override;
|
||||
IceGatheringState ice_gathering_state() override;
|
||||
absl::optional<bool> can_trickle_ice_candidates() override;
|
||||
|
||||
const SessionDescriptionInterface* local_description() const override;
|
||||
const SessionDescriptionInterface* remote_description() const override;
|
||||
const SessionDescriptionInterface* current_local_description() const override;
|
||||
const SessionDescriptionInterface* current_remote_description()
|
||||
const override;
|
||||
const SessionDescriptionInterface* pending_local_description() const override;
|
||||
const SessionDescriptionInterface* pending_remote_description()
|
||||
const override;
|
||||
|
||||
void RestartIce() override;
|
||||
|
||||
// JSEP01
|
||||
void CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
const RTCOfferAnswerOptions& options) override;
|
||||
void CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
const RTCOfferAnswerOptions& options) override;
|
||||
|
||||
void SetLocalDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface> desc,
|
||||
rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer)
|
||||
override;
|
||||
void SetLocalDescription(
|
||||
rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer)
|
||||
override;
|
||||
// TODO(https://crbug.com/webrtc/11798): Delete these methods in favor of the
|
||||
// ones taking SetLocalDescriptionObserverInterface as argument.
|
||||
void SetLocalDescription(SetSessionDescriptionObserver* observer,
|
||||
SessionDescriptionInterface* desc) override;
|
||||
void SetLocalDescription(SetSessionDescriptionObserver* observer) override;
|
||||
|
||||
void SetRemoteDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface> desc,
|
||||
rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer)
|
||||
override;
|
||||
// TODO(https://crbug.com/webrtc/11798): Delete this methods in favor of the
|
||||
// ones taking SetRemoteDescriptionObserverInterface as argument.
|
||||
void SetRemoteDescription(SetSessionDescriptionObserver* observer,
|
||||
SessionDescriptionInterface* desc) override;
|
||||
|
||||
PeerConnectionInterface::RTCConfiguration GetConfiguration() override;
|
||||
RTCError SetConfiguration(
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration) override;
|
||||
bool AddIceCandidate(const IceCandidateInterface* candidate) override;
|
||||
void AddIceCandidate(std::unique_ptr<IceCandidateInterface> candidate,
|
||||
std::function<void(RTCError)> callback) override;
|
||||
bool RemoveIceCandidates(
|
||||
const std::vector<cricket::Candidate>& candidates) override;
|
||||
|
||||
RTCError SetBitrate(const BitrateSettings& bitrate) override;
|
||||
void ReconfigureBandwidthEstimation(
|
||||
const BandwidthEstimationSettings& settings) override;
|
||||
|
||||
void SetAudioPlayout(bool playout) override;
|
||||
void SetAudioRecording(bool recording) override;
|
||||
|
||||
rtc::scoped_refptr<DtlsTransportInterface> LookupDtlsTransportByMid(
|
||||
const std::string& mid) override;
|
||||
rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMidInternal(
|
||||
const std::string& mid);
|
||||
|
||||
rtc::scoped_refptr<SctpTransportInterface> GetSctpTransport() const override;
|
||||
|
||||
void AddAdaptationResource(rtc::scoped_refptr<Resource> resource) override;
|
||||
|
||||
bool StartRtcEventLog(std::unique_ptr<RtcEventLogOutput> output,
|
||||
int64_t output_period_ms) override;
|
||||
bool StartRtcEventLog(std::unique_ptr<RtcEventLogOutput> output) override;
|
||||
void StopRtcEventLog() override;
|
||||
|
||||
void Close() override;
|
||||
|
||||
rtc::Thread* signaling_thread() const final {
|
||||
return context_->signaling_thread();
|
||||
}
|
||||
|
||||
rtc::Thread* network_thread() const final {
|
||||
return context_->network_thread();
|
||||
}
|
||||
rtc::Thread* worker_thread() const final { return context_->worker_thread(); }
|
||||
|
||||
std::string session_id() const override { return session_id_; }
|
||||
|
||||
bool initial_offerer() const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return sdp_handler_->initial_offerer();
|
||||
}
|
||||
|
||||
std::vector<
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
||||
GetTransceiversInternal() const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
if (!ConfiguredForMedia()) {
|
||||
return {};
|
||||
}
|
||||
return rtp_manager()->transceivers()->List();
|
||||
}
|
||||
|
||||
std::vector<DataChannelStats> GetDataChannelStats() const override;
|
||||
|
||||
absl::optional<std::string> sctp_transport_name() const override;
|
||||
absl::optional<std::string> sctp_mid() const override;
|
||||
|
||||
cricket::CandidateStatsList GetPooledCandidateStats() const override;
|
||||
std::map<std::string, cricket::TransportStats> GetTransportStatsByNames(
|
||||
const std::set<std::string>& transport_names) override;
|
||||
Call::Stats GetCallStats() override;
|
||||
|
||||
absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() override;
|
||||
|
||||
bool GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override;
|
||||
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain(
|
||||
const std::string& transport_name) override;
|
||||
bool IceRestartPending(const std::string& content_name) const override;
|
||||
bool NeedsIceRestart(const std::string& content_name) const override;
|
||||
bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) override;
|
||||
|
||||
// Functions needed by DataChannelController
|
||||
void NoteDataAddedEvent() override { NoteUsageEvent(UsageEvent::DATA_ADDED); }
|
||||
// Returns the observer. Will crash on CHECK if the observer is removed.
|
||||
PeerConnectionObserver* Observer() const override;
|
||||
bool IsClosed() const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return !sdp_handler_ ||
|
||||
sdp_handler_->signaling_state() == PeerConnectionInterface::kClosed;
|
||||
}
|
||||
// Get current SSL role used by SCTP's underlying transport.
|
||||
absl::optional<rtc::SSLRole> GetSctpSslRole_n() override;
|
||||
|
||||
void OnSctpDataChannelStateChanged(
|
||||
int channel_id,
|
||||
DataChannelInterface::DataState state) override;
|
||||
|
||||
bool ShouldFireNegotiationNeededEvent(uint32_t event_id) override;
|
||||
|
||||
// Functions needed by SdpOfferAnswerHandler
|
||||
LegacyStatsCollector* legacy_stats() override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return legacy_stats_.get();
|
||||
}
|
||||
DataChannelController* data_channel_controller() override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return &data_channel_controller_;
|
||||
}
|
||||
bool dtls_enabled() const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return dtls_enabled_;
|
||||
}
|
||||
const PeerConnectionInterface::RTCConfiguration* configuration()
|
||||
const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return &configuration_;
|
||||
}
|
||||
PeerConnectionMessageHandler* message_handler() override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return &message_handler_;
|
||||
}
|
||||
|
||||
RtpTransmissionManager* rtp_manager() override { return rtp_manager_.get(); }
|
||||
const RtpTransmissionManager* rtp_manager() const override {
|
||||
return rtp_manager_.get();
|
||||
}
|
||||
|
||||
JsepTransportController* transport_controller_s() override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return transport_controller_copy_;
|
||||
}
|
||||
JsepTransportController* transport_controller_n() override {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
return transport_controller_.get();
|
||||
}
|
||||
cricket::PortAllocator* port_allocator() override {
|
||||
return port_allocator_.get();
|
||||
}
|
||||
Call* call_ptr() override { return call_ptr_; }
|
||||
|
||||
ConnectionContext* context() { return context_.get(); }
|
||||
const PeerConnectionFactoryInterface::Options* options() const override {
|
||||
return &options_;
|
||||
}
|
||||
void SetIceConnectionState(IceConnectionState new_state) override;
|
||||
void NoteUsageEvent(UsageEvent event) override;
|
||||
|
||||
// Asynchronously adds a remote candidate on the network thread.
|
||||
void AddRemoteCandidate(const std::string& mid,
|
||||
const cricket::Candidate& candidate) override;
|
||||
|
||||
// Report the UMA metric BundleUsage for the given remote description.
|
||||
void ReportSdpBundleUsage(
|
||||
const SessionDescriptionInterface& remote_description) override;
|
||||
|
||||
// Report several UMA metrics on establishing the connection.
|
||||
void ReportFirstConnectUsageMetrics() RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Returns true if the PeerConnection is configured to use Unified Plan
|
||||
// semantics for creating offers/answers and setting local/remote
|
||||
// descriptions. If this is true the RtpTransceiver API will also be available
|
||||
// to the user. If this is false, Plan B semantics are assumed.
|
||||
// TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once
|
||||
// sufficient time has passed.
|
||||
bool IsUnifiedPlan() const override {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return is_unified_plan_;
|
||||
}
|
||||
bool ValidateBundleSettings(
|
||||
const cricket::SessionDescription* desc,
|
||||
const std::map<std::string, const cricket::ContentGroup*>&
|
||||
bundle_groups_by_mid) override;
|
||||
|
||||
bool CreateDataChannelTransport(absl::string_view mid) override;
|
||||
void DestroyDataChannelTransport(RTCError error) override;
|
||||
|
||||
// Asynchronously calls SctpTransport::Start() on the network thread for
|
||||
// `sctp_mid()` if set. Called as part of setting the local description.
|
||||
void StartSctpTransport(int local_port,
|
||||
int remote_port,
|
||||
int max_message_size) override;
|
||||
|
||||
// Returns the CryptoOptions for this PeerConnection. This will always
|
||||
// return the RTCConfiguration.crypto_options if set and will only default
|
||||
// back to the PeerConnectionFactory settings if nothing was set.
|
||||
CryptoOptions GetCryptoOptions() override;
|
||||
|
||||
// Internal implementation for AddTransceiver family of methods. If
|
||||
// `fire_callback` is set, fires OnRenegotiationNeeded callback if successful.
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
|
||||
cricket::MediaType media_type,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const RtpTransceiverInit& init,
|
||||
bool fire_callback = true) override;
|
||||
|
||||
// Returns true if SRTP (either using DTLS-SRTP or SDES) is required by
|
||||
// this session.
|
||||
bool SrtpRequired() const override;
|
||||
|
||||
absl::optional<std::string> SetupDataChannelTransport_n(absl::string_view mid)
|
||||
RTC_RUN_ON(network_thread());
|
||||
void TeardownDataChannelTransport_n(RTCError error)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
const FieldTrialsView& trials() const override { return env_.field_trials(); }
|
||||
|
||||
bool ConfiguredForMedia() const;
|
||||
|
||||
// Functions made public for testing.
|
||||
void ReturnHistogramVeryQuicklyForTesting() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return_histogram_very_quickly_ = true;
|
||||
}
|
||||
void RequestUsagePatternReportForTesting();
|
||||
|
||||
protected:
|
||||
// Available for rtc::scoped_refptr creation
|
||||
PeerConnection(const Environment& env,
|
||||
rtc::scoped_refptr<ConnectionContext> context,
|
||||
const PeerConnectionFactoryInterface::Options& options,
|
||||
bool is_unified_plan,
|
||||
std::unique_ptr<Call> call,
|
||||
PeerConnectionDependencies& dependencies,
|
||||
bool dtls_enabled);
|
||||
|
||||
~PeerConnection() override;
|
||||
|
||||
private:
|
||||
RTCError Initialize(
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration,
|
||||
PeerConnectionDependencies dependencies);
|
||||
JsepTransportController* InitializeTransportController_n(
|
||||
const RTCConfiguration& configuration,
|
||||
const PeerConnectionDependencies& dependencies)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||
FindTransceiverBySender(rtc::scoped_refptr<RtpSenderInterface> sender)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
void SetStandardizedIceConnectionState(
|
||||
PeerConnectionInterface::IceConnectionState new_state)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void SetConnectionState(
|
||||
PeerConnectionInterface::PeerConnectionState new_state)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// Called any time the IceGatheringState changes.
|
||||
void OnIceGatheringChange(IceGatheringState new_state)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
// New ICE candidate has been gathered.
|
||||
void OnIceCandidate(std::unique_ptr<IceCandidateInterface> candidate)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
// Gathering of an ICE candidate failed.
|
||||
void OnIceCandidateError(const std::string& address,
|
||||
int port,
|
||||
const std::string& url,
|
||||
int error_code,
|
||||
const std::string& error_text)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
// Some local ICE candidates have been removed.
|
||||
void OnIceCandidatesRemoved(const std::vector<cricket::Candidate>& candidates)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
void OnSelectedCandidatePairChanged(
|
||||
const cricket::CandidatePairChangeEvent& event)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
void OnNegotiationNeeded();
|
||||
|
||||
// Called when first configuring the port allocator.
|
||||
struct InitializePortAllocatorResult {
|
||||
bool enable_ipv6;
|
||||
};
|
||||
InitializePortAllocatorResult InitializePortAllocator_n(
|
||||
const cricket::ServerAddresses& stun_servers,
|
||||
const std::vector<cricket::RelayServerConfig>& turn_servers,
|
||||
const RTCConfiguration& configuration);
|
||||
// Called when SetConfiguration is called to apply the supported subset
|
||||
// of the configuration on the network thread.
|
||||
bool ReconfigurePortAllocator_n(
|
||||
const cricket::ServerAddresses& stun_servers,
|
||||
const std::vector<cricket::RelayServerConfig>& turn_servers,
|
||||
IceTransportsType type,
|
||||
int candidate_pool_size,
|
||||
PortPrunePolicy turn_port_prune_policy,
|
||||
TurnCustomizer* turn_customizer,
|
||||
absl::optional<int> stun_candidate_keepalive_interval,
|
||||
bool have_local_description);
|
||||
|
||||
// Starts output of an RTC event log to the given output object.
|
||||
// This function should only be called from the worker thread.
|
||||
bool StartRtcEventLog_w(std::unique_ptr<RtcEventLogOutput> output,
|
||||
int64_t output_period_ms);
|
||||
|
||||
// Stops recording an RTC event log.
|
||||
// This function should only be called from the worker thread.
|
||||
void StopRtcEventLog_w();
|
||||
|
||||
// Returns true and the TransportInfo of the given `content_name`
|
||||
// from `description`. Returns false if it's not available.
|
||||
static bool GetTransportDescription(
|
||||
const cricket::SessionDescription* description,
|
||||
const std::string& content_name,
|
||||
cricket::TransportDescription* info);
|
||||
|
||||
// Returns the media index for a local ice candidate given the content name.
|
||||
// Returns false if the local session description does not have a media
|
||||
// content called `content_name`.
|
||||
bool GetLocalCandidateMediaIndex(const std::string& content_name,
|
||||
int* sdp_mline_index)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
// JsepTransportController signal handlers.
|
||||
void OnTransportControllerConnectionState(cricket::IceConnectionState state)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerGatheringState(cricket::IceGatheringState state)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerCandidatesGathered(
|
||||
const std::string& transport_name,
|
||||
const std::vector<cricket::Candidate>& candidates)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerCandidateError(
|
||||
const cricket::IceCandidateErrorEvent& event)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerCandidatesRemoved(
|
||||
const std::vector<cricket::Candidate>& candidates)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerCandidateChanged(
|
||||
const cricket::CandidatePairChangeEvent& event)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
|
||||
|
||||
// Invoked when TransportController connection completion is signaled.
|
||||
// Reports stats for all transports in use.
|
||||
void ReportTransportStats(std::vector<RtpTransceiverProxyRefPtr> transceivers)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
// Gather the usage of IPv4/IPv6 as best connection.
|
||||
static void ReportBestConnectionState(const cricket::TransportStats& stats);
|
||||
|
||||
static void ReportNegotiatedCiphers(
|
||||
bool dtls_enabled,
|
||||
const cricket::TransportStats& stats,
|
||||
const std::set<cricket::MediaType>& media_types);
|
||||
void ReportIceCandidateCollected(const cricket::Candidate& candidate)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
|
||||
void ReportUsagePattern() const RTC_RUN_ON(signaling_thread());
|
||||
|
||||
void ReportRemoteIceCandidateAdded(const cricket::Candidate& candidate);
|
||||
|
||||
// JsepTransportController::Observer override.
|
||||
//
|
||||
// Called by `transport_controller_` when processing transport information
|
||||
// from a session description, and the mapping from m= sections to transports
|
||||
// changed (as a result of BUNDLE negotiation, or m= sections being
|
||||
// rejected).
|
||||
bool OnTransportChanged(
|
||||
const std::string& mid,
|
||||
RtpTransportInternal* rtp_transport,
|
||||
rtc::scoped_refptr<DtlsTransport> dtls_transport,
|
||||
DataChannelTransportInterface* data_channel_transport) override;
|
||||
|
||||
void SetSctpTransportName(std::string sctp_transport_name);
|
||||
|
||||
std::function<void(const rtc::CopyOnWriteBuffer& packet,
|
||||
int64_t packet_time_us)>
|
||||
InitializeRtcpCallback();
|
||||
|
||||
std::function<void(const RtpPacketReceived& parsed_packet)>
|
||||
InitializeUnDemuxablePacketHandler();
|
||||
|
||||
const Environment env_;
|
||||
const rtc::scoped_refptr<ConnectionContext> context_;
|
||||
const PeerConnectionFactoryInterface::Options options_;
|
||||
PeerConnectionObserver* observer_ RTC_GUARDED_BY(signaling_thread()) =
|
||||
nullptr;
|
||||
|
||||
const bool is_unified_plan_;
|
||||
|
||||
IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) =
|
||||
kIceConnectionNew;
|
||||
PeerConnectionInterface::IceConnectionState standardized_ice_connection_state_
|
||||
RTC_GUARDED_BY(signaling_thread()) = kIceConnectionNew;
|
||||
PeerConnectionInterface::PeerConnectionState connection_state_
|
||||
RTC_GUARDED_BY(signaling_thread()) = PeerConnectionState::kNew;
|
||||
|
||||
IceGatheringState ice_gathering_state_ RTC_GUARDED_BY(signaling_thread()) =
|
||||
kIceGatheringNew;
|
||||
PeerConnectionInterface::RTCConfiguration configuration_
|
||||
RTC_GUARDED_BY(signaling_thread());
|
||||
|
||||
const std::unique_ptr<AsyncDnsResolverFactoryInterface>
|
||||
async_dns_resolver_factory_;
|
||||
std::unique_ptr<cricket::PortAllocator>
|
||||
port_allocator_; // TODO(bugs.webrtc.org/9987): Accessed on both
|
||||
// signaling and network thread.
|
||||
const std::unique_ptr<IceTransportFactory>
|
||||
ice_transport_factory_; // TODO(bugs.webrtc.org/9987): Accessed on the
|
||||
// signaling thread but the underlying raw
|
||||
// pointer is given to
|
||||
// `jsep_transport_controller_` and used on the
|
||||
// network thread.
|
||||
const std::unique_ptr<rtc::SSLCertificateVerifier> tls_cert_verifier_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
|
||||
// The unique_ptr belongs to the worker thread, but the Call object manages
|
||||
// its own thread safety.
|
||||
std::unique_ptr<Call> call_ RTC_GUARDED_BY(worker_thread());
|
||||
ScopedTaskSafety signaling_thread_safety_;
|
||||
rtc::scoped_refptr<PendingTaskSafetyFlag> network_thread_safety_;
|
||||
rtc::scoped_refptr<PendingTaskSafetyFlag> worker_thread_safety_;
|
||||
|
||||
// Points to the same thing as `call_`. Since it's const, we may read the
|
||||
// pointer from any thread.
|
||||
// TODO(bugs.webrtc.org/11992): Remove this workaround (and potential dangling
|
||||
// pointer).
|
||||
Call* const call_ptr_;
|
||||
|
||||
std::unique_ptr<LegacyStatsCollector> legacy_stats_
|
||||
RTC_GUARDED_BY(signaling_thread()); // A pointer is passed to senders_
|
||||
rtc::scoped_refptr<RTCStatsCollector> stats_collector_
|
||||
RTC_GUARDED_BY(signaling_thread());
|
||||
|
||||
const std::string session_id_;
|
||||
|
||||
// The transport controller is set and used on the network thread.
|
||||
// Some functions pass the value of the transport_controller_ pointer
|
||||
// around as arguments while running on the signaling thread; these
|
||||
// use the transport_controller_copy.
|
||||
std::unique_ptr<JsepTransportController> transport_controller_
|
||||
RTC_GUARDED_BY(network_thread());
|
||||
JsepTransportController* transport_controller_copy_
|
||||
RTC_GUARDED_BY(signaling_thread()) = nullptr;
|
||||
|
||||
// `sctp_mid_` is the content name (MID) in SDP.
|
||||
// Note: this is used as the data channel MID by both SCTP and data channel
|
||||
// transports. It is set when either transport is initialized and unset when
|
||||
// both transports are deleted.
|
||||
// There is one copy on the signaling thread and another copy on the
|
||||
// networking thread. Changes are always initiated from the signaling
|
||||
// thread, but applied first on the networking thread via an invoke().
|
||||
absl::optional<std::string> sctp_mid_s_ RTC_GUARDED_BY(signaling_thread());
|
||||
absl::optional<std::string> sctp_mid_n_ RTC_GUARDED_BY(network_thread());
|
||||
std::string sctp_transport_name_s_ RTC_GUARDED_BY(signaling_thread());
|
||||
|
||||
// The machinery for handling offers and answers. Const after initialization.
|
||||
std::unique_ptr<SdpOfferAnswerHandler> sdp_handler_
|
||||
RTC_GUARDED_BY(signaling_thread()) RTC_PT_GUARDED_BY(signaling_thread());
|
||||
|
||||
const bool dtls_enabled_;
|
||||
|
||||
UsagePattern usage_pattern_ RTC_GUARDED_BY(signaling_thread());
|
||||
bool return_histogram_very_quickly_ RTC_GUARDED_BY(signaling_thread()) =
|
||||
false;
|
||||
|
||||
// The DataChannelController is accessed from both the signaling thread
|
||||
// and networking thread. It is a thread-aware object.
|
||||
DataChannelController data_channel_controller_;
|
||||
|
||||
// Machinery for handling messages posted to oneself
|
||||
PeerConnectionMessageHandler message_handler_
|
||||
RTC_GUARDED_BY(signaling_thread());
|
||||
|
||||
// Administration of senders, receivers and transceivers
|
||||
// Accessed on both signaling and network thread. Const after Initialize().
|
||||
std::unique_ptr<RtpTransmissionManager> rtp_manager_;
|
||||
|
||||
// Did the connectionState ever change to `connected`?
|
||||
// Used to gather metrics only the first such state change.
|
||||
bool was_ever_connected_ RTC_GUARDED_BY(signaling_thread()) = false;
|
||||
|
||||
// This variable needs to be the last one in the class.
|
||||
rtc::WeakPtrFactory<PeerConnection> weak_factory_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_H_
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2020 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 <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/video_source_interface.h"
|
||||
#include "call/adaptation/test/fake_resource.h"
|
||||
#include "pc/test/fake_periodic_video_source.h"
|
||||
#include "pc/test/fake_periodic_video_track_source.h"
|
||||
#include "pc/test/peer_connection_test_wrapper.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "rtc_base/virtual_socket_server.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int64_t kDefaultTimeoutMs = 5000;
|
||||
|
||||
struct TrackWithPeriodicSource {
|
||||
rtc::scoped_refptr<VideoTrackInterface> track;
|
||||
rtc::scoped_refptr<FakePeriodicVideoTrackSource> periodic_track_source;
|
||||
};
|
||||
|
||||
// Performs an O/A exchange and waits until the signaling state is stable again.
|
||||
void Negotiate(rtc::scoped_refptr<PeerConnectionTestWrapper> caller,
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> callee) {
|
||||
// Wire up callbacks and listeners such that a full O/A is performed in
|
||||
// response to CreateOffer().
|
||||
PeerConnectionTestWrapper::Connect(caller.get(), callee.get());
|
||||
caller->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
caller->WaitForNegotiation();
|
||||
}
|
||||
|
||||
TrackWithPeriodicSource CreateTrackWithPeriodicSource(
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory) {
|
||||
FakePeriodicVideoSource::Config periodic_track_source_config;
|
||||
periodic_track_source_config.frame_interval_ms = 100;
|
||||
periodic_track_source_config.timestamp_offset_ms = rtc::TimeMillis();
|
||||
rtc::scoped_refptr<FakePeriodicVideoTrackSource> periodic_track_source =
|
||||
rtc::make_ref_counted<FakePeriodicVideoTrackSource>(
|
||||
periodic_track_source_config, /* remote */ false);
|
||||
TrackWithPeriodicSource track_with_source;
|
||||
track_with_source.track =
|
||||
factory->CreateVideoTrack(periodic_track_source, "PeriodicTrack");
|
||||
track_with_source.periodic_track_source = periodic_track_source;
|
||||
return track_with_source;
|
||||
}
|
||||
|
||||
// Triggers overuse and obtains VideoSinkWants. Adaptation processing happens in
|
||||
// parallel and this function makes no guarantee that the returnd VideoSinkWants
|
||||
// have yet to reflect the overuse signal. Used together with EXPECT_TRUE_WAIT
|
||||
// to "spam overuse until a change is observed".
|
||||
rtc::VideoSinkWants TriggerOveruseAndGetSinkWants(
|
||||
rtc::scoped_refptr<FakeResource> fake_resource,
|
||||
const FakePeriodicVideoSource& source) {
|
||||
fake_resource->SetUsageState(ResourceUsageState::kOveruse);
|
||||
return source.wants();
|
||||
}
|
||||
|
||||
class PeerConnectionAdaptationIntegrationTest : public ::testing::Test {
|
||||
public:
|
||||
PeerConnectionAdaptationIntegrationTest()
|
||||
: virtual_socket_server_(),
|
||||
network_thread_(new rtc::Thread(&virtual_socket_server_)),
|
||||
worker_thread_(rtc::Thread::Create()) {
|
||||
RTC_CHECK(network_thread_->Start());
|
||||
RTC_CHECK(worker_thread_->Start());
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> CreatePcWrapper(
|
||||
const char* name) {
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper =
|
||||
rtc::make_ref_counted<PeerConnectionTestWrapper>(
|
||||
name, &virtual_socket_server_, network_thread_.get(),
|
||||
worker_thread_.get());
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
EXPECT_TRUE(pc_wrapper->CreatePc(config, CreateBuiltinAudioEncoderFactory(),
|
||||
CreateBuiltinAudioDecoderFactory()));
|
||||
return pc_wrapper;
|
||||
}
|
||||
|
||||
protected:
|
||||
rtc::VirtualSocketServer virtual_socket_server_;
|
||||
std::unique_ptr<rtc::Thread> network_thread_;
|
||||
std::unique_ptr<rtc::Thread> worker_thread_;
|
||||
};
|
||||
|
||||
TEST_F(PeerConnectionAdaptationIntegrationTest,
|
||||
ResouceInjectedAfterNegotiationCausesReductionInResolution) {
|
||||
auto caller_wrapper = CreatePcWrapper("caller");
|
||||
auto caller = caller_wrapper->pc();
|
||||
auto callee_wrapper = CreatePcWrapper("callee");
|
||||
|
||||
// Adding a track and negotiating ensures that a VideoSendStream exists.
|
||||
TrackWithPeriodicSource track_with_source =
|
||||
CreateTrackWithPeriodicSource(caller_wrapper->pc_factory());
|
||||
auto sender = caller->AddTrack(track_with_source.track, {}).value();
|
||||
Negotiate(caller_wrapper, callee_wrapper);
|
||||
// Prefer degrading resolution.
|
||||
auto parameters = sender->GetParameters();
|
||||
parameters.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE;
|
||||
sender->SetParameters(parameters);
|
||||
|
||||
const auto& source =
|
||||
track_with_source.periodic_track_source->fake_periodic_source();
|
||||
int pixel_count_before_overuse = source.wants().max_pixel_count;
|
||||
|
||||
// Inject a fake resource and spam kOveruse until resolution becomes limited.
|
||||
auto fake_resource = FakeResource::Create("FakeResource");
|
||||
caller->AddAdaptationResource(fake_resource);
|
||||
EXPECT_TRUE_WAIT(
|
||||
TriggerOveruseAndGetSinkWants(fake_resource, source).max_pixel_count <
|
||||
pixel_count_before_overuse,
|
||||
kDefaultTimeoutMs);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionAdaptationIntegrationTest,
|
||||
ResouceInjectedBeforeNegotiationCausesReductionInResolution) {
|
||||
auto caller_wrapper = CreatePcWrapper("caller");
|
||||
auto caller = caller_wrapper->pc();
|
||||
auto callee_wrapper = CreatePcWrapper("callee");
|
||||
|
||||
// Inject a fake resource before adding any tracks or negotiating.
|
||||
auto fake_resource = FakeResource::Create("FakeResource");
|
||||
caller->AddAdaptationResource(fake_resource);
|
||||
|
||||
// Adding a track and negotiating ensures that a VideoSendStream exists.
|
||||
TrackWithPeriodicSource track_with_source =
|
||||
CreateTrackWithPeriodicSource(caller_wrapper->pc_factory());
|
||||
auto sender = caller->AddTrack(track_with_source.track, {}).value();
|
||||
Negotiate(caller_wrapper, callee_wrapper);
|
||||
// Prefer degrading resolution.
|
||||
auto parameters = sender->GetParameters();
|
||||
parameters.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE;
|
||||
sender->SetParameters(parameters);
|
||||
|
||||
const auto& source =
|
||||
track_with_source.periodic_track_source->fake_periodic_source();
|
||||
int pixel_count_before_overuse = source.wants().max_pixel_count;
|
||||
|
||||
// Spam kOveruse until resolution becomes limited.
|
||||
EXPECT_TRUE_WAIT(
|
||||
TriggerOveruseAndGetSinkWants(fake_resource, source).max_pixel_count <
|
||||
pixel_count_before_overuse,
|
||||
kDefaultTimeoutMs);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
1074
TMessagesProj/jni/voip/webrtc/pc/peer_connection_bundle_unittest.cc
Normal file
1074
TMessagesProj/jni/voip/webrtc/pc/peer_connection_bundle_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
* 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 <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/create_peerconnection_factory.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "modules/audio_processing/include/audio_processing.h"
|
||||
#include "p2p/base/fake_port_allocator.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/media_protocol_names.h"
|
||||
#include "pc/media_session.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/sdp_utils.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/android_test_initializer.h"
|
||||
#endif
|
||||
#include "pc/test/fake_audio_capture_module.h"
|
||||
#include "pc/test/fake_rtc_certificate_generator.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/virtual_socket_server.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
||||
using ::testing::Combine;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Values;
|
||||
|
||||
constexpr int kGenerateCertTimeout = 1000;
|
||||
|
||||
class PeerConnectionCryptoBaseTest : public ::testing::Test {
|
||||
protected:
|
||||
typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
|
||||
|
||||
explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
|
||||
: vss_(new rtc::VirtualSocketServer()),
|
||||
main_(vss_.get()),
|
||||
sdp_semantics_(sdp_semantics) {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
pc_factory_ = CreatePeerConnectionFactory(
|
||||
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
|
||||
FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
|
||||
CreateBuiltinAudioDecoderFactory(),
|
||||
std::make_unique<VideoEncoderFactoryTemplate<
|
||||
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
|
||||
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>(),
|
||||
std::make_unique<VideoDecoderFactoryTemplate<
|
||||
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
|
||||
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
|
||||
nullptr /* audio_mixer */, nullptr /* audio_processing */);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection() {
|
||||
return CreatePeerConnection(RTCConfiguration());
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
||||
return CreatePeerConnection(config, nullptr);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(
|
||||
const RTCConfiguration& config,
|
||||
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
|
||||
auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
|
||||
rtc::Thread::Current(),
|
||||
std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()),
|
||||
&field_trials_);
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
RTCConfiguration modified_config = config;
|
||||
modified_config.sdp_semantics = sdp_semantics_;
|
||||
PeerConnectionDependencies pc_dependencies(observer.get());
|
||||
pc_dependencies.allocator = std::move(fake_port_allocator);
|
||||
pc_dependencies.cert_generator = std::move(cert_gen);
|
||||
auto result = pc_factory_->CreatePeerConnectionOrError(
|
||||
modified_config, std::move(pc_dependencies));
|
||||
if (!result.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
return std::make_unique<PeerConnectionWrapper>(
|
||||
pc_factory_, result.MoveValue(), std::move(observer));
|
||||
}
|
||||
|
||||
// Accepts the same arguments as CreatePeerConnection and adds default audio
|
||||
// and video tracks.
|
||||
template <typename... Args>
|
||||
WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
|
||||
auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
|
||||
if (!wrapper) {
|
||||
return nullptr;
|
||||
}
|
||||
wrapper->AddAudioTrack("a");
|
||||
wrapper->AddVideoTrack("v");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
cricket::ConnectionRole& AudioConnectionRole(
|
||||
cricket::SessionDescription* desc) {
|
||||
return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
|
||||
}
|
||||
|
||||
cricket::ConnectionRole& VideoConnectionRole(
|
||||
cricket::SessionDescription* desc) {
|
||||
return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
|
||||
}
|
||||
|
||||
cricket::ConnectionRole& ConnectionRoleFromContent(
|
||||
cricket::SessionDescription* desc,
|
||||
cricket::ContentInfo* content) {
|
||||
RTC_DCHECK(content);
|
||||
auto* transport_info = desc->GetTransportInfoByName(content->name);
|
||||
RTC_DCHECK(transport_info);
|
||||
return transport_info->description.connection_role;
|
||||
}
|
||||
|
||||
test::ScopedKeyValueConfig field_trials_;
|
||||
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
||||
rtc::AutoSocketServerThread main_;
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
|
||||
const SdpSemantics sdp_semantics_;
|
||||
};
|
||||
|
||||
SdpContentPredicate HaveDtlsFingerprint() {
|
||||
return [](const cricket::ContentInfo* content,
|
||||
const cricket::TransportInfo* transport) {
|
||||
return transport->description.identity_fingerprint != nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
SdpContentPredicate HaveProtocol(const std::string& protocol) {
|
||||
return [protocol](const cricket::ContentInfo* content,
|
||||
const cricket::TransportInfo* transport) {
|
||||
return content->media_description()->protocol() == protocol;
|
||||
};
|
||||
}
|
||||
|
||||
class PeerConnectionCryptoTest
|
||||
: public PeerConnectionCryptoBaseTest,
|
||||
public ::testing::WithParamInterface<SdpSemantics> {
|
||||
protected:
|
||||
PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
|
||||
};
|
||||
|
||||
SdpContentMutator RemoveDtlsFingerprint() {
|
||||
return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
|
||||
transport->description.identity_fingerprint.reset();
|
||||
};
|
||||
}
|
||||
|
||||
// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint
|
||||
TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
ASSERT_TRUE(offer);
|
||||
|
||||
ASSERT_FALSE(offer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
|
||||
offer->description()));
|
||||
}
|
||||
TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
callee->SetRemoteDescription(caller->CreateOffer());
|
||||
auto answer = callee->CreateAnswer();
|
||||
ASSERT_TRUE(answer);
|
||||
|
||||
ASSERT_FALSE(answer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
|
||||
answer->description()));
|
||||
}
|
||||
|
||||
// The following group tests that two PeerConnections can successfully exchange
|
||||
// an offer/answer when DTLS is on and that they will refuse any offer/answer
|
||||
// applied locally/remotely if it does not include a DTLS fingerprint.
|
||||
TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
ASSERT_TRUE(offer);
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
ASSERT_TRUE(answer);
|
||||
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
||||
}
|
||||
TEST_P(PeerConnectionCryptoTest,
|
||||
FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
|
||||
|
||||
EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
|
||||
}
|
||||
TEST_P(PeerConnectionCryptoTest,
|
||||
FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
|
||||
|
||||
EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
|
||||
}
|
||||
TEST_P(PeerConnectionCryptoTest,
|
||||
FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
|
||||
auto answer = callee->CreateAnswer();
|
||||
SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
|
||||
}
|
||||
TEST_P(PeerConnectionCryptoTest,
|
||||
FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(config);
|
||||
|
||||
callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
|
||||
|
||||
EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
|
||||
}
|
||||
|
||||
// Tests that a DTLS call can be established when the certificate is specified
|
||||
// in the PeerConnection config and no certificate generator is specified.
|
||||
TEST_P(PeerConnectionCryptoTest,
|
||||
ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
|
||||
RTCConfiguration caller_config;
|
||||
caller_config.certificates.push_back(
|
||||
FakeRTCCertificateGenerator::GenerateCertificate());
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
|
||||
|
||||
RTCConfiguration callee_config;
|
||||
callee_config.certificates.push_back(
|
||||
FakeRTCCertificateGenerator::GenerateCertificate());
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
|
||||
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
ASSERT_TRUE(offer);
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
ASSERT_TRUE(answer);
|
||||
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
||||
}
|
||||
|
||||
// The following parameterized test verifies that CreateOffer/CreateAnswer
|
||||
// returns successfully (or with failure if the underlying certificate generator
|
||||
// fails) no matter when the DTLS certificate is generated. If multiple
|
||||
// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
|
||||
// they all finish after the certificate is generated.
|
||||
|
||||
// Whether the certificate will be generated before calling CreateOffer or
|
||||
// while CreateOffer is executing.
|
||||
enum class CertGenTime { kBefore, kDuring };
|
||||
std::ostream& operator<<(std::ostream& out, CertGenTime value) {
|
||||
switch (value) {
|
||||
case CertGenTime::kBefore:
|
||||
return out << "before";
|
||||
case CertGenTime::kDuring:
|
||||
return out << "during";
|
||||
default:
|
||||
return out << "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Whether the fake certificate generator will produce a certificate or fail.
|
||||
enum class CertGenResult { kSucceed, kFail };
|
||||
std::ostream& operator<<(std::ostream& out, CertGenResult value) {
|
||||
switch (value) {
|
||||
case CertGenResult::kSucceed:
|
||||
return out << "succeed";
|
||||
case CertGenResult::kFail:
|
||||
return out << "fail";
|
||||
default:
|
||||
return out << "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
class PeerConnectionCryptoDtlsCertGenTest
|
||||
: public PeerConnectionCryptoBaseTest,
|
||||
public ::testing::WithParamInterface<std::tuple<SdpSemantics,
|
||||
SdpType,
|
||||
CertGenTime,
|
||||
CertGenResult,
|
||||
size_t>> {
|
||||
protected:
|
||||
PeerConnectionCryptoDtlsCertGenTest()
|
||||
: PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
|
||||
sdp_type_ = std::get<1>(GetParam());
|
||||
cert_gen_time_ = std::get<2>(GetParam());
|
||||
cert_gen_result_ = std::get<3>(GetParam());
|
||||
concurrent_calls_ = std::get<4>(GetParam());
|
||||
}
|
||||
|
||||
SdpType sdp_type_;
|
||||
CertGenTime cert_gen_time_;
|
||||
CertGenResult cert_gen_result_;
|
||||
size_t concurrent_calls_;
|
||||
};
|
||||
|
||||
TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
|
||||
RTCConfiguration config;
|
||||
auto owned_fake_certificate_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
auto* fake_certificate_generator = owned_fake_certificate_generator.get();
|
||||
fake_certificate_generator->set_should_fail(cert_gen_result_ ==
|
||||
CertGenResult::kFail);
|
||||
fake_certificate_generator->set_should_wait(cert_gen_time_ ==
|
||||
CertGenTime::kDuring);
|
||||
WrapperPtr pc;
|
||||
if (sdp_type_ == SdpType::kOffer) {
|
||||
pc = CreatePeerConnectionWithAudioVideo(
|
||||
config, std::move(owned_fake_certificate_generator));
|
||||
} else {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
pc = CreatePeerConnectionWithAudioVideo(
|
||||
config, std::move(owned_fake_certificate_generator));
|
||||
pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
|
||||
}
|
||||
if (cert_gen_time_ == CertGenTime::kBefore) {
|
||||
ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
|
||||
fake_certificate_generator->generated_failures() >
|
||||
0,
|
||||
kGenerateCertTimeout);
|
||||
} else {
|
||||
ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
|
||||
fake_certificate_generator->set_should_wait(false);
|
||||
}
|
||||
std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
|
||||
observers;
|
||||
for (size_t i = 0; i < concurrent_calls_; i++) {
|
||||
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
|
||||
rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
|
||||
observers.push_back(observer);
|
||||
if (sdp_type_ == SdpType::kOffer) {
|
||||
pc->pc()->CreateOffer(observer.get(),
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
} else {
|
||||
pc->pc()->CreateAnswer(observer.get(),
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
}
|
||||
}
|
||||
for (auto& observer : observers) {
|
||||
EXPECT_TRUE_WAIT(observer->called(), 1000);
|
||||
if (cert_gen_result_ == CertGenResult::kSucceed) {
|
||||
EXPECT_TRUE(observer->result());
|
||||
} else {
|
||||
EXPECT_FALSE(observer->result());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
PeerConnectionCryptoTest,
|
||||
PeerConnectionCryptoDtlsCertGenTest,
|
||||
Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
|
||||
Values(SdpType::kOffer, SdpType::kAnswer),
|
||||
Values(CertGenTime::kBefore, CertGenTime::kDuring),
|
||||
Values(CertGenResult::kSucceed, CertGenResult::kFail),
|
||||
Values(1, 3)));
|
||||
|
||||
// Test that we can create and set an answer correctly when different
|
||||
// SSL roles have been negotiated for different transports.
|
||||
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
|
||||
TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
RTCOfferAnswerOptions options_no_bundle;
|
||||
options_no_bundle.use_rtp_mux = false;
|
||||
|
||||
// First, negotiate different SSL roles for audio and video.
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
auto answer = callee->CreateAnswer(options_no_bundle);
|
||||
|
||||
AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
|
||||
VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
|
||||
|
||||
ASSERT_TRUE(
|
||||
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
||||
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
||||
|
||||
// Now create an offer in the reverse direction, and ensure the initial
|
||||
// offerer responds with an answer with the correct SSL roles.
|
||||
ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
|
||||
answer = caller->CreateAnswer(options_no_bundle);
|
||||
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
|
||||
AudioConnectionRole(answer->description()));
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
|
||||
VideoConnectionRole(answer->description()));
|
||||
|
||||
ASSERT_TRUE(
|
||||
caller->SetLocalDescription(CloneSessionDescription(answer.get())));
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
|
||||
|
||||
// Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
|
||||
// audio is transferred over to video in the answer that completes the BUNDLE
|
||||
// negotiation.
|
||||
RTCOfferAnswerOptions options_bundle;
|
||||
options_bundle.use_rtp_mux = true;
|
||||
|
||||
ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
|
||||
answer = caller->CreateAnswer(options_bundle);
|
||||
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
|
||||
AudioConnectionRole(answer->description()));
|
||||
EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
|
||||
VideoConnectionRole(answer->description()));
|
||||
|
||||
ASSERT_TRUE(
|
||||
caller->SetLocalDescription(CloneSessionDescription(answer.get())));
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
|
||||
}
|
||||
|
||||
// Tests that if the DTLS fingerprint is invalid then all future calls to
|
||||
// SetLocalDescription and SetRemoteDescription will fail due to a session
|
||||
// error.
|
||||
// This is a regression test for crbug.com/800775
|
||||
TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
|
||||
auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
|
||||
auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
RTCConfiguration callee_config;
|
||||
callee_config.certificates.push_back(callee_certificate);
|
||||
auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
|
||||
// Create an invalid answer with the other certificate's fingerprint.
|
||||
auto valid_answer = callee->CreateAnswer();
|
||||
auto invalid_answer = CloneSessionDescription(valid_answer.get());
|
||||
auto* audio_content =
|
||||
cricket::GetFirstAudioContent(invalid_answer->description());
|
||||
ASSERT_TRUE(audio_content);
|
||||
auto* audio_transport_info =
|
||||
invalid_answer->description()->GetTransportInfoByName(
|
||||
audio_content->name);
|
||||
ASSERT_TRUE(audio_transport_info);
|
||||
audio_transport_info->description.identity_fingerprint =
|
||||
rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
|
||||
|
||||
// Set the invalid answer and expect a fingerprint error.
|
||||
std::string error;
|
||||
ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
|
||||
EXPECT_THAT(error, HasSubstr("Local fingerprint does not match identity."));
|
||||
|
||||
// Make sure that setting a valid remote offer or local answer also fails now.
|
||||
ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
|
||||
EXPECT_THAT(error, HasSubstr("Session error code: ERROR_CONTENT."));
|
||||
ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
|
||||
EXPECT_THAT(error, HasSubstr("Session error code: ERROR_CONTENT."));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
|
||||
PeerConnectionCryptoTest,
|
||||
Values(SdpSemantics::kPlanB_DEPRECATED,
|
||||
SdpSemantics::kUnifiedPlan));
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* 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 <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sctp_transport_interface.h"
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/transport/sctp_transport_factory_interface.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "pc/media_session.h"
|
||||
#include "pc/peer_connection.h"
|
||||
#include "pc/peer_connection_proxy.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/sctp_transport.h"
|
||||
#include "pc/sdp_utils.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/test/enable_fake_media.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/android_test_initializer.h"
|
||||
#endif
|
||||
#include "rtc_base/virtual_socket_server.h"
|
||||
#include "test/pc/sctp/fake_sctp_transport.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Not;
|
||||
using ::testing::Values;
|
||||
|
||||
namespace {
|
||||
|
||||
PeerConnectionFactoryDependencies CreatePeerConnectionFactoryDependencies() {
|
||||
PeerConnectionFactoryDependencies deps;
|
||||
deps.network_thread = rtc::Thread::Current();
|
||||
deps.worker_thread = rtc::Thread::Current();
|
||||
deps.signaling_thread = rtc::Thread::Current();
|
||||
deps.task_queue_factory = CreateDefaultTaskQueueFactory();
|
||||
EnableFakeMedia(deps);
|
||||
deps.sctp_factory = std::make_unique<FakeSctpTransportFactory>();
|
||||
return deps;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class PeerConnectionWrapperForDataChannelTest : public PeerConnectionWrapper {
|
||||
public:
|
||||
using PeerConnectionWrapper::PeerConnectionWrapper;
|
||||
|
||||
FakeSctpTransportFactory* sctp_transport_factory() {
|
||||
return sctp_transport_factory_;
|
||||
}
|
||||
|
||||
void set_sctp_transport_factory(
|
||||
FakeSctpTransportFactory* sctp_transport_factory) {
|
||||
sctp_transport_factory_ = sctp_transport_factory;
|
||||
}
|
||||
|
||||
absl::optional<std::string> sctp_mid() {
|
||||
return GetInternalPeerConnection()->sctp_mid();
|
||||
}
|
||||
|
||||
absl::optional<std::string> sctp_transport_name() {
|
||||
return GetInternalPeerConnection()->sctp_transport_name();
|
||||
}
|
||||
|
||||
PeerConnection* GetInternalPeerConnection() {
|
||||
auto* pci =
|
||||
static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
|
||||
pc());
|
||||
return static_cast<PeerConnection*>(pci->internal());
|
||||
}
|
||||
|
||||
private:
|
||||
FakeSctpTransportFactory* sctp_transport_factory_ = nullptr;
|
||||
};
|
||||
|
||||
class PeerConnectionDataChannelBaseTest : public ::testing::Test {
|
||||
protected:
|
||||
typedef std::unique_ptr<PeerConnectionWrapperForDataChannelTest> WrapperPtr;
|
||||
|
||||
explicit PeerConnectionDataChannelBaseTest(SdpSemantics sdp_semantics)
|
||||
: vss_(new rtc::VirtualSocketServer()),
|
||||
main_(vss_.get()),
|
||||
sdp_semantics_(sdp_semantics) {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection() {
|
||||
return CreatePeerConnection(RTCConfiguration());
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
||||
return CreatePeerConnection(config,
|
||||
PeerConnectionFactoryInterface::Options());
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(
|
||||
const RTCConfiguration& config,
|
||||
const PeerConnectionFactoryInterface::Options factory_options) {
|
||||
auto factory_deps = CreatePeerConnectionFactoryDependencies();
|
||||
FakeSctpTransportFactory* fake_sctp_transport_factory =
|
||||
static_cast<FakeSctpTransportFactory*>(factory_deps.sctp_factory.get());
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory =
|
||||
CreateModularPeerConnectionFactory(std::move(factory_deps));
|
||||
pc_factory->SetOptions(factory_options);
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
RTCConfiguration modified_config = config;
|
||||
modified_config.sdp_semantics = sdp_semantics_;
|
||||
auto result = pc_factory->CreatePeerConnectionOrError(
|
||||
modified_config, PeerConnectionDependencies(observer.get()));
|
||||
if (!result.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
auto wrapper = std::make_unique<PeerConnectionWrapperForDataChannelTest>(
|
||||
pc_factory, result.MoveValue(), std::move(observer));
|
||||
wrapper->set_sctp_transport_factory(fake_sctp_transport_factory);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Accepts the same arguments as CreatePeerConnection and adds a default data
|
||||
// channel.
|
||||
template <typename... Args>
|
||||
WrapperPtr CreatePeerConnectionWithDataChannel(Args&&... args) {
|
||||
auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
|
||||
if (!wrapper) {
|
||||
return nullptr;
|
||||
}
|
||||
EXPECT_TRUE(wrapper->pc()->CreateDataChannelOrError("dc", nullptr).ok());
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Changes the SCTP data channel port on the given session description.
|
||||
void ChangeSctpPortOnDescription(cricket::SessionDescription* desc,
|
||||
int port) {
|
||||
auto* data_content = cricket::GetFirstDataContent(desc);
|
||||
RTC_DCHECK(data_content);
|
||||
auto* data_desc = data_content->media_description()->as_sctp();
|
||||
RTC_DCHECK(data_desc);
|
||||
data_desc->set_port(port);
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
||||
rtc::AutoSocketServerThread main_;
|
||||
const SdpSemantics sdp_semantics_;
|
||||
};
|
||||
|
||||
class PeerConnectionDataChannelTest
|
||||
: public PeerConnectionDataChannelBaseTest,
|
||||
public ::testing::WithParamInterface<SdpSemantics> {
|
||||
protected:
|
||||
PeerConnectionDataChannelTest()
|
||||
: PeerConnectionDataChannelBaseTest(GetParam()) {}
|
||||
};
|
||||
|
||||
class PeerConnectionDataChannelUnifiedPlanTest
|
||||
: public PeerConnectionDataChannelBaseTest {
|
||||
protected:
|
||||
PeerConnectionDataChannelUnifiedPlanTest()
|
||||
: PeerConnectionDataChannelBaseTest(SdpSemantics::kUnifiedPlan) {}
|
||||
};
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest, InternalSctpTransportDeletedOnTeardown) {
|
||||
auto caller = CreatePeerConnectionWithDataChannel();
|
||||
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
EXPECT_TRUE(caller->sctp_transport_factory()->last_fake_sctp_transport());
|
||||
|
||||
rtc::scoped_refptr<SctpTransportInterface> sctp_transport =
|
||||
caller->GetInternalPeerConnection()->GetSctpTransport();
|
||||
|
||||
caller.reset();
|
||||
EXPECT_EQ(static_cast<SctpTransport*>(sctp_transport.get())->internal(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Test that sctp_mid/sctp_transport_name (used for stats) are correct
|
||||
// before and after BUNDLE is negotiated.
|
||||
TEST_P(PeerConnectionDataChannelTest, SctpContentAndTransportNameSetCorrectly) {
|
||||
auto caller = CreatePeerConnection();
|
||||
auto callee = CreatePeerConnection();
|
||||
|
||||
// Initially these fields should be empty.
|
||||
EXPECT_FALSE(caller->sctp_mid());
|
||||
EXPECT_FALSE(caller->sctp_transport_name());
|
||||
|
||||
// Create offer with audio/video/data.
|
||||
// Default bundle policy is "balanced", so data should be using its own
|
||||
// transport.
|
||||
caller->AddAudioTrack("a");
|
||||
caller->AddVideoTrack("v");
|
||||
caller->pc()->CreateDataChannelOrError("dc", nullptr);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
const auto& offer_contents = offer->description()->contents();
|
||||
ASSERT_EQ(cricket::MEDIA_TYPE_AUDIO,
|
||||
offer_contents[0].media_description()->type());
|
||||
std::string audio_mid = offer_contents[0].name;
|
||||
ASSERT_EQ(cricket::MEDIA_TYPE_DATA,
|
||||
offer_contents[2].media_description()->type());
|
||||
std::string data_mid = offer_contents[2].name;
|
||||
|
||||
ASSERT_TRUE(
|
||||
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
ASSERT_TRUE(caller->sctp_mid());
|
||||
EXPECT_EQ(data_mid, *caller->sctp_mid());
|
||||
ASSERT_TRUE(caller->sctp_transport_name());
|
||||
EXPECT_EQ(data_mid, *caller->sctp_transport_name());
|
||||
|
||||
// Create answer that finishes BUNDLE negotiation, which means everything
|
||||
// should be bundled on the first transport (audio).
|
||||
RTCOfferAnswerOptions options;
|
||||
options.use_rtp_mux = true;
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
ASSERT_TRUE(caller->sctp_mid());
|
||||
EXPECT_EQ(data_mid, *caller->sctp_mid());
|
||||
ASSERT_TRUE(caller->sctp_transport_name());
|
||||
EXPECT_EQ(audio_mid, *caller->sctp_transport_name());
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest,
|
||||
CreateOfferWithNoDataChannelsGivesNoDataSection) {
|
||||
auto caller = CreatePeerConnection();
|
||||
auto offer = caller->CreateOffer();
|
||||
|
||||
EXPECT_FALSE(offer->description()->GetContentByName(cricket::CN_DATA));
|
||||
EXPECT_FALSE(offer->description()->GetTransportInfoByName(cricket::CN_DATA));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest,
|
||||
CreateAnswerWithRemoteSctpDataChannelIncludesDataSection) {
|
||||
auto caller = CreatePeerConnectionWithDataChannel();
|
||||
auto callee = CreatePeerConnection();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
|
||||
auto answer = callee->CreateAnswer();
|
||||
ASSERT_TRUE(answer);
|
||||
auto* data_content = cricket::GetFirstDataContent(answer->description());
|
||||
ASSERT_TRUE(data_content);
|
||||
EXPECT_FALSE(data_content->rejected);
|
||||
EXPECT_TRUE(
|
||||
answer->description()->GetTransportInfoByName(data_content->name));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest, SctpPortPropagatedFromSdpToTransport) {
|
||||
constexpr int kNewSendPort = 9998;
|
||||
constexpr int kNewRecvPort = 7775;
|
||||
|
||||
auto caller = CreatePeerConnectionWithDataChannel();
|
||||
auto callee = CreatePeerConnectionWithDataChannel();
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
ChangeSctpPortOnDescription(offer->description(), kNewSendPort);
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
auto answer = callee->CreateAnswer();
|
||||
ChangeSctpPortOnDescription(answer->description(), kNewRecvPort);
|
||||
std::string sdp;
|
||||
answer->ToString(&sdp);
|
||||
ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
|
||||
auto* callee_transport =
|
||||
callee->sctp_transport_factory()->last_fake_sctp_transport();
|
||||
ASSERT_TRUE(callee_transport);
|
||||
EXPECT_EQ(kNewSendPort, callee_transport->remote_port());
|
||||
EXPECT_EQ(kNewRecvPort, callee_transport->local_port());
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest, ModernSdpSyntaxByDefault) {
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions options;
|
||||
auto caller = CreatePeerConnectionWithDataChannel();
|
||||
auto offer = caller->CreateOffer(options);
|
||||
EXPECT_FALSE(cricket::GetFirstSctpDataContentDescription(offer->description())
|
||||
->use_sctpmap());
|
||||
std::string sdp;
|
||||
offer->ToString(&sdp);
|
||||
RTC_LOG(LS_ERROR) << sdp;
|
||||
EXPECT_THAT(sdp, HasSubstr(" UDP/DTLS/SCTP webrtc-datachannel"));
|
||||
EXPECT_THAT(sdp, Not(HasSubstr("a=sctpmap:")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionDataChannelTest, ObsoleteSdpSyntaxIfSet) {
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions options;
|
||||
options.use_obsolete_sctp_sdp = true;
|
||||
auto caller = CreatePeerConnectionWithDataChannel();
|
||||
auto offer = caller->CreateOffer(options);
|
||||
EXPECT_TRUE(cricket::GetFirstSctpDataContentDescription(offer->description())
|
||||
->use_sctpmap());
|
||||
std::string sdp;
|
||||
offer->ToString(&sdp);
|
||||
EXPECT_THAT(sdp, Not(HasSubstr(" UDP/DTLS/SCTP webrtc-datachannel")));
|
||||
EXPECT_THAT(sdp, HasSubstr("a=sctpmap:"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PeerConnectionDataChannelTest,
|
||||
PeerConnectionDataChannelTest,
|
||||
Values(SdpSemantics::kPlanB_DEPRECATED,
|
||||
SdpSemantics::kUnifiedPlan));
|
||||
|
||||
} // namespace webrtc
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,765 @@
|
|||
/*
|
||||
* Copyright 2013 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 <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/audio_codecs/L16/audio_decoder_L16.h"
|
||||
#include "api/audio_codecs/L16/audio_encoder_L16.h"
|
||||
#include "api/audio_codecs/audio_codec_pair_id.h"
|
||||
#include "api/audio_codecs/audio_decoder.h"
|
||||
#include "api/audio_codecs/audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/audio_decoder_factory_template.h"
|
||||
#include "api/audio_codecs/audio_encoder.h"
|
||||
#include "api/audio_codecs/audio_encoder_factory.h"
|
||||
#include "api/audio_codecs/audio_encoder_factory_template.h"
|
||||
#include "api/audio_codecs/audio_format.h"
|
||||
#include "api/audio_codecs/opus_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/opus_audio_encoder_factory.h"
|
||||
#include "api/audio_options.h"
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "media/sctp/sctp_transport_internal.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/physical_socket_server.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/android_test_initializer.h"
|
||||
#endif
|
||||
#include "pc/test/peer_connection_test_wrapper.h"
|
||||
// Notice that mockpeerconnectionobservers.h must be included after the above!
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "test/mock_audio_decoder.h"
|
||||
#include "test/mock_audio_decoder_factory.h"
|
||||
#include "test/mock_audio_encoder_factory.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::StrictMock;
|
||||
using ::testing::Values;
|
||||
|
||||
using webrtc::DataChannelInterface;
|
||||
using webrtc::MediaStreamInterface;
|
||||
using webrtc::PeerConnectionInterface;
|
||||
using webrtc::SdpSemantics;
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxWait = 25000;
|
||||
|
||||
} // namespace
|
||||
|
||||
class PeerConnectionEndToEndBaseTest : public sigslot::has_slots<>,
|
||||
public ::testing::Test {
|
||||
public:
|
||||
typedef std::vector<rtc::scoped_refptr<DataChannelInterface>> DataChannelList;
|
||||
|
||||
explicit PeerConnectionEndToEndBaseTest(SdpSemantics sdp_semantics)
|
||||
: network_thread_(std::make_unique<rtc::Thread>(&pss_)),
|
||||
worker_thread_(rtc::Thread::Create()) {
|
||||
RTC_CHECK(network_thread_->Start());
|
||||
RTC_CHECK(worker_thread_->Start());
|
||||
caller_ = rtc::make_ref_counted<PeerConnectionTestWrapper>(
|
||||
"caller", &pss_, network_thread_.get(), worker_thread_.get());
|
||||
callee_ = rtc::make_ref_counted<PeerConnectionTestWrapper>(
|
||||
"callee", &pss_, network_thread_.get(), worker_thread_.get());
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = "stun:stun.l.google.com:19302";
|
||||
config_.servers.push_back(ice_server);
|
||||
config_.sdp_semantics = sdp_semantics;
|
||||
|
||||
#ifdef WEBRTC_ANDROID
|
||||
webrtc::InitializeAndroidObjects();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CreatePcs(
|
||||
rtc::scoped_refptr<webrtc::AudioEncoderFactory> audio_encoder_factory1,
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> audio_decoder_factory1,
|
||||
rtc::scoped_refptr<webrtc::AudioEncoderFactory> audio_encoder_factory2,
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> audio_decoder_factory2) {
|
||||
EXPECT_TRUE(caller_->CreatePc(config_, audio_encoder_factory1,
|
||||
audio_decoder_factory1));
|
||||
EXPECT_TRUE(callee_->CreatePc(config_, audio_encoder_factory2,
|
||||
audio_decoder_factory2));
|
||||
PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get());
|
||||
|
||||
caller_->SignalOnDataChannel.connect(
|
||||
this, &PeerConnectionEndToEndBaseTest::OnCallerAddedDataChanel);
|
||||
callee_->SignalOnDataChannel.connect(
|
||||
this, &PeerConnectionEndToEndBaseTest::OnCalleeAddedDataChannel);
|
||||
}
|
||||
|
||||
void CreatePcs(
|
||||
rtc::scoped_refptr<webrtc::AudioEncoderFactory> audio_encoder_factory,
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> audio_decoder_factory) {
|
||||
CreatePcs(audio_encoder_factory, audio_decoder_factory,
|
||||
audio_encoder_factory, audio_decoder_factory);
|
||||
}
|
||||
|
||||
void GetAndAddUserMedia() {
|
||||
cricket::AudioOptions audio_options;
|
||||
GetAndAddUserMedia(true, audio_options, true);
|
||||
}
|
||||
|
||||
void GetAndAddUserMedia(bool audio,
|
||||
const cricket::AudioOptions& audio_options,
|
||||
bool video) {
|
||||
caller_->GetAndAddUserMedia(audio, audio_options, video);
|
||||
callee_->GetAndAddUserMedia(audio, audio_options, video);
|
||||
}
|
||||
|
||||
void Negotiate() {
|
||||
caller_->CreateOffer(
|
||||
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
void WaitForCallEstablished() {
|
||||
caller_->WaitForCallEstablished();
|
||||
callee_->WaitForCallEstablished();
|
||||
}
|
||||
|
||||
void WaitForConnection() {
|
||||
caller_->WaitForConnection();
|
||||
callee_->WaitForConnection();
|
||||
}
|
||||
|
||||
void OnCallerAddedDataChanel(DataChannelInterface* dc) {
|
||||
caller_signaled_data_channels_.push_back(
|
||||
rtc::scoped_refptr<DataChannelInterface>(dc));
|
||||
}
|
||||
|
||||
void OnCalleeAddedDataChannel(DataChannelInterface* dc) {
|
||||
callee_signaled_data_channels_.push_back(
|
||||
rtc::scoped_refptr<DataChannelInterface>(dc));
|
||||
}
|
||||
|
||||
// Tests that `dc1` and `dc2` can send to and receive from each other.
|
||||
void TestDataChannelSendAndReceive(DataChannelInterface* dc1,
|
||||
DataChannelInterface* dc2,
|
||||
size_t size = 6) {
|
||||
std::unique_ptr<webrtc::MockDataChannelObserver> dc1_observer(
|
||||
new webrtc::MockDataChannelObserver(dc1));
|
||||
|
||||
std::unique_ptr<webrtc::MockDataChannelObserver> dc2_observer(
|
||||
new webrtc::MockDataChannelObserver(dc2));
|
||||
|
||||
static const std::string kDummyData =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
webrtc::DataBuffer buffer("");
|
||||
|
||||
size_t sizeLeft = size;
|
||||
while (sizeLeft > 0) {
|
||||
size_t chunkSize =
|
||||
sizeLeft > kDummyData.length() ? kDummyData.length() : sizeLeft;
|
||||
buffer.data.AppendData(kDummyData.data(), chunkSize);
|
||||
sizeLeft -= chunkSize;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(dc1->Send(buffer));
|
||||
EXPECT_EQ_WAIT(buffer.data,
|
||||
rtc::CopyOnWriteBuffer(dc2_observer->last_message()),
|
||||
kMaxWait);
|
||||
|
||||
EXPECT_TRUE(dc2->Send(buffer));
|
||||
EXPECT_EQ_WAIT(buffer.data,
|
||||
rtc::CopyOnWriteBuffer(dc1_observer->last_message()),
|
||||
kMaxWait);
|
||||
|
||||
EXPECT_EQ(1U, dc1_observer->received_message_count());
|
||||
EXPECT_EQ(size, dc1_observer->last_message().length());
|
||||
EXPECT_EQ(1U, dc2_observer->received_message_count());
|
||||
EXPECT_EQ(size, dc2_observer->last_message().length());
|
||||
}
|
||||
|
||||
void WaitForDataChannelsToOpen(DataChannelInterface* local_dc,
|
||||
const DataChannelList& remote_dc_list,
|
||||
size_t remote_dc_index) {
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, local_dc->state(), kMaxWait);
|
||||
|
||||
ASSERT_TRUE_WAIT(remote_dc_list.size() > remote_dc_index, kMaxWait);
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kOpen,
|
||||
remote_dc_list[remote_dc_index]->state(), kMaxWait);
|
||||
EXPECT_EQ(local_dc->id(), remote_dc_list[remote_dc_index]->id());
|
||||
}
|
||||
|
||||
void CloseDataChannels(DataChannelInterface* local_dc,
|
||||
const DataChannelList& remote_dc_list,
|
||||
size_t remote_dc_index) {
|
||||
local_dc->Close();
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kClosed, local_dc->state(), kMaxWait);
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kClosed,
|
||||
remote_dc_list[remote_dc_index]->state(), kMaxWait);
|
||||
}
|
||||
|
||||
protected:
|
||||
rtc::AutoThread main_thread_;
|
||||
rtc::PhysicalSocketServer pss_;
|
||||
std::unique_ptr<rtc::Thread> network_thread_;
|
||||
std::unique_ptr<rtc::Thread> worker_thread_;
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> caller_;
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> callee_;
|
||||
DataChannelList caller_signaled_data_channels_;
|
||||
DataChannelList callee_signaled_data_channels_;
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration config_;
|
||||
};
|
||||
|
||||
class PeerConnectionEndToEndTest
|
||||
: public PeerConnectionEndToEndBaseTest,
|
||||
public ::testing::WithParamInterface<SdpSemantics> {
|
||||
protected:
|
||||
PeerConnectionEndToEndTest() : PeerConnectionEndToEndBaseTest(GetParam()) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<webrtc::AudioDecoder> CreateForwardingMockDecoder(
|
||||
std::unique_ptr<webrtc::AudioDecoder> real_decoder) {
|
||||
class ForwardingMockDecoder : public StrictMock<webrtc::MockAudioDecoder> {
|
||||
public:
|
||||
explicit ForwardingMockDecoder(std::unique_ptr<AudioDecoder> decoder)
|
||||
: decoder_(std::move(decoder)) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioDecoder> decoder_;
|
||||
};
|
||||
|
||||
const auto dec = real_decoder.get(); // For lambda capturing.
|
||||
auto mock_decoder =
|
||||
std::make_unique<ForwardingMockDecoder>(std::move(real_decoder));
|
||||
EXPECT_CALL(*mock_decoder, Channels())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Invoke([dec] { return dec->Channels(); }));
|
||||
EXPECT_CALL(*mock_decoder, DecodeInternal(_, _, _, _, _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(
|
||||
Invoke([dec](const uint8_t* encoded, size_t encoded_len,
|
||||
int sample_rate_hz, int16_t* decoded,
|
||||
webrtc::AudioDecoder::SpeechType* speech_type) {
|
||||
return dec->Decode(encoded, encoded_len, sample_rate_hz,
|
||||
std::numeric_limits<size_t>::max(), decoded,
|
||||
speech_type);
|
||||
}));
|
||||
EXPECT_CALL(*mock_decoder, Die());
|
||||
EXPECT_CALL(*mock_decoder, HasDecodePlc()).WillRepeatedly(Invoke([dec] {
|
||||
return dec->HasDecodePlc();
|
||||
}));
|
||||
EXPECT_CALL(*mock_decoder, PacketDuration(_, _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Invoke([dec](const uint8_t* encoded, size_t encoded_len) {
|
||||
return dec->PacketDuration(encoded, encoded_len);
|
||||
}));
|
||||
EXPECT_CALL(*mock_decoder, SampleRateHz())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Invoke([dec] { return dec->SampleRateHz(); }));
|
||||
|
||||
return std::move(mock_decoder);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory>
|
||||
CreateForwardingMockDecoderFactory(
|
||||
webrtc::AudioDecoderFactory* real_decoder_factory) {
|
||||
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> mock_decoder_factory =
|
||||
rtc::make_ref_counted<StrictMock<webrtc::MockAudioDecoderFactory>>();
|
||||
EXPECT_CALL(*mock_decoder_factory, GetSupportedDecoders())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Invoke([real_decoder_factory] {
|
||||
return real_decoder_factory->GetSupportedDecoders();
|
||||
}));
|
||||
EXPECT_CALL(*mock_decoder_factory, IsSupportedDecoder(_))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(
|
||||
Invoke([real_decoder_factory](const webrtc::SdpAudioFormat& format) {
|
||||
return real_decoder_factory->IsSupportedDecoder(format);
|
||||
}));
|
||||
EXPECT_CALL(*mock_decoder_factory, MakeAudioDecoderMock(_, _, _))
|
||||
.Times(AtLeast(2))
|
||||
.WillRepeatedly(
|
||||
Invoke([real_decoder_factory](
|
||||
const webrtc::SdpAudioFormat& format,
|
||||
absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
|
||||
std::unique_ptr<webrtc::AudioDecoder>* return_value) {
|
||||
auto real_decoder =
|
||||
real_decoder_factory->MakeAudioDecoder(format, codec_pair_id);
|
||||
*return_value =
|
||||
real_decoder
|
||||
? CreateForwardingMockDecoder(std::move(real_decoder))
|
||||
: nullptr;
|
||||
}));
|
||||
return mock_decoder_factory;
|
||||
}
|
||||
|
||||
struct AudioEncoderUnicornSparklesRainbow {
|
||||
using Config = webrtc::AudioEncoderL16::Config;
|
||||
static absl::optional<Config> SdpToConfig(webrtc::SdpAudioFormat format) {
|
||||
if (absl::EqualsIgnoreCase(format.name, "UnicornSparklesRainbow")) {
|
||||
const webrtc::CodecParameterMap expected_params = {{"num_horns", "1"}};
|
||||
EXPECT_EQ(expected_params, format.parameters);
|
||||
format.parameters.clear();
|
||||
format.name = "L16";
|
||||
return webrtc::AudioEncoderL16::SdpToConfig(format);
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
static void AppendSupportedEncoders(
|
||||
std::vector<webrtc::AudioCodecSpec>* specs) {
|
||||
std::vector<webrtc::AudioCodecSpec> new_specs;
|
||||
webrtc::AudioEncoderL16::AppendSupportedEncoders(&new_specs);
|
||||
for (auto& spec : new_specs) {
|
||||
spec.format.name = "UnicornSparklesRainbow";
|
||||
EXPECT_TRUE(spec.format.parameters.empty());
|
||||
spec.format.parameters.emplace("num_horns", "1");
|
||||
specs->push_back(spec);
|
||||
}
|
||||
}
|
||||
static webrtc::AudioCodecInfo QueryAudioEncoder(const Config& config) {
|
||||
return webrtc::AudioEncoderL16::QueryAudioEncoder(config);
|
||||
}
|
||||
static std::unique_ptr<webrtc::AudioEncoder> MakeAudioEncoder(
|
||||
const Config& config,
|
||||
int payload_type,
|
||||
absl::optional<webrtc::AudioCodecPairId> codec_pair_id = absl::nullopt) {
|
||||
return webrtc::AudioEncoderL16::MakeAudioEncoder(config, payload_type,
|
||||
codec_pair_id);
|
||||
}
|
||||
};
|
||||
|
||||
struct AudioDecoderUnicornSparklesRainbow {
|
||||
using Config = webrtc::AudioDecoderL16::Config;
|
||||
static absl::optional<Config> SdpToConfig(webrtc::SdpAudioFormat format) {
|
||||
if (absl::EqualsIgnoreCase(format.name, "UnicornSparklesRainbow")) {
|
||||
const webrtc::CodecParameterMap expected_params = {{"num_horns", "1"}};
|
||||
EXPECT_EQ(expected_params, format.parameters);
|
||||
format.parameters.clear();
|
||||
format.name = "L16";
|
||||
return webrtc::AudioDecoderL16::SdpToConfig(format);
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
static void AppendSupportedDecoders(
|
||||
std::vector<webrtc::AudioCodecSpec>* specs) {
|
||||
std::vector<webrtc::AudioCodecSpec> new_specs;
|
||||
webrtc::AudioDecoderL16::AppendSupportedDecoders(&new_specs);
|
||||
for (auto& spec : new_specs) {
|
||||
spec.format.name = "UnicornSparklesRainbow";
|
||||
EXPECT_TRUE(spec.format.parameters.empty());
|
||||
spec.format.parameters.emplace("num_horns", "1");
|
||||
specs->push_back(spec);
|
||||
}
|
||||
}
|
||||
static std::unique_ptr<webrtc::AudioDecoder> MakeAudioDecoder(
|
||||
const Config& config,
|
||||
absl::optional<webrtc::AudioCodecPairId> codec_pair_id = absl::nullopt) {
|
||||
return webrtc::AudioDecoderL16::MakeAudioDecoder(config, codec_pair_id);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(PeerConnectionEndToEndTest, Call) {
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> real_decoder_factory =
|
||||
webrtc::CreateOpusAudioDecoderFactory();
|
||||
CreatePcs(webrtc::CreateOpusAudioEncoderFactory(),
|
||||
CreateForwardingMockDecoderFactory(real_decoder_factory.get()));
|
||||
GetAndAddUserMedia();
|
||||
Negotiate();
|
||||
WaitForCallEstablished();
|
||||
}
|
||||
|
||||
#if defined(IS_FUCHSIA)
|
||||
TEST_P(PeerConnectionEndToEndTest, CallWithSdesKeyNegotiation) {
|
||||
config_.enable_dtls_srtp = false;
|
||||
CreatePcs(webrtc::CreateOpusAudioEncoderFactory(),
|
||||
webrtc::CreateOpusAudioDecoderFactory());
|
||||
GetAndAddUserMedia();
|
||||
Negotiate();
|
||||
WaitForCallEstablished();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(PeerConnectionEndToEndTest, CallWithCustomCodec) {
|
||||
class IdLoggingAudioEncoderFactory : public webrtc::AudioEncoderFactory {
|
||||
public:
|
||||
IdLoggingAudioEncoderFactory(
|
||||
rtc::scoped_refptr<AudioEncoderFactory> real_factory,
|
||||
std::vector<webrtc::AudioCodecPairId>* const codec_ids)
|
||||
: fact_(real_factory), codec_ids_(codec_ids) {}
|
||||
std::vector<webrtc::AudioCodecSpec> GetSupportedEncoders() override {
|
||||
return fact_->GetSupportedEncoders();
|
||||
}
|
||||
absl::optional<webrtc::AudioCodecInfo> QueryAudioEncoder(
|
||||
const webrtc::SdpAudioFormat& format) override {
|
||||
return fact_->QueryAudioEncoder(format);
|
||||
}
|
||||
std::unique_ptr<webrtc::AudioEncoder> MakeAudioEncoder(
|
||||
int payload_type,
|
||||
const webrtc::SdpAudioFormat& format,
|
||||
absl::optional<webrtc::AudioCodecPairId> codec_pair_id) override {
|
||||
EXPECT_TRUE(codec_pair_id.has_value());
|
||||
codec_ids_->push_back(*codec_pair_id);
|
||||
return fact_->MakeAudioEncoder(payload_type, format, codec_pair_id);
|
||||
}
|
||||
|
||||
private:
|
||||
const rtc::scoped_refptr<webrtc::AudioEncoderFactory> fact_;
|
||||
std::vector<webrtc::AudioCodecPairId>* const codec_ids_;
|
||||
};
|
||||
|
||||
class IdLoggingAudioDecoderFactory : public webrtc::AudioDecoderFactory {
|
||||
public:
|
||||
IdLoggingAudioDecoderFactory(
|
||||
rtc::scoped_refptr<AudioDecoderFactory> real_factory,
|
||||
std::vector<webrtc::AudioCodecPairId>* const codec_ids)
|
||||
: fact_(real_factory), codec_ids_(codec_ids) {}
|
||||
std::vector<webrtc::AudioCodecSpec> GetSupportedDecoders() override {
|
||||
return fact_->GetSupportedDecoders();
|
||||
}
|
||||
bool IsSupportedDecoder(const webrtc::SdpAudioFormat& format) override {
|
||||
return fact_->IsSupportedDecoder(format);
|
||||
}
|
||||
std::unique_ptr<webrtc::AudioDecoder> MakeAudioDecoder(
|
||||
const webrtc::SdpAudioFormat& format,
|
||||
absl::optional<webrtc::AudioCodecPairId> codec_pair_id) override {
|
||||
EXPECT_TRUE(codec_pair_id.has_value());
|
||||
codec_ids_->push_back(*codec_pair_id);
|
||||
return fact_->MakeAudioDecoder(format, codec_pair_id);
|
||||
}
|
||||
|
||||
private:
|
||||
const rtc::scoped_refptr<webrtc::AudioDecoderFactory> fact_;
|
||||
std::vector<webrtc::AudioCodecPairId>* const codec_ids_;
|
||||
};
|
||||
|
||||
std::vector<webrtc::AudioCodecPairId> encoder_id1, encoder_id2, decoder_id1,
|
||||
decoder_id2;
|
||||
CreatePcs(rtc::make_ref_counted<IdLoggingAudioEncoderFactory>(
|
||||
webrtc::CreateAudioEncoderFactory<
|
||||
AudioEncoderUnicornSparklesRainbow>(),
|
||||
&encoder_id1),
|
||||
rtc::make_ref_counted<IdLoggingAudioDecoderFactory>(
|
||||
webrtc::CreateAudioDecoderFactory<
|
||||
AudioDecoderUnicornSparklesRainbow>(),
|
||||
&decoder_id1),
|
||||
rtc::make_ref_counted<IdLoggingAudioEncoderFactory>(
|
||||
webrtc::CreateAudioEncoderFactory<
|
||||
AudioEncoderUnicornSparklesRainbow>(),
|
||||
&encoder_id2),
|
||||
rtc::make_ref_counted<IdLoggingAudioDecoderFactory>(
|
||||
webrtc::CreateAudioDecoderFactory<
|
||||
AudioDecoderUnicornSparklesRainbow>(),
|
||||
&decoder_id2));
|
||||
GetAndAddUserMedia();
|
||||
Negotiate();
|
||||
WaitForCallEstablished();
|
||||
|
||||
// Each codec factory has been used to create one codec. The first pair got
|
||||
// the same ID because they were passed to the same PeerConnectionFactory,
|
||||
// and the second pair got the same ID---but these two IDs are not equal,
|
||||
// because each PeerConnectionFactory has its own ID.
|
||||
EXPECT_EQ(1U, encoder_id1.size());
|
||||
EXPECT_EQ(1U, encoder_id2.size());
|
||||
EXPECT_EQ(encoder_id1, decoder_id1);
|
||||
EXPECT_EQ(encoder_id2, decoder_id2);
|
||||
EXPECT_NE(encoder_id1, encoder_id2);
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_HAVE_SCTP
|
||||
// Verifies that a DataChannel created before the negotiation can transition to
|
||||
// "OPEN" and transfer data.
|
||||
TEST_P(PeerConnectionEndToEndTest, CreateDataChannelBeforeNegotiate) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> callee_dc(
|
||||
callee_->CreateDataChannel("data", init));
|
||||
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0);
|
||||
WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
|
||||
TestDataChannelSendAndReceive(caller_dc.get(),
|
||||
callee_signaled_data_channels_[0].get());
|
||||
TestDataChannelSendAndReceive(callee_dc.get(),
|
||||
caller_signaled_data_channels_[0].get());
|
||||
|
||||
CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 0);
|
||||
CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
}
|
||||
|
||||
// Verifies that a DataChannel created after the negotiation can transition to
|
||||
// "OPEN" and transfer data.
|
||||
TEST_P(PeerConnectionEndToEndTest, CreateDataChannelAfterNegotiate) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
|
||||
// This DataChannel is for creating the data content in the negotiation.
|
||||
rtc::scoped_refptr<DataChannelInterface> dummy(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
// Wait for the data channel created pre-negotiation to be opened.
|
||||
WaitForDataChannelsToOpen(dummy.get(), callee_signaled_data_channels_, 0);
|
||||
|
||||
// Create new DataChannels after the negotiation and verify their states.
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("hello", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> callee_dc(
|
||||
callee_->CreateDataChannel("hello", init));
|
||||
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
|
||||
TestDataChannelSendAndReceive(caller_dc.get(),
|
||||
callee_signaled_data_channels_[1].get());
|
||||
TestDataChannelSendAndReceive(callee_dc.get(),
|
||||
caller_signaled_data_channels_[0].get());
|
||||
|
||||
CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
}
|
||||
|
||||
// Verifies that a DataChannel created can transfer large messages.
|
||||
TEST_P(PeerConnectionEndToEndTest, CreateDataChannelLargeTransfer) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
|
||||
// This DataChannel is for creating the data content in the negotiation.
|
||||
rtc::scoped_refptr<DataChannelInterface> dummy(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
// Wait for the data channel created pre-negotiation to be opened.
|
||||
WaitForDataChannelsToOpen(dummy.get(), callee_signaled_data_channels_, 0);
|
||||
|
||||
// Create new DataChannels after the negotiation and verify their states.
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("hello", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> callee_dc(
|
||||
callee_->CreateDataChannel("hello", init));
|
||||
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
|
||||
TestDataChannelSendAndReceive(
|
||||
caller_dc.get(), callee_signaled_data_channels_[1].get(), 256 * 1024);
|
||||
TestDataChannelSendAndReceive(
|
||||
callee_dc.get(), caller_signaled_data_channels_[0].get(), 256 * 1024);
|
||||
|
||||
CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0);
|
||||
}
|
||||
|
||||
// Verifies that DataChannel IDs are even/odd based on the DTLS roles.
|
||||
TEST_P(PeerConnectionEndToEndTest, DataChannelIdAssignment) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc_1(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> callee_dc_1(
|
||||
callee_->CreateDataChannel("data", init));
|
||||
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
EXPECT_EQ(1, caller_dc_1->id() % 2);
|
||||
EXPECT_EQ(0, callee_dc_1->id() % 2);
|
||||
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc_2(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> callee_dc_2(
|
||||
callee_->CreateDataChannel("data", init));
|
||||
|
||||
EXPECT_EQ(1, caller_dc_2->id() % 2);
|
||||
EXPECT_EQ(0, callee_dc_2->id() % 2);
|
||||
}
|
||||
|
||||
// Verifies that the message is received by the right remote DataChannel when
|
||||
// there are multiple DataChannels.
|
||||
TEST_P(PeerConnectionEndToEndTest,
|
||||
MessageTransferBetweenTwoPairsOfDataChannels) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc_1(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc_2(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
WaitForDataChannelsToOpen(caller_dc_1.get(), callee_signaled_data_channels_,
|
||||
0);
|
||||
WaitForDataChannelsToOpen(caller_dc_2.get(), callee_signaled_data_channels_,
|
||||
1);
|
||||
|
||||
std::unique_ptr<webrtc::MockDataChannelObserver> dc_1_observer(
|
||||
new webrtc::MockDataChannelObserver(
|
||||
callee_signaled_data_channels_[0].get()));
|
||||
|
||||
std::unique_ptr<webrtc::MockDataChannelObserver> dc_2_observer(
|
||||
new webrtc::MockDataChannelObserver(
|
||||
callee_signaled_data_channels_[1].get()));
|
||||
|
||||
const std::string message_1 = "hello 1";
|
||||
const std::string message_2 = "hello 2";
|
||||
|
||||
caller_dc_1->Send(webrtc::DataBuffer(message_1));
|
||||
EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait);
|
||||
|
||||
caller_dc_2->Send(webrtc::DataBuffer(message_2));
|
||||
EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait);
|
||||
|
||||
EXPECT_EQ(1U, dc_1_observer->received_message_count());
|
||||
EXPECT_EQ(1U, dc_2_observer->received_message_count());
|
||||
}
|
||||
|
||||
// Verifies that a DataChannel added from an OPEN message functions after
|
||||
// a channel has been previously closed (webrtc issue 3778).
|
||||
// This previously failed because the new channel re-used the ID of the closed
|
||||
// channel, and the closed channel was incorrectly still assigned to the ID.
|
||||
TEST_P(PeerConnectionEndToEndTest,
|
||||
DataChannelFromOpenWorksAfterPreviousChannelClosed) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0);
|
||||
int first_channel_id = caller_dc->id();
|
||||
// Wait for the local side to say it's closed, but not the remote side.
|
||||
// Previously, the channel on which Close is called reported being closed
|
||||
// prematurely, and this caused issues; see bugs.webrtc.org/4453.
|
||||
caller_dc->Close();
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kClosed, caller_dc->state(), kMaxWait);
|
||||
|
||||
// Create a new channel and ensure it works after closing the previous one.
|
||||
caller_dc = caller_->CreateDataChannel("data2", init);
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
// Since the second channel was created after the first finished closing, it
|
||||
// should be able to re-use the first one's ID.
|
||||
EXPECT_EQ(first_channel_id, caller_dc->id());
|
||||
TestDataChannelSendAndReceive(caller_dc.get(),
|
||||
callee_signaled_data_channels_[1].get());
|
||||
|
||||
CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1);
|
||||
}
|
||||
|
||||
// This tests that if a data channel is closed remotely while not referenced
|
||||
// by the application (meaning only the PeerConnection contributes to its
|
||||
// reference count), no memory access violation will occur.
|
||||
// See: https://code.google.com/p/chromium/issues/detail?id=565048
|
||||
TEST_P(PeerConnectionEndToEndTest, CloseDataChannelRemotelyWhileNotReferenced) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
|
||||
WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0);
|
||||
// This removes the reference to the remote data channel that we hold.
|
||||
callee_signaled_data_channels_.clear();
|
||||
caller_dc->Close();
|
||||
EXPECT_EQ_WAIT(DataChannelInterface::kClosed, caller_dc->state(), kMaxWait);
|
||||
|
||||
// Wait for a bit longer so the remote data channel will receive the
|
||||
// close message and be destroyed.
|
||||
rtc::Thread::Current()->ProcessMessages(100);
|
||||
}
|
||||
|
||||
// Test behavior of creating too many datachannels.
|
||||
TEST_P(PeerConnectionEndToEndTest, TooManyDataChannelsOpenedBeforeConnecting) {
|
||||
CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(),
|
||||
webrtc::MockAudioDecoderFactory::CreateEmptyFactory());
|
||||
|
||||
webrtc::DataChannelInit init;
|
||||
std::vector<rtc::scoped_refptr<DataChannelInterface>> channels;
|
||||
for (int i = 0; i <= cricket::kMaxSctpStreams / 2; i++) {
|
||||
rtc::scoped_refptr<DataChannelInterface> caller_dc(
|
||||
caller_->CreateDataChannel("data", init));
|
||||
channels.push_back(std::move(caller_dc));
|
||||
}
|
||||
Negotiate();
|
||||
WaitForConnection();
|
||||
EXPECT_EQ_WAIT(callee_signaled_data_channels_.size(),
|
||||
static_cast<size_t>(cricket::kMaxSctpStreams / 2), kMaxWait);
|
||||
EXPECT_EQ(DataChannelInterface::kOpen,
|
||||
channels[(cricket::kMaxSctpStreams / 2) - 1]->state());
|
||||
EXPECT_EQ(DataChannelInterface::kClosed,
|
||||
channels[cricket::kMaxSctpStreams / 2]->state());
|
||||
}
|
||||
|
||||
#endif // WEBRTC_HAVE_SCTP
|
||||
|
||||
TEST_P(PeerConnectionEndToEndTest, CanRestartIce) {
|
||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> real_decoder_factory =
|
||||
webrtc::CreateOpusAudioDecoderFactory();
|
||||
CreatePcs(webrtc::CreateOpusAudioEncoderFactory(),
|
||||
CreateForwardingMockDecoderFactory(real_decoder_factory.get()));
|
||||
GetAndAddUserMedia();
|
||||
Negotiate();
|
||||
WaitForCallEstablished();
|
||||
// Cause ICE restart to be requested.
|
||||
auto config = caller_->pc()->GetConfiguration();
|
||||
ASSERT_NE(PeerConnectionInterface::kRelay, config.type);
|
||||
config.type = PeerConnectionInterface::kRelay;
|
||||
ASSERT_TRUE(caller_->pc()->SetConfiguration(config).ok());
|
||||
// When solving https://crbug.com/webrtc/10504, all we need to check
|
||||
// is that we do not crash. We should also be testing that restart happens.
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PeerConnectionEndToEndTest,
|
||||
PeerConnectionEndToEndTest,
|
||||
Values(SdpSemantics::kPlanB_DEPRECATED,
|
||||
SdpSemantics::kUnifiedPlan));
|
||||
358
TMessagesProj/jni/voip/webrtc/pc/peer_connection_factory.cc
Normal file
358
TMessagesProj/jni/voip/webrtc/pc/peer_connection_factory.cc
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright 2004 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 "pc/peer_connection_factory.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/environment/environment_factory.h"
|
||||
#include "api/fec_controller.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/network_state_predictor.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/bitrate_settings.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "call/audio_state.h"
|
||||
#include "call/rtp_transport_controller_send_factory.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "p2p/base/default_ice_transport_factory.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/client/basic_port_allocator.h"
|
||||
#include "pc/audio_track.h"
|
||||
#include "pc/local_audio_source.h"
|
||||
#include "pc/media_factory.h"
|
||||
#include "pc/media_stream.h"
|
||||
#include "pc/media_stream_proxy.h"
|
||||
#include "pc/media_stream_track_proxy.h"
|
||||
#include "pc/peer_connection.h"
|
||||
#include "pc/peer_connection_factory_proxy.h"
|
||||
#include "pc/peer_connection_proxy.h"
|
||||
#include "pc/rtp_parameters_conversion.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/video_track.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
#include "rtc_base/experiments/field_trial_units.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/system/file_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface>
|
||||
CreateModularPeerConnectionFactory(
|
||||
PeerConnectionFactoryDependencies dependencies) {
|
||||
// The PeerConnectionFactory must be created on the signaling thread.
|
||||
if (dependencies.signaling_thread &&
|
||||
!dependencies.signaling_thread->IsCurrent()) {
|
||||
return dependencies.signaling_thread->BlockingCall([&dependencies] {
|
||||
return CreateModularPeerConnectionFactory(std::move(dependencies));
|
||||
});
|
||||
}
|
||||
|
||||
auto pc_factory = PeerConnectionFactory::Create(std::move(dependencies));
|
||||
if (!pc_factory) {
|
||||
return nullptr;
|
||||
}
|
||||
// Verify that the invocation and the initialization ended up agreeing on the
|
||||
// thread.
|
||||
RTC_DCHECK_RUN_ON(pc_factory->signaling_thread());
|
||||
return PeerConnectionFactoryProxy::Create(
|
||||
pc_factory->signaling_thread(), pc_factory->worker_thread(), pc_factory);
|
||||
}
|
||||
|
||||
// Static
|
||||
rtc::scoped_refptr<PeerConnectionFactory> PeerConnectionFactory::Create(
|
||||
PeerConnectionFactoryDependencies dependencies) {
|
||||
auto context = ConnectionContext::Create(
|
||||
CreateEnvironment(std::move(dependencies.trials),
|
||||
std::move(dependencies.task_queue_factory)),
|
||||
&dependencies);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
return rtc::make_ref_counted<PeerConnectionFactory>(context, &dependencies);
|
||||
}
|
||||
|
||||
PeerConnectionFactory::PeerConnectionFactory(
|
||||
rtc::scoped_refptr<ConnectionContext> context,
|
||||
PeerConnectionFactoryDependencies* dependencies)
|
||||
: context_(context),
|
||||
event_log_factory_(std::move(dependencies->event_log_factory)),
|
||||
fec_controller_factory_(std::move(dependencies->fec_controller_factory)),
|
||||
network_state_predictor_factory_(
|
||||
std::move(dependencies->network_state_predictor_factory)),
|
||||
injected_network_controller_factory_(
|
||||
std::move(dependencies->network_controller_factory)),
|
||||
neteq_factory_(std::move(dependencies->neteq_factory)),
|
||||
transport_controller_send_factory_(
|
||||
(dependencies->transport_controller_send_factory)
|
||||
? std::move(dependencies->transport_controller_send_factory)
|
||||
: std::make_unique<RtpTransportControllerSendFactory>()),
|
||||
decode_metronome_(std::move(dependencies->decode_metronome)),
|
||||
encode_metronome_(std::move(dependencies->encode_metronome)) {}
|
||||
|
||||
PeerConnectionFactory::PeerConnectionFactory(
|
||||
PeerConnectionFactoryDependencies dependencies)
|
||||
: PeerConnectionFactory(
|
||||
ConnectionContext::Create(
|
||||
CreateEnvironment(std::move(dependencies.trials),
|
||||
std::move(dependencies.task_queue_factory)),
|
||||
&dependencies),
|
||||
&dependencies) {}
|
||||
|
||||
PeerConnectionFactory::~PeerConnectionFactory() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
worker_thread()->BlockingCall([this] {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
decode_metronome_ = nullptr;
|
||||
encode_metronome_ = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void PeerConnectionFactory::SetOptions(const Options& options) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
options_ = options;
|
||||
}
|
||||
|
||||
RtpCapabilities PeerConnectionFactory::GetRtpSenderCapabilities(
|
||||
cricket::MediaType kind) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
switch (kind) {
|
||||
case cricket::MEDIA_TYPE_AUDIO: {
|
||||
cricket::AudioCodecs cricket_codecs;
|
||||
cricket_codecs = media_engine()->voice().send_codecs();
|
||||
auto extensions =
|
||||
GetDefaultEnabledRtpHeaderExtensions(media_engine()->voice());
|
||||
return ToRtpCapabilities(cricket_codecs, extensions);
|
||||
}
|
||||
case cricket::MEDIA_TYPE_VIDEO: {
|
||||
cricket::VideoCodecs cricket_codecs;
|
||||
cricket_codecs = media_engine()->video().send_codecs(context_->use_rtx());
|
||||
auto extensions =
|
||||
GetDefaultEnabledRtpHeaderExtensions(media_engine()->video());
|
||||
return ToRtpCapabilities(cricket_codecs, extensions);
|
||||
}
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
return RtpCapabilities();
|
||||
case cricket::MEDIA_TYPE_UNSUPPORTED:
|
||||
return RtpCapabilities();
|
||||
}
|
||||
RTC_DLOG(LS_ERROR) << "Got unexpected MediaType " << kind;
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
RtpCapabilities PeerConnectionFactory::GetRtpReceiverCapabilities(
|
||||
cricket::MediaType kind) const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
switch (kind) {
|
||||
case cricket::MEDIA_TYPE_AUDIO: {
|
||||
cricket::AudioCodecs cricket_codecs;
|
||||
cricket_codecs = media_engine()->voice().recv_codecs();
|
||||
auto extensions =
|
||||
GetDefaultEnabledRtpHeaderExtensions(media_engine()->voice());
|
||||
return ToRtpCapabilities(cricket_codecs, extensions);
|
||||
}
|
||||
case cricket::MEDIA_TYPE_VIDEO: {
|
||||
cricket::VideoCodecs cricket_codecs =
|
||||
media_engine()->video().recv_codecs(context_->use_rtx());
|
||||
auto extensions =
|
||||
GetDefaultEnabledRtpHeaderExtensions(media_engine()->video());
|
||||
return ToRtpCapabilities(cricket_codecs, extensions);
|
||||
}
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
return RtpCapabilities();
|
||||
case cricket::MEDIA_TYPE_UNSUPPORTED:
|
||||
return RtpCapabilities();
|
||||
}
|
||||
RTC_DLOG(LS_ERROR) << "Got unexpected MediaType " << kind;
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioSourceInterface>
|
||||
PeerConnectionFactory::CreateAudioSource(const cricket::AudioOptions& options) {
|
||||
RTC_DCHECK(signaling_thread()->IsCurrent());
|
||||
rtc::scoped_refptr<LocalAudioSource> source(
|
||||
LocalAudioSource::Create(&options));
|
||||
return source;
|
||||
}
|
||||
|
||||
bool PeerConnectionFactory::StartAecDump(FILE* file, int64_t max_size_bytes) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
return media_engine()->voice().StartAecDump(FileWrapper(file),
|
||||
max_size_bytes);
|
||||
}
|
||||
|
||||
void PeerConnectionFactory::StopAecDump() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
media_engine()->voice().StopAecDump();
|
||||
}
|
||||
|
||||
cricket::MediaEngineInterface* PeerConnectionFactory::media_engine() const {
|
||||
RTC_DCHECK(context_);
|
||||
return context_->media_engine();
|
||||
}
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<PeerConnectionInterface>>
|
||||
PeerConnectionFactory::CreatePeerConnectionOrError(
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration,
|
||||
PeerConnectionDependencies dependencies) {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
|
||||
EnvironmentFactory env_factory(context_->env());
|
||||
|
||||
// Field trials active for this PeerConnection is the first of:
|
||||
// a) Specified in the PeerConnectionDependencies
|
||||
// b) Specified in the PeerConnectionFactoryDependencies
|
||||
// c) Created as default by the EnvironmentFactory.
|
||||
env_factory.Set(std::move(dependencies.trials));
|
||||
|
||||
if (event_log_factory_ != nullptr) {
|
||||
worker_thread()->BlockingCall([&] {
|
||||
Environment env_for_rtc_event_log = env_factory.Create();
|
||||
env_factory.Set(event_log_factory_->Create(env_for_rtc_event_log));
|
||||
});
|
||||
}
|
||||
|
||||
const Environment env = env_factory.Create();
|
||||
|
||||
// Set internal defaults if optional dependencies are not set.
|
||||
if (!dependencies.cert_generator) {
|
||||
dependencies.cert_generator =
|
||||
std::make_unique<rtc::RTCCertificateGenerator>(signaling_thread(),
|
||||
network_thread());
|
||||
}
|
||||
if (!dependencies.allocator) {
|
||||
dependencies.allocator = std::make_unique<cricket::BasicPortAllocator>(
|
||||
context_->default_network_manager(), context_->default_socket_factory(),
|
||||
configuration.turn_customizer, /*relay_port_factory=*/nullptr,
|
||||
&env.field_trials());
|
||||
dependencies.allocator->SetPortRange(
|
||||
configuration.port_allocator_config.min_port,
|
||||
configuration.port_allocator_config.max_port);
|
||||
dependencies.allocator->set_flags(
|
||||
configuration.port_allocator_config.flags);
|
||||
}
|
||||
|
||||
if (!dependencies.ice_transport_factory) {
|
||||
dependencies.ice_transport_factory =
|
||||
std::make_unique<DefaultIceTransportFactory>();
|
||||
}
|
||||
|
||||
dependencies.allocator->SetNetworkIgnoreMask(options().network_ignore_mask);
|
||||
dependencies.allocator->SetVpnList(configuration.vpn_list);
|
||||
|
||||
std::unique_ptr<Call> call =
|
||||
worker_thread()->BlockingCall([this, &env, &configuration] {
|
||||
return CreateCall_w(env, configuration);
|
||||
});
|
||||
|
||||
auto result = PeerConnection::Create(env, context_, options_, std::move(call),
|
||||
configuration, std::move(dependencies));
|
||||
if (!result.ok()) {
|
||||
return result.MoveError();
|
||||
}
|
||||
// We configure the proxy with a pointer to the network thread for methods
|
||||
// that need to be invoked there rather than on the signaling thread.
|
||||
// Internally, the proxy object has a member variable named `worker_thread_`
|
||||
// which will point to the network thread (and not the factory's
|
||||
// worker_thread()). All such methods have thread checks though, so the code
|
||||
// should still be clear (outside of macro expansion).
|
||||
rtc::scoped_refptr<PeerConnectionInterface> result_proxy =
|
||||
PeerConnectionProxy::Create(signaling_thread(), network_thread(),
|
||||
result.MoveValue());
|
||||
return result_proxy;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<MediaStreamInterface>
|
||||
PeerConnectionFactory::CreateLocalMediaStream(const std::string& stream_id) {
|
||||
RTC_DCHECK(signaling_thread()->IsCurrent());
|
||||
return MediaStreamProxy::Create(signaling_thread(),
|
||||
MediaStream::Create(stream_id));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> PeerConnectionFactory::CreateVideoTrack(
|
||||
rtc::scoped_refptr<VideoTrackSourceInterface> source,
|
||||
absl::string_view id) {
|
||||
RTC_DCHECK(signaling_thread()->IsCurrent());
|
||||
rtc::scoped_refptr<VideoTrackInterface> track =
|
||||
VideoTrack::Create(id, source, worker_thread());
|
||||
return VideoTrackProxy::Create(signaling_thread(), worker_thread(), track);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> PeerConnectionFactory::CreateAudioTrack(
|
||||
const std::string& id,
|
||||
AudioSourceInterface* source) {
|
||||
RTC_DCHECK(signaling_thread()->IsCurrent());
|
||||
rtc::scoped_refptr<AudioTrackInterface> track =
|
||||
AudioTrack::Create(id, rtc::scoped_refptr<AudioSourceInterface>(source));
|
||||
return AudioTrackProxy::Create(signaling_thread(), track);
|
||||
}
|
||||
|
||||
std::unique_ptr<Call> PeerConnectionFactory::CreateCall_w(
|
||||
const Environment& env,
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
|
||||
CallConfig call_config(env, network_thread());
|
||||
if (!media_engine() || !context_->call_factory()) {
|
||||
return nullptr;
|
||||
}
|
||||
call_config.audio_state = media_engine()->voice().GetAudioState();
|
||||
|
||||
FieldTrialParameter<DataRate> min_bandwidth("min",
|
||||
DataRate::KilobitsPerSec(30));
|
||||
FieldTrialParameter<DataRate> start_bandwidth("start",
|
||||
DataRate::KilobitsPerSec(300));
|
||||
FieldTrialParameter<DataRate> max_bandwidth("max",
|
||||
DataRate::KilobitsPerSec(2000));
|
||||
ParseFieldTrial({&min_bandwidth, &start_bandwidth, &max_bandwidth},
|
||||
env.field_trials().Lookup("WebRTC-PcFactoryDefaultBitrates"));
|
||||
|
||||
call_config.bitrate_config.min_bitrate_bps =
|
||||
rtc::saturated_cast<int>(min_bandwidth->bps());
|
||||
call_config.bitrate_config.start_bitrate_bps =
|
||||
rtc::saturated_cast<int>(start_bandwidth->bps());
|
||||
call_config.bitrate_config.max_bitrate_bps =
|
||||
rtc::saturated_cast<int>(max_bandwidth->bps());
|
||||
|
||||
call_config.fec_controller_factory = fec_controller_factory_.get();
|
||||
call_config.network_state_predictor_factory =
|
||||
network_state_predictor_factory_.get();
|
||||
call_config.neteq_factory = neteq_factory_.get();
|
||||
|
||||
if (IsTrialEnabled("WebRTC-Bwe-InjectedCongestionController")) {
|
||||
RTC_LOG(LS_INFO) << "Using injected network controller factory";
|
||||
call_config.network_controller_factory =
|
||||
injected_network_controller_factory_.get();
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << "Using default network controller factory";
|
||||
}
|
||||
|
||||
call_config.rtp_transport_controller_send_factory =
|
||||
transport_controller_send_factory_.get();
|
||||
call_config.decode_metronome = decode_metronome_.get();
|
||||
call_config.encode_metronome = encode_metronome_.get();
|
||||
call_config.pacer_burst_interval = configuration.pacer_burst_interval;
|
||||
return context_->call_factory()->CreateCall(call_config);
|
||||
}
|
||||
|
||||
bool PeerConnectionFactory::IsTrialEnabled(absl::string_view key) const {
|
||||
return absl::StartsWith(field_trials().Lookup(key), "Enabled");
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
156
TMessagesProj/jni/voip/webrtc/pc/peer_connection_factory.h
Normal file
156
TMessagesProj/jni/voip/webrtc/pc/peer_connection_factory.h
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
|
||||
/*
|
||||
* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_FACTORY_H_
|
||||
#define PC_PEER_CONNECTION_FACTORY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/audio_options.h"
|
||||
#include "api/fec_controller.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/metronome/metronome.h"
|
||||
#include "api/neteq/neteq_factory.h"
|
||||
#include "api/network_state_predictor.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/network_control.h"
|
||||
#include "api/transport/sctp_transport_factory_interface.h"
|
||||
#include "call/call.h"
|
||||
#include "call/rtp_transport_controller_send_factory_interface.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "pc/connection_context.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace rtc {
|
||||
class BasicNetworkManager;
|
||||
class BasicPacketSocketFactory;
|
||||
} // namespace rtc
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnectionFactory : public PeerConnectionFactoryInterface {
|
||||
public:
|
||||
// Creates a PeerConnectionFactory. It returns nullptr on initialization
|
||||
// error.
|
||||
//
|
||||
// The Dependencies structure allows simple management of all new
|
||||
// dependencies being added to the PeerConnectionFactory.
|
||||
static rtc::scoped_refptr<PeerConnectionFactory> Create(
|
||||
PeerConnectionFactoryDependencies dependencies);
|
||||
|
||||
void SetOptions(const Options& options) override;
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<PeerConnectionInterface>>
|
||||
CreatePeerConnectionOrError(
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration,
|
||||
PeerConnectionDependencies dependencies) override;
|
||||
|
||||
RtpCapabilities GetRtpSenderCapabilities(
|
||||
cricket::MediaType kind) const override;
|
||||
|
||||
RtpCapabilities GetRtpReceiverCapabilities(
|
||||
cricket::MediaType kind) const override;
|
||||
|
||||
rtc::scoped_refptr<MediaStreamInterface> CreateLocalMediaStream(
|
||||
const std::string& stream_id) override;
|
||||
|
||||
rtc::scoped_refptr<AudioSourceInterface> CreateAudioSource(
|
||||
const cricket::AudioOptions& options) override;
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> CreateVideoTrack(
|
||||
rtc::scoped_refptr<VideoTrackSourceInterface> video_source,
|
||||
absl::string_view id) override;
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> CreateAudioTrack(
|
||||
const std::string& id,
|
||||
AudioSourceInterface* audio_source) override;
|
||||
|
||||
bool StartAecDump(FILE* file, int64_t max_size_bytes) override;
|
||||
void StopAecDump() override;
|
||||
|
||||
SctpTransportFactoryInterface* sctp_transport_factory() {
|
||||
return context_->sctp_transport_factory();
|
||||
}
|
||||
|
||||
rtc::Thread* signaling_thread() const {
|
||||
// This method can be called on a different thread when the factory is
|
||||
// created in CreatePeerConnectionFactory().
|
||||
return context_->signaling_thread();
|
||||
}
|
||||
|
||||
rtc::Thread* worker_thread() const { return context_->worker_thread(); }
|
||||
|
||||
const Options& options() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return options_;
|
||||
}
|
||||
|
||||
const FieldTrialsView& field_trials() const {
|
||||
return context_->env().field_trials();
|
||||
}
|
||||
|
||||
cricket::MediaEngineInterface* media_engine() const;
|
||||
|
||||
protected:
|
||||
// Constructor used by the static Create() method. Modifies the dependencies.
|
||||
PeerConnectionFactory(rtc::scoped_refptr<ConnectionContext> context,
|
||||
PeerConnectionFactoryDependencies* dependencies);
|
||||
|
||||
// Constructor for use in testing. Ignores the possibility of initialization
|
||||
// failure. The dependencies are passed in by std::move().
|
||||
explicit PeerConnectionFactory(
|
||||
PeerConnectionFactoryDependencies dependencies);
|
||||
|
||||
virtual ~PeerConnectionFactory();
|
||||
|
||||
private:
|
||||
rtc::Thread* network_thread() const { return context_->network_thread(); }
|
||||
|
||||
bool IsTrialEnabled(absl::string_view key) const;
|
||||
|
||||
std::unique_ptr<Call> CreateCall_w(
|
||||
const Environment& env,
|
||||
const PeerConnectionInterface::RTCConfiguration& configuration);
|
||||
|
||||
rtc::scoped_refptr<ConnectionContext> context_;
|
||||
PeerConnectionFactoryInterface::Options options_
|
||||
RTC_GUARDED_BY(signaling_thread());
|
||||
std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory_;
|
||||
std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory_;
|
||||
std::unique_ptr<NetworkStatePredictorFactoryInterface>
|
||||
network_state_predictor_factory_;
|
||||
std::unique_ptr<NetworkControllerFactoryInterface>
|
||||
injected_network_controller_factory_;
|
||||
std::unique_ptr<NetEqFactory> neteq_factory_;
|
||||
const std::unique_ptr<RtpTransportControllerSendFactoryInterface>
|
||||
transport_controller_send_factory_;
|
||||
std::unique_ptr<Metronome> decode_metronome_ RTC_GUARDED_BY(worker_thread());
|
||||
std::unique_ptr<Metronome> encode_metronome_ RTC_GUARDED_BY(worker_thread());
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_FACTORY_H_
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_FACTORY_PROXY_H_
|
||||
#define PC_PEER_CONNECTION_FACTORY_PROXY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "pc/proxy.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(deadbeef): Move this to .cc file. What threads methods are called on is
|
||||
// an implementation detail.
|
||||
BEGIN_PROXY_MAP(PeerConnectionFactory)
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
PROXY_METHOD1(void, SetOptions, const Options&)
|
||||
PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<PeerConnectionInterface>>,
|
||||
CreatePeerConnectionOrError,
|
||||
const PeerConnectionInterface::RTCConfiguration&,
|
||||
PeerConnectionDependencies)
|
||||
PROXY_CONSTMETHOD1(RtpCapabilities,
|
||||
GetRtpSenderCapabilities,
|
||||
cricket::MediaType)
|
||||
PROXY_CONSTMETHOD1(RtpCapabilities,
|
||||
GetRtpReceiverCapabilities,
|
||||
cricket::MediaType)
|
||||
PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,
|
||||
CreateLocalMediaStream,
|
||||
const std::string&)
|
||||
PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,
|
||||
CreateAudioSource,
|
||||
const cricket::AudioOptions&)
|
||||
PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,
|
||||
CreateVideoTrack,
|
||||
rtc::scoped_refptr<VideoTrackSourceInterface>,
|
||||
absl::string_view)
|
||||
PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,
|
||||
CreateAudioTrack,
|
||||
const std::string&,
|
||||
AudioSourceInterface*)
|
||||
PROXY_SECONDARY_METHOD2(bool, StartAecDump, FILE*, int64_t)
|
||||
PROXY_SECONDARY_METHOD0(void, StopAecDump)
|
||||
END_PROXY_MAP(PeerConnectionFactory)
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_FACTORY_PROXY_H_
|
||||
|
|
@ -0,0 +1,724 @@
|
|||
/*
|
||||
* Copyright 2012 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 "pc/peer_connection_factory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/create_peerconnection_factory.h"
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "api/enable_media.h"
|
||||
#include "api/environment/environment_factory.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/test/mock_packet_socket_factory.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
|
||||
#include "media/base/fake_frame_source.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "modules/audio_processing/include/audio_processing.h"
|
||||
#include "p2p/base/fake_port_allocator.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "pc/test/fake_audio_capture_module.h"
|
||||
#include "pc/test/fake_video_track_source.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/internal/default_socket_server.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/android_test_initializer.h"
|
||||
#endif
|
||||
#include "pc/test/fake_rtc_certificate_generator.h"
|
||||
#include "pc/test/fake_video_track_renderer.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::InvokeWithoutArgs;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
static const char kStunIceServer[] = "stun:stun.l.google.com:19302";
|
||||
static const char kTurnIceServer[] = "turn:test.com:1234";
|
||||
static const char kTurnIceServerWithTransport[] =
|
||||
"turn:hello.com?transport=tcp";
|
||||
static const char kSecureTurnIceServer[] = "turns:hello.com?transport=tcp";
|
||||
static const char kSecureTurnIceServerWithoutTransportParam[] =
|
||||
"turns:hello.com:443";
|
||||
static const char kSecureTurnIceServerWithoutTransportAndPortParam[] =
|
||||
"turns:hello.com";
|
||||
static const char kTurnIceServerWithNoUsernameInUri[] = "turn:test.com:1234";
|
||||
static const char kTurnPassword[] = "turnpassword";
|
||||
static const int kDefaultStunPort = 3478;
|
||||
static const int kDefaultStunTlsPort = 5349;
|
||||
static const char kTurnUsername[] = "test";
|
||||
static const char kStunIceServerWithIPv4Address[] = "stun:1.2.3.4:1234";
|
||||
static const char kStunIceServerWithIPv4AddressWithoutPort[] = "stun:1.2.3.4";
|
||||
static const char kStunIceServerWithIPv6Address[] = "stun:[2401:fa00:4::]:1234";
|
||||
static const char kStunIceServerWithIPv6AddressWithoutPort[] =
|
||||
"stun:[2401:fa00:4::]";
|
||||
static const char kTurnIceServerWithIPv6Address[] = "turn:[2401:fa00:4::]:1234";
|
||||
|
||||
class NullPeerConnectionObserver : public PeerConnectionObserver {
|
||||
public:
|
||||
virtual ~NullPeerConnectionObserver() = default;
|
||||
void OnSignalingChange(
|
||||
PeerConnectionInterface::SignalingState new_state) override {}
|
||||
void OnAddStream(rtc::scoped_refptr<MediaStreamInterface> stream) override {}
|
||||
void OnRemoveStream(
|
||||
rtc::scoped_refptr<MediaStreamInterface> stream) override {}
|
||||
void OnDataChannel(
|
||||
rtc::scoped_refptr<DataChannelInterface> data_channel) override {}
|
||||
void OnRenegotiationNeeded() override {}
|
||||
void OnIceConnectionChange(
|
||||
PeerConnectionInterface::IceConnectionState new_state) override {}
|
||||
void OnIceGatheringChange(
|
||||
PeerConnectionInterface::IceGatheringState new_state) override {}
|
||||
void OnIceCandidate(const IceCandidateInterface* candidate) override {}
|
||||
};
|
||||
|
||||
class MockNetworkManager : public rtc::NetworkManager {
|
||||
public:
|
||||
MOCK_METHOD(void, StartUpdating, (), (override));
|
||||
MOCK_METHOD(void, StopUpdating, (), (override));
|
||||
MOCK_METHOD(std::vector<const rtc::Network*>,
|
||||
GetNetworks,
|
||||
(),
|
||||
(const override));
|
||||
MOCK_METHOD(std::vector<const rtc::Network*>,
|
||||
GetAnyAddressNetworks,
|
||||
(),
|
||||
(override));
|
||||
};
|
||||
|
||||
class PeerConnectionFactoryTest : public ::testing::Test {
|
||||
public:
|
||||
PeerConnectionFactoryTest()
|
||||
: socket_server_(rtc::CreateDefaultSocketServer()),
|
||||
main_thread_(socket_server_.get()) {}
|
||||
|
||||
private:
|
||||
void SetUp() {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
// Use fake audio device module since we're only testing the interface
|
||||
// level, and using a real one could make tests flaky e.g. when run in
|
||||
// parallel.
|
||||
factory_ = CreatePeerConnectionFactory(
|
||||
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
|
||||
rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
|
||||
CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
|
||||
std::make_unique<VideoEncoderFactoryTemplate<
|
||||
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
|
||||
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>(),
|
||||
std::make_unique<VideoDecoderFactoryTemplate<
|
||||
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
|
||||
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
|
||||
nullptr /* audio_mixer */, nullptr /* audio_processing */);
|
||||
|
||||
ASSERT_TRUE(factory_.get() != NULL);
|
||||
packet_socket_factory_.reset(
|
||||
new rtc::BasicPacketSocketFactory(socket_server_.get()));
|
||||
port_allocator_.reset(new cricket::FakePortAllocator(
|
||||
rtc::Thread::Current(), packet_socket_factory_.get(), &field_trials_));
|
||||
raw_port_allocator_ = port_allocator_.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
void VerifyStunServers(cricket::ServerAddresses stun_servers) {
|
||||
EXPECT_EQ(stun_servers, raw_port_allocator_->stun_servers());
|
||||
}
|
||||
|
||||
void VerifyTurnServers(std::vector<cricket::RelayServerConfig> turn_servers) {
|
||||
EXPECT_EQ(turn_servers.size(), raw_port_allocator_->turn_servers().size());
|
||||
for (size_t i = 0; i < turn_servers.size(); ++i) {
|
||||
ASSERT_EQ(1u, turn_servers[i].ports.size());
|
||||
EXPECT_EQ(1u, raw_port_allocator_->turn_servers()[i].ports.size());
|
||||
EXPECT_EQ(
|
||||
turn_servers[i].ports[0].address.ToString(),
|
||||
raw_port_allocator_->turn_servers()[i].ports[0].address.ToString());
|
||||
EXPECT_EQ(turn_servers[i].ports[0].proto,
|
||||
raw_port_allocator_->turn_servers()[i].ports[0].proto);
|
||||
EXPECT_EQ(turn_servers[i].credentials.username,
|
||||
raw_port_allocator_->turn_servers()[i].credentials.username);
|
||||
EXPECT_EQ(turn_servers[i].credentials.password,
|
||||
raw_port_allocator_->turn_servers()[i].credentials.password);
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyAudioCodecCapability(const RtpCodecCapability& codec) {
|
||||
EXPECT_EQ(codec.kind, cricket::MEDIA_TYPE_AUDIO);
|
||||
EXPECT_FALSE(codec.name.empty());
|
||||
EXPECT_GT(codec.clock_rate, 0);
|
||||
EXPECT_GT(codec.num_channels, 0);
|
||||
}
|
||||
|
||||
void VerifyVideoCodecCapability(const RtpCodecCapability& codec,
|
||||
bool sender) {
|
||||
EXPECT_EQ(codec.kind, cricket::MEDIA_TYPE_VIDEO);
|
||||
EXPECT_FALSE(codec.name.empty());
|
||||
EXPECT_GT(codec.clock_rate, 0);
|
||||
if (sender) {
|
||||
if (codec.name == "VP8" || codec.name == "H264") {
|
||||
EXPECT_THAT(
|
||||
codec.scalability_modes,
|
||||
UnorderedElementsAre(ScalabilityMode::kL1T1, ScalabilityMode::kL1T2,
|
||||
ScalabilityMode::kL1T3))
|
||||
<< "Codec: " << codec.name;
|
||||
} else if (codec.name == "VP9" || codec.name == "AV1") {
|
||||
EXPECT_THAT(
|
||||
codec.scalability_modes,
|
||||
UnorderedElementsAre(
|
||||
// clang-format off
|
||||
ScalabilityMode::kL1T1,
|
||||
ScalabilityMode::kL1T2,
|
||||
ScalabilityMode::kL1T3,
|
||||
ScalabilityMode::kL2T1,
|
||||
ScalabilityMode::kL2T1h,
|
||||
ScalabilityMode::kL2T1_KEY,
|
||||
ScalabilityMode::kL2T2,
|
||||
ScalabilityMode::kL2T2h,
|
||||
ScalabilityMode::kL2T2_KEY,
|
||||
ScalabilityMode::kL2T2_KEY_SHIFT,
|
||||
ScalabilityMode::kL2T3,
|
||||
ScalabilityMode::kL2T3h,
|
||||
ScalabilityMode::kL2T3_KEY,
|
||||
ScalabilityMode::kL3T1,
|
||||
ScalabilityMode::kL3T1h,
|
||||
ScalabilityMode::kL3T1_KEY,
|
||||
ScalabilityMode::kL3T2,
|
||||
ScalabilityMode::kL3T2h,
|
||||
ScalabilityMode::kL3T2_KEY,
|
||||
ScalabilityMode::kL3T3,
|
||||
ScalabilityMode::kL3T3h,
|
||||
ScalabilityMode::kL3T3_KEY,
|
||||
ScalabilityMode::kS2T1,
|
||||
ScalabilityMode::kS2T1h,
|
||||
ScalabilityMode::kS2T2,
|
||||
ScalabilityMode::kS2T2h,
|
||||
ScalabilityMode::kS2T3,
|
||||
ScalabilityMode::kS2T3h,
|
||||
ScalabilityMode::kS3T1,
|
||||
ScalabilityMode::kS3T1h,
|
||||
ScalabilityMode::kS3T2,
|
||||
ScalabilityMode::kS3T2h,
|
||||
ScalabilityMode::kS3T3,
|
||||
ScalabilityMode::kS3T3h)
|
||||
// clang-format on
|
||||
)
|
||||
<< "Codec: " << codec.name;
|
||||
} else {
|
||||
EXPECT_TRUE(codec.scalability_modes.empty());
|
||||
}
|
||||
} else {
|
||||
EXPECT_TRUE(codec.scalability_modes.empty());
|
||||
}
|
||||
}
|
||||
|
||||
test::ScopedKeyValueConfig field_trials_;
|
||||
std::unique_ptr<rtc::SocketServer> socket_server_;
|
||||
rtc::AutoSocketServerThread main_thread_;
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory_;
|
||||
NullPeerConnectionObserver observer_;
|
||||
std::unique_ptr<rtc::PacketSocketFactory> packet_socket_factory_;
|
||||
std::unique_ptr<cricket::FakePortAllocator> port_allocator_;
|
||||
// Since the PC owns the port allocator after it's been initialized,
|
||||
// this should only be used when known to be safe.
|
||||
cricket::FakePortAllocator* raw_port_allocator_;
|
||||
};
|
||||
|
||||
// Since there is no public PeerConnectionFactory API to control RTX usage, need
|
||||
// to reconstruct factory with our own ConnectionContext.
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface>
|
||||
CreatePeerConnectionFactoryWithRtxDisabled() {
|
||||
PeerConnectionFactoryDependencies pcf_dependencies;
|
||||
pcf_dependencies.signaling_thread = rtc::Thread::Current();
|
||||
pcf_dependencies.worker_thread = rtc::Thread::Current();
|
||||
pcf_dependencies.network_thread = rtc::Thread::Current();
|
||||
pcf_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
|
||||
|
||||
pcf_dependencies.adm = FakeAudioCaptureModule::Create();
|
||||
pcf_dependencies.audio_encoder_factory = CreateBuiltinAudioEncoderFactory();
|
||||
pcf_dependencies.audio_decoder_factory = CreateBuiltinAudioDecoderFactory();
|
||||
pcf_dependencies.video_encoder_factory =
|
||||
std::make_unique<VideoEncoderFactoryTemplate<
|
||||
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
|
||||
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>();
|
||||
pcf_dependencies.video_decoder_factory =
|
||||
std::make_unique<VideoDecoderFactoryTemplate<
|
||||
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
|
||||
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
|
||||
EnableMedia(pcf_dependencies);
|
||||
|
||||
rtc::scoped_refptr<ConnectionContext> context =
|
||||
ConnectionContext::Create(CreateEnvironment(), &pcf_dependencies);
|
||||
context->set_use_rtx(false);
|
||||
return rtc::make_ref_counted<PeerConnectionFactory>(context,
|
||||
&pcf_dependencies);
|
||||
}
|
||||
|
||||
// Verify creation of PeerConnection using internal ADM, video factory and
|
||||
// internal libjingle threads.
|
||||
// TODO(henrika): disabling this test since relying on real audio can result in
|
||||
// flaky tests and focus on details that are out of scope for you might expect
|
||||
// for a PeerConnectionFactory unit test.
|
||||
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=7806 for details.
|
||||
TEST(PeerConnectionFactoryTestInternal, DISABLED_CreatePCUsingInternalModules) {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
|
||||
CreatePeerConnectionFactory(
|
||||
nullptr /* network_thread */, nullptr /* worker_thread */,
|
||||
nullptr /* signaling_thread */, nullptr /* default_adm */,
|
||||
CreateBuiltinAudioEncoderFactory(),
|
||||
CreateBuiltinAudioDecoderFactory(),
|
||||
nullptr /* video_encoder_factory */,
|
||||
nullptr /* video_decoder_factory */, nullptr /* audio_mixer */,
|
||||
nullptr /* audio_processing */));
|
||||
|
||||
NullPeerConnectionObserver observer;
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
|
||||
std::unique_ptr<FakeRTCCertificateGenerator> cert_generator(
|
||||
new FakeRTCCertificateGenerator());
|
||||
PeerConnectionDependencies pc_dependencies(&observer);
|
||||
pc_dependencies.cert_generator = std::move(cert_generator);
|
||||
auto result =
|
||||
factory->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
|
||||
EXPECT_TRUE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderAudioCapabilities) {
|
||||
RtpCapabilities audio_capabilities =
|
||||
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO);
|
||||
EXPECT_FALSE(audio_capabilities.codecs.empty());
|
||||
for (const auto& codec : audio_capabilities.codecs) {
|
||||
VerifyAudioCodecCapability(codec);
|
||||
}
|
||||
EXPECT_FALSE(audio_capabilities.header_extensions.empty());
|
||||
for (const auto& header_extension : audio_capabilities.header_extensions) {
|
||||
EXPECT_FALSE(header_extension.uri.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderVideoCapabilities) {
|
||||
RtpCapabilities video_capabilities =
|
||||
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
EXPECT_FALSE(video_capabilities.codecs.empty());
|
||||
for (const auto& codec : video_capabilities.codecs) {
|
||||
VerifyVideoCodecCapability(codec, true);
|
||||
}
|
||||
EXPECT_FALSE(video_capabilities.header_extensions.empty());
|
||||
for (const auto& header_extension : video_capabilities.header_extensions) {
|
||||
EXPECT_FALSE(header_extension.uri.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderRtxEnabledCapabilities) {
|
||||
RtpCapabilities video_capabilities =
|
||||
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
const auto it = std::find_if(
|
||||
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
|
||||
[](const auto& c) { return c.name == cricket::kRtxCodecName; });
|
||||
EXPECT_TRUE(it != video_capabilities.codecs.end());
|
||||
}
|
||||
|
||||
TEST(PeerConnectionFactoryTestInternal, CheckRtpSenderRtxDisabledCapabilities) {
|
||||
auto factory = CreatePeerConnectionFactoryWithRtxDisabled();
|
||||
RtpCapabilities video_capabilities =
|
||||
factory->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
const auto it = std::find_if(
|
||||
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
|
||||
[](const auto& c) { return c.name == cricket::kRtxCodecName; });
|
||||
EXPECT_TRUE(it == video_capabilities.codecs.end());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderDataCapabilities) {
|
||||
RtpCapabilities data_capabilities =
|
||||
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_DATA);
|
||||
EXPECT_TRUE(data_capabilities.codecs.empty());
|
||||
EXPECT_TRUE(data_capabilities.header_extensions.empty());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverAudioCapabilities) {
|
||||
RtpCapabilities audio_capabilities =
|
||||
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_AUDIO);
|
||||
EXPECT_FALSE(audio_capabilities.codecs.empty());
|
||||
for (const auto& codec : audio_capabilities.codecs) {
|
||||
VerifyAudioCodecCapability(codec);
|
||||
}
|
||||
EXPECT_FALSE(audio_capabilities.header_extensions.empty());
|
||||
for (const auto& header_extension : audio_capabilities.header_extensions) {
|
||||
EXPECT_FALSE(header_extension.uri.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverVideoCapabilities) {
|
||||
RtpCapabilities video_capabilities =
|
||||
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
EXPECT_FALSE(video_capabilities.codecs.empty());
|
||||
for (const auto& codec : video_capabilities.codecs) {
|
||||
VerifyVideoCodecCapability(codec, false);
|
||||
}
|
||||
EXPECT_FALSE(video_capabilities.header_extensions.empty());
|
||||
for (const auto& header_extension : video_capabilities.header_extensions) {
|
||||
EXPECT_FALSE(header_extension.uri.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverRtxEnabledCapabilities) {
|
||||
RtpCapabilities video_capabilities =
|
||||
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
const auto it = std::find_if(
|
||||
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
|
||||
[](const auto& c) { return c.name == cricket::kRtxCodecName; });
|
||||
EXPECT_TRUE(it != video_capabilities.codecs.end());
|
||||
}
|
||||
|
||||
TEST(PeerConnectionFactoryTestInternal,
|
||||
CheckRtpReceiverRtxDisabledCapabilities) {
|
||||
auto factory = CreatePeerConnectionFactoryWithRtxDisabled();
|
||||
RtpCapabilities video_capabilities =
|
||||
factory->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
|
||||
const auto it = std::find_if(
|
||||
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
|
||||
[](const auto& c) { return c.name == cricket::kRtxCodecName; });
|
||||
EXPECT_TRUE(it == video_capabilities.codecs.end());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverDataCapabilities) {
|
||||
RtpCapabilities data_capabilities =
|
||||
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_DATA);
|
||||
EXPECT_TRUE(data_capabilities.codecs.empty());
|
||||
EXPECT_TRUE(data_capabilities.header_extensions.empty());
|
||||
}
|
||||
|
||||
// This test verifies creation of PeerConnection with valid STUN and TURN
|
||||
// configuration. Also verifies the URL's parsed correctly as expected.
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = kStunIceServer;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kTurnIceServer;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kTurnIceServerWithTransport;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
cricket::ServerAddresses stun_servers;
|
||||
rtc::SocketAddress stun1("stun.l.google.com", 19302);
|
||||
stun_servers.insert(stun1);
|
||||
VerifyStunServers(stun_servers);
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn1("test.com", 1234, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_UDP);
|
||||
turn_servers.push_back(turn1);
|
||||
cricket::RelayServerConfig turn2("hello.com", kDefaultStunPort, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_TCP);
|
||||
turn_servers.push_back(turn2);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
// This test verifies creation of PeerConnection with valid STUN and TURN
|
||||
// configuration. Also verifies the list of URL's parsed correctly as expected.
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.urls.push_back(kStunIceServer);
|
||||
ice_server.urls.push_back(kTurnIceServer);
|
||||
ice_server.urls.push_back(kTurnIceServerWithTransport);
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
cricket::ServerAddresses stun_servers;
|
||||
rtc::SocketAddress stun1("stun.l.google.com", 19302);
|
||||
stun_servers.insert(stun1);
|
||||
VerifyStunServers(stun_servers);
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn1("test.com", 1234, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_UDP);
|
||||
turn_servers.push_back(turn1);
|
||||
cricket::RelayServerConfig turn2("hello.com", kDefaultStunPort, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_TCP);
|
||||
turn_servers.push_back(turn2);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = kStunIceServer;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kTurnIceServerWithNoUsernameInUri;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn("test.com", 1234, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_UDP);
|
||||
turn_servers.push_back(turn);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
// This test verifies the PeerConnection created properly with TURN url which
|
||||
// has transport parameter in it.
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = kTurnIceServerWithTransport;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn("hello.com", kDefaultStunPort, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_TCP);
|
||||
turn_servers.push_back(turn);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = kSecureTurnIceServer;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kSecureTurnIceServerWithoutTransportParam;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kSecureTurnIceServerWithoutTransportAndPortParam;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn1("hello.com", kDefaultStunTlsPort,
|
||||
kTurnUsername, kTurnPassword,
|
||||
cricket::PROTO_TLS);
|
||||
turn_servers.push_back(turn1);
|
||||
// TURNS with transport param should be default to tcp.
|
||||
cricket::RelayServerConfig turn2("hello.com", 443, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_TLS);
|
||||
turn_servers.push_back(turn2);
|
||||
cricket::RelayServerConfig turn3("hello.com", kDefaultStunTlsPort,
|
||||
kTurnUsername, kTurnPassword,
|
||||
cricket::PROTO_TLS);
|
||||
turn_servers.push_back(turn3);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = kStunIceServerWithIPv4Address;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kStunIceServerWithIPv4AddressWithoutPort;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kStunIceServerWithIPv6Address;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kStunIceServerWithIPv6AddressWithoutPort;
|
||||
config.servers.push_back(ice_server);
|
||||
ice_server.uri = kTurnIceServerWithIPv6Address;
|
||||
ice_server.username = kTurnUsername;
|
||||
ice_server.password = kTurnPassword;
|
||||
config.servers.push_back(ice_server);
|
||||
PeerConnectionDependencies pc_dependencies(&observer_);
|
||||
pc_dependencies.cert_generator =
|
||||
std::make_unique<FakeRTCCertificateGenerator>();
|
||||
pc_dependencies.allocator = std::move(port_allocator_);
|
||||
auto result =
|
||||
factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
ASSERT_TRUE(result.ok());
|
||||
cricket::ServerAddresses stun_servers;
|
||||
rtc::SocketAddress stun1("1.2.3.4", 1234);
|
||||
stun_servers.insert(stun1);
|
||||
rtc::SocketAddress stun2("1.2.3.4", 3478);
|
||||
stun_servers.insert(stun2); // Default port
|
||||
rtc::SocketAddress stun3("2401:fa00:4::", 1234);
|
||||
stun_servers.insert(stun3);
|
||||
rtc::SocketAddress stun4("2401:fa00:4::", 3478);
|
||||
stun_servers.insert(stun4); // Default port
|
||||
VerifyStunServers(stun_servers);
|
||||
|
||||
std::vector<cricket::RelayServerConfig> turn_servers;
|
||||
cricket::RelayServerConfig turn1("2401:fa00:4::", 1234, kTurnUsername,
|
||||
kTurnPassword, cricket::PROTO_UDP);
|
||||
turn_servers.push_back(turn1);
|
||||
VerifyTurnServers(turn_servers);
|
||||
}
|
||||
|
||||
// This test verifies the captured stream is rendered locally using a
|
||||
// local video track.
|
||||
TEST_F(PeerConnectionFactoryTest, LocalRendering) {
|
||||
rtc::scoped_refptr<FakeVideoTrackSource> source =
|
||||
FakeVideoTrackSource::Create(/*is_screencast=*/false);
|
||||
|
||||
cricket::FakeFrameSource frame_source(1280, 720,
|
||||
rtc::kNumMicrosecsPerSec / 30);
|
||||
|
||||
ASSERT_TRUE(source.get() != NULL);
|
||||
rtc::scoped_refptr<VideoTrackInterface> track(
|
||||
factory_->CreateVideoTrack(source, "testlabel"));
|
||||
ASSERT_TRUE(track.get() != NULL);
|
||||
FakeVideoTrackRenderer local_renderer(track.get());
|
||||
|
||||
EXPECT_EQ(0, local_renderer.num_rendered_frames());
|
||||
source->InjectFrame(frame_source.GetFrame());
|
||||
EXPECT_EQ(1, local_renderer.num_rendered_frames());
|
||||
EXPECT_FALSE(local_renderer.black_frame());
|
||||
|
||||
track->set_enabled(false);
|
||||
source->InjectFrame(frame_source.GetFrame());
|
||||
EXPECT_EQ(2, local_renderer.num_rendered_frames());
|
||||
EXPECT_TRUE(local_renderer.black_frame());
|
||||
|
||||
track->set_enabled(true);
|
||||
source->InjectFrame(frame_source.GetFrame());
|
||||
EXPECT_EQ(3, local_renderer.num_rendered_frames());
|
||||
EXPECT_FALSE(local_renderer.black_frame());
|
||||
}
|
||||
|
||||
TEST(PeerConnectionFactoryDependenciesTest, UsesNetworkManager) {
|
||||
constexpr TimeDelta kWaitTimeout = TimeDelta::Seconds(10);
|
||||
auto mock_network_manager = std::make_unique<NiceMock<MockNetworkManager>>();
|
||||
|
||||
rtc::Event called;
|
||||
EXPECT_CALL(*mock_network_manager, StartUpdating())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(InvokeWithoutArgs([&] { called.Set(); }));
|
||||
|
||||
PeerConnectionFactoryDependencies pcf_dependencies;
|
||||
pcf_dependencies.network_manager = std::move(mock_network_manager);
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pcf =
|
||||
CreateModularPeerConnectionFactory(std::move(pcf_dependencies));
|
||||
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.ice_candidate_pool_size = 2;
|
||||
NullPeerConnectionObserver observer;
|
||||
auto pc = pcf->CreatePeerConnectionOrError(
|
||||
config, PeerConnectionDependencies(&observer));
|
||||
ASSERT_TRUE(pc.ok());
|
||||
|
||||
called.Wait(kWaitTimeout);
|
||||
}
|
||||
|
||||
TEST(PeerConnectionFactoryDependenciesTest, UsesPacketSocketFactory) {
|
||||
constexpr TimeDelta kWaitTimeout = TimeDelta::Seconds(10);
|
||||
auto mock_socket_factory =
|
||||
std::make_unique<NiceMock<rtc::MockPacketSocketFactory>>();
|
||||
|
||||
rtc::Event called;
|
||||
EXPECT_CALL(*mock_socket_factory, CreateUdpSocket(_, _, _))
|
||||
.WillOnce(InvokeWithoutArgs([&] {
|
||||
called.Set();
|
||||
return nullptr;
|
||||
}))
|
||||
.WillRepeatedly(Return(nullptr));
|
||||
|
||||
PeerConnectionFactoryDependencies pcf_dependencies;
|
||||
pcf_dependencies.packet_socket_factory = std::move(mock_socket_factory);
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pcf =
|
||||
CreateModularPeerConnectionFactory(std::move(pcf_dependencies));
|
||||
|
||||
// By default, localhost addresses are ignored, which makes tests fail if test
|
||||
// machine is offline.
|
||||
PeerConnectionFactoryInterface::Options options;
|
||||
options.network_ignore_mask = 0;
|
||||
pcf->SetOptions(options);
|
||||
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.ice_candidate_pool_size = 2;
|
||||
NullPeerConnectionObserver observer;
|
||||
auto pc = pcf->CreatePeerConnectionOrError(
|
||||
config, PeerConnectionDependencies(&observer));
|
||||
ASSERT_TRUE(pc.ok());
|
||||
|
||||
called.Wait(kWaitTimeout);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.
|
||||
*/
|
||||
|
||||
// This file contains tests that verify that field trials do what they're
|
||||
// supposed to do.
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/create_peerconnection_factory.h"
|
||||
#include "api/enable_media_with_defaults.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/stats/rtcstats_objects.h"
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/video_codecs/builtin_video_decoder_factory.h"
|
||||
#include "api/video_codecs/builtin_video_encoder_factory.h"
|
||||
#include "media/engine/webrtc_media_engine.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/test/fake_audio_capture_module.h"
|
||||
#include "pc/test/frame_generator_capturer_video_track_source.h"
|
||||
#include "pc/test/peer_connection_test_wrapper.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/internal/default_socket_server.h"
|
||||
#include "rtc_base/physical_socket_server.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/android_test_initializer.h"
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
static const int kDefaultTimeoutMs = 5000;
|
||||
|
||||
bool AddIceCandidates(PeerConnectionWrapper* peer,
|
||||
std::vector<const IceCandidateInterface*> candidates) {
|
||||
for (const auto candidate : candidates) {
|
||||
if (!peer->pc()->AddIceCandidate(candidate)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
|
||||
class PeerConnectionFieldTrialTest : public ::testing::Test {
|
||||
protected:
|
||||
typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
|
||||
|
||||
PeerConnectionFieldTrialTest()
|
||||
: clock_(Clock::GetRealTimeClock()),
|
||||
socket_server_(rtc::CreateDefaultSocketServer()),
|
||||
main_thread_(socket_server_.get()) {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = "stun:stun.l.google.com:19302";
|
||||
config_.servers.push_back(ice_server);
|
||||
config_.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
}
|
||||
|
||||
void TearDown() override { pc_factory_ = nullptr; }
|
||||
|
||||
void CreatePCFactory(std::unique_ptr<FieldTrialsView> field_trials) {
|
||||
PeerConnectionFactoryDependencies pcf_deps;
|
||||
pcf_deps.signaling_thread = rtc::Thread::Current();
|
||||
pcf_deps.trials = std::move(field_trials);
|
||||
pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
|
||||
pcf_deps.adm = FakeAudioCaptureModule::Create();
|
||||
EnableMediaWithDefaults(pcf_deps);
|
||||
pc_factory_ = CreateModularPeerConnectionFactory(std::move(pcf_deps));
|
||||
|
||||
// Allow ADAPTER_TYPE_LOOPBACK to create PeerConnections with loopback in
|
||||
// this test.
|
||||
RTC_DCHECK(pc_factory_);
|
||||
PeerConnectionFactoryInterface::Options options;
|
||||
options.network_ignore_mask = 0;
|
||||
pc_factory_->SetOptions(options);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection() {
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
auto result = pc_factory_->CreatePeerConnectionOrError(
|
||||
config_, PeerConnectionDependencies(observer.get()));
|
||||
RTC_CHECK(result.ok());
|
||||
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
return std::make_unique<PeerConnectionWrapper>(
|
||||
pc_factory_, result.MoveValue(), std::move(observer));
|
||||
}
|
||||
|
||||
Clock* const clock_;
|
||||
std::unique_ptr<rtc::SocketServer> socket_server_;
|
||||
rtc::AutoSocketServerThread main_thread_;
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_ = nullptr;
|
||||
PeerConnectionInterface::RTCConfiguration config_;
|
||||
};
|
||||
|
||||
// Tests for the dependency descriptor field trial. The dependency descriptor
|
||||
// field trial is implemented in media/engine/webrtc_video_engine.cc.
|
||||
TEST_F(PeerConnectionFieldTrialTest, EnableDependencyDescriptorAdvertised) {
|
||||
std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
|
||||
std::make_unique<test::ScopedKeyValueConfig>(
|
||||
"WebRTC-DependencyDescriptorAdvertised/Enabled/");
|
||||
CreatePCFactory(std::move(field_trials));
|
||||
|
||||
WrapperPtr caller = CreatePeerConnection();
|
||||
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
auto contents1 = offer->description()->contents();
|
||||
ASSERT_EQ(1u, contents1.size());
|
||||
|
||||
const cricket::MediaContentDescription* media_description1 =
|
||||
contents1[0].media_description();
|
||||
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
|
||||
const cricket::RtpHeaderExtensions& rtp_header_extensions1 =
|
||||
media_description1->rtp_header_extensions();
|
||||
|
||||
bool found = absl::c_find_if(rtp_header_extensions1,
|
||||
[](const RtpExtension& rtp_extension) {
|
||||
return rtp_extension.uri ==
|
||||
RtpExtension::kDependencyDescriptorUri;
|
||||
}) != rtp_header_extensions1.end();
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
// Tests that dependency descriptor RTP header extensions can be exchanged
|
||||
// via SDP munging, even if dependency descriptor field trial is disabled.
|
||||
TEST_F(PeerConnectionFieldTrialTest, InjectDependencyDescriptor) {
|
||||
std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
|
||||
std::make_unique<test::ScopedKeyValueConfig>(
|
||||
"WebRTC-DependencyDescriptorAdvertised/Disabled/");
|
||||
CreatePCFactory(std::move(field_trials));
|
||||
|
||||
WrapperPtr caller = CreatePeerConnection();
|
||||
WrapperPtr callee = CreatePeerConnection();
|
||||
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
cricket::ContentInfos& contents1 = offer->description()->contents();
|
||||
ASSERT_EQ(1u, contents1.size());
|
||||
|
||||
cricket::MediaContentDescription* media_description1 =
|
||||
contents1[0].media_description();
|
||||
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
|
||||
cricket::RtpHeaderExtensions rtp_header_extensions1 =
|
||||
media_description1->rtp_header_extensions();
|
||||
|
||||
bool found1 = absl::c_find_if(rtp_header_extensions1,
|
||||
[](const RtpExtension& rtp_extension) {
|
||||
return rtp_extension.uri ==
|
||||
RtpExtension::kDependencyDescriptorUri;
|
||||
}) != rtp_header_extensions1.end();
|
||||
EXPECT_FALSE(found1);
|
||||
|
||||
std::set<int> existing_ids;
|
||||
for (const RtpExtension& rtp_extension : rtp_header_extensions1) {
|
||||
existing_ids.insert(rtp_extension.id);
|
||||
}
|
||||
|
||||
// Find the currently unused RTP header extension ID.
|
||||
int insert_id = 1;
|
||||
std::set<int>::const_iterator iter = existing_ids.begin();
|
||||
while (true) {
|
||||
if (iter == existing_ids.end()) {
|
||||
break;
|
||||
}
|
||||
if (*iter != insert_id) {
|
||||
break;
|
||||
}
|
||||
insert_id++;
|
||||
iter++;
|
||||
}
|
||||
|
||||
rtp_header_extensions1.emplace_back(RtpExtension::kDependencyDescriptorUri,
|
||||
insert_id);
|
||||
media_description1->set_rtp_header_extensions(rtp_header_extensions1);
|
||||
|
||||
caller->SetLocalDescription(offer->Clone());
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
auto answer = callee->CreateAnswer();
|
||||
|
||||
cricket::ContentInfos& contents2 = answer->description()->contents();
|
||||
ASSERT_EQ(1u, contents2.size());
|
||||
|
||||
cricket::MediaContentDescription* media_description2 =
|
||||
contents2[0].media_description();
|
||||
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description2->type());
|
||||
cricket::RtpHeaderExtensions rtp_header_extensions2 =
|
||||
media_description2->rtp_header_extensions();
|
||||
|
||||
bool found2 = absl::c_find_if(rtp_header_extensions2,
|
||||
[](const RtpExtension& rtp_extension) {
|
||||
return rtp_extension.uri ==
|
||||
RtpExtension::kDependencyDescriptorUri;
|
||||
}) != rtp_header_extensions2.end();
|
||||
EXPECT_TRUE(found2);
|
||||
}
|
||||
|
||||
// Test that the ability to emulate degraded networks works without crashing.
|
||||
TEST_F(PeerConnectionFieldTrialTest, ApplyFakeNetworkConfig) {
|
||||
std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
|
||||
std::make_unique<test::ScopedKeyValueConfig>(
|
||||
"WebRTC-FakeNetworkSendConfig/link_capacity_kbps:500/"
|
||||
"WebRTC-FakeNetworkReceiveConfig/loss_percent:1/");
|
||||
|
||||
CreatePCFactory(std::move(field_trials));
|
||||
|
||||
WrapperPtr caller = CreatePeerConnection();
|
||||
BitrateSettings bitrate_settings;
|
||||
bitrate_settings.start_bitrate_bps = 1'000'000;
|
||||
bitrate_settings.max_bitrate_bps = 1'000'000;
|
||||
caller->pc()->SetBitrate(bitrate_settings);
|
||||
FrameGeneratorCapturerVideoTrackSource::Config config;
|
||||
auto video_track_source =
|
||||
rtc::make_ref_counted<FrameGeneratorCapturerVideoTrackSource>(
|
||||
config, clock_, /*is_screencast=*/false);
|
||||
video_track_source->Start();
|
||||
caller->AddTrack(pc_factory_->CreateVideoTrack(video_track_source, "v"));
|
||||
WrapperPtr callee = CreatePeerConnection();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
// Do the SDP negotiation, and also exchange ice candidates.
|
||||
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
||||
ASSERT_TRUE_WAIT(
|
||||
caller->signaling_state() == PeerConnectionInterface::kStable,
|
||||
kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeoutMs);
|
||||
|
||||
// Connect an ICE candidate pairs.
|
||||
ASSERT_TRUE(
|
||||
AddIceCandidates(callee.get(), caller->observer()->GetAllCandidates()));
|
||||
ASSERT_TRUE(
|
||||
AddIceCandidates(caller.get(), callee->observer()->GetAllCandidates()));
|
||||
|
||||
// This means that ICE and DTLS are connected.
|
||||
ASSERT_TRUE_WAIT(callee->IsIceConnected(), kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(caller->IsIceConnected(), kDefaultTimeoutMs);
|
||||
|
||||
// Send packets for kDefaultTimeoutMs
|
||||
WAIT(false, kDefaultTimeoutMs);
|
||||
|
||||
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtp_stats =
|
||||
caller->GetStats()->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
ASSERT_GE(outbound_rtp_stats.size(), 1u);
|
||||
ASSERT_TRUE(outbound_rtp_stats[0]->target_bitrate.has_value());
|
||||
// Link capacity is limited to 500k, so BWE is expected to be close to 500k.
|
||||
ASSERT_LE(*outbound_rtp_stats[0]->target_bitrate, 500'000 * 1.1);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* Copyright 2020 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 <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtc_event_log/rtc_event_log_factory.h"
|
||||
#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "api/rtp_transceiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "media/base/fake_media_engine.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "p2p/base/fake_port_allocator.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/test/enable_fake_media.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "rtc_base/internal/default_socket_server.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using ::testing::Combine;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Field;
|
||||
using ::testing::Return;
|
||||
using ::testing::Values;
|
||||
|
||||
class PeerConnectionHeaderExtensionTest
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<cricket::MediaType, SdpSemantics>> {
|
||||
protected:
|
||||
PeerConnectionHeaderExtensionTest()
|
||||
: socket_server_(rtc::CreateDefaultSocketServer()),
|
||||
main_thread_(socket_server_.get()),
|
||||
extensions_(
|
||||
{RtpHeaderExtensionCapability("uri1",
|
||||
1,
|
||||
RtpTransceiverDirection::kStopped),
|
||||
RtpHeaderExtensionCapability("uri2",
|
||||
2,
|
||||
RtpTransceiverDirection::kSendOnly),
|
||||
RtpHeaderExtensionCapability("uri3",
|
||||
3,
|
||||
RtpTransceiverDirection::kRecvOnly),
|
||||
RtpHeaderExtensionCapability(
|
||||
"uri4",
|
||||
4,
|
||||
RtpTransceiverDirection::kSendRecv)}) {}
|
||||
|
||||
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection(
|
||||
cricket::MediaType media_type,
|
||||
absl::optional<SdpSemantics> semantics) {
|
||||
auto media_engine = std::make_unique<cricket::FakeMediaEngine>();
|
||||
if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO)
|
||||
media_engine->fake_voice_engine()->SetRtpHeaderExtensions(extensions_);
|
||||
else
|
||||
media_engine->fake_video_engine()->SetRtpHeaderExtensions(extensions_);
|
||||
PeerConnectionFactoryDependencies factory_dependencies;
|
||||
factory_dependencies.network_thread = rtc::Thread::Current();
|
||||
factory_dependencies.worker_thread = rtc::Thread::Current();
|
||||
factory_dependencies.signaling_thread = rtc::Thread::Current();
|
||||
factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
|
||||
EnableFakeMedia(factory_dependencies, std::move(media_engine));
|
||||
|
||||
factory_dependencies.event_log_factory =
|
||||
std::make_unique<RtcEventLogFactory>();
|
||||
|
||||
auto pc_factory =
|
||||
CreateModularPeerConnectionFactory(std::move(factory_dependencies));
|
||||
|
||||
auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
|
||||
rtc::Thread::Current(),
|
||||
std::make_unique<rtc::BasicPacketSocketFactory>(socket_server_.get()),
|
||||
&field_trials_);
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
if (semantics)
|
||||
config.sdp_semantics = *semantics;
|
||||
PeerConnectionDependencies pc_dependencies(observer.get());
|
||||
pc_dependencies.allocator = std::move(fake_port_allocator);
|
||||
auto result = pc_factory->CreatePeerConnectionOrError(
|
||||
config, std::move(pc_dependencies));
|
||||
EXPECT_TRUE(result.ok());
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
return std::make_unique<PeerConnectionWrapper>(
|
||||
pc_factory, result.MoveValue(), std::move(observer));
|
||||
}
|
||||
|
||||
test::ScopedKeyValueConfig field_trials_;
|
||||
std::unique_ptr<rtc::SocketServer> socket_server_;
|
||||
rtc::AutoSocketServerThread main_thread_;
|
||||
std::vector<RtpHeaderExtensionCapability> extensions_;
|
||||
};
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, TransceiverOffersHeaderExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> wrapper =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver = wrapper->AddTransceiver(media_type);
|
||||
EXPECT_EQ(transceiver->GetHeaderExtensionsToNegotiate(), extensions_);
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest,
|
||||
SenderReceiverCapabilitiesReturnNotStoppedExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
std::unique_ptr<PeerConnectionWrapper> wrapper =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
EXPECT_THAT(wrapper->pc_factory()
|
||||
->GetRtpSenderCapabilities(media_type)
|
||||
.header_extensions,
|
||||
ElementsAre(Field(&RtpHeaderExtensionCapability::uri, "uri2"),
|
||||
Field(&RtpHeaderExtensionCapability::uri, "uri3"),
|
||||
Field(&RtpHeaderExtensionCapability::uri, "uri4")));
|
||||
EXPECT_EQ(wrapper->pc_factory()
|
||||
->GetRtpReceiverCapabilities(media_type)
|
||||
.header_extensions,
|
||||
wrapper->pc_factory()
|
||||
->GetRtpSenderCapabilities(media_type)
|
||||
.header_extensions);
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedDefaultExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> wrapper =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver = wrapper->AddTransceiver(media_type);
|
||||
auto session_description = wrapper->CreateOffer();
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3"),
|
||||
Field(&RtpExtension::uri, "uri4")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedModifiedExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> wrapper =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver = wrapper->AddTransceiver(media_type);
|
||||
auto modified_extensions = transceiver->GetHeaderExtensionsToNegotiate();
|
||||
modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv;
|
||||
modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
|
||||
EXPECT_TRUE(
|
||||
transceiver->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
|
||||
auto session_description = wrapper->CreateOffer();
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri1"),
|
||||
Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, AnswersUnstoppedModifiedExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc1 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::unique_ptr<PeerConnectionWrapper> pc2 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver1 = pc1->AddTransceiver(media_type);
|
||||
|
||||
auto offer = pc1->CreateOfferAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
pc2->SetRemoteDescription(std::move(offer));
|
||||
|
||||
ASSERT_EQ(pc2->pc()->GetTransceivers().size(), 1u);
|
||||
auto transceiver2 = pc2->pc()->GetTransceivers()[0];
|
||||
auto modified_extensions = transceiver2->GetHeaderExtensionsToNegotiate();
|
||||
// Don't offer uri4.
|
||||
modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
|
||||
transceiver2->SetHeaderExtensionsToNegotiate(modified_extensions);
|
||||
|
||||
auto answer = pc2->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
EXPECT_THAT(answer->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, NegotiatedExtensionsAreAccessible) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc1 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver1 = pc1->AddTransceiver(media_type);
|
||||
auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
|
||||
modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
|
||||
transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
|
||||
auto offer = pc1->CreateOfferAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
|
||||
std::unique_ptr<PeerConnectionWrapper> pc2 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver2 = pc2->AddTransceiver(media_type);
|
||||
pc2->SetRemoteDescription(std::move(offer));
|
||||
auto answer = pc2->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
pc1->SetRemoteDescription(std::move(answer));
|
||||
|
||||
// PC1 has exts 2-4 unstopped and PC2 has exts 1-3 unstopped -> ext 2, 3
|
||||
// survives.
|
||||
EXPECT_THAT(transceiver1->GetNegotiatedHeaderExtensions(),
|
||||
ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kStopped),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kStopped)));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, OfferedExtensionsArePerTransceiver) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc1 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver1 = pc1->AddTransceiver(media_type);
|
||||
auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
|
||||
modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
|
||||
transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
|
||||
auto transceiver2 = pc1->AddTransceiver(media_type);
|
||||
|
||||
auto session_description = pc1->CreateOffer();
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3")));
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[1]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3"),
|
||||
Field(&RtpExtension::uri, "uri4")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, RemovalAfterRenegotiation) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc1 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::unique_ptr<PeerConnectionWrapper> pc2 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver1 = pc1->AddTransceiver(media_type);
|
||||
|
||||
auto offer = pc1->CreateOfferAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
pc2->SetRemoteDescription(std::move(offer));
|
||||
auto answer = pc2->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
pc1->SetRemoteDescription(std::move(answer));
|
||||
|
||||
auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
|
||||
modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
|
||||
transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
|
||||
auto session_description = pc1->CreateOffer();
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest,
|
||||
StoppedByDefaultExtensionCanBeActivatedByRemoteSdp) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc1 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::unique_ptr<PeerConnectionWrapper> pc2 =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
auto transceiver1 = pc1->AddTransceiver(media_type);
|
||||
|
||||
auto offer = pc1->CreateOfferAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
pc2->SetRemoteDescription(std::move(offer));
|
||||
auto answer = pc2->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
std::string sdp;
|
||||
ASSERT_TRUE(answer->ToString(&sdp));
|
||||
// We support uri1 but it is stopped by default. Let the remote reactivate it.
|
||||
sdp += "a=extmap:15 uri1\r\n";
|
||||
auto modified_answer = CreateSessionDescription(SdpType::kAnswer, sdp);
|
||||
pc1->SetRemoteDescription(std::move(modified_answer));
|
||||
EXPECT_THAT(transceiver1->GetNegotiatedHeaderExtensions(),
|
||||
ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv),
|
||||
Field(&RtpHeaderExtensionCapability::direction,
|
||||
RtpTransceiverDirection::kSendRecv)));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest,
|
||||
UnknownExtensionInRemoteOfferDoesNotShowUp) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::string sdp =
|
||||
"v=0\r\n"
|
||||
"o=- 0 3 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a=fingerprint:sha-256 "
|
||||
"A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
|
||||
"AD:7E:77:43:2A:29:EC:93\r\n"
|
||||
"a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
|
||||
"a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
|
||||
sdp +=
|
||||
"m=audio 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_audio_codec/8000\r\n";
|
||||
} else {
|
||||
sdp +=
|
||||
"m=video 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_video_codec/90000\r\n";
|
||||
}
|
||||
sdp +=
|
||||
"c=IN IP4 0.0.0.0\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=sendonly\r\n"
|
||||
"a=mid:audio\r\n"
|
||||
"a=setup:actpass\r\n"
|
||||
"a=extmap:1 urn:bogus\r\n";
|
||||
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
|
||||
pc->SetRemoteDescription(std::move(offer));
|
||||
pc->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
ASSERT_GT(pc->pc()->GetTransceivers().size(), 0u);
|
||||
auto transceiver = pc->pc()->GetTransceivers()[0];
|
||||
auto negotiated = transceiver->GetNegotiatedHeaderExtensions();
|
||||
EXPECT_EQ(negotiated.size(),
|
||||
transceiver->GetHeaderExtensionsToNegotiate().size());
|
||||
// All extensions are stopped, the "bogus" one does not show up.
|
||||
for (const auto& extension : negotiated) {
|
||||
EXPECT_EQ(extension.direction, RtpTransceiverDirection::kStopped);
|
||||
EXPECT_NE(extension.uri, "urn:bogus");
|
||||
}
|
||||
}
|
||||
|
||||
// These tests are regression tests for behavior that the API
|
||||
// enables in a proper way. It conflicts with the behavior
|
||||
// of the API to only offer non-stopped extensions.
|
||||
TEST_P(PeerConnectionHeaderExtensionTest,
|
||||
SdpMungingAnswerWithoutApiUsageEnablesExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::string sdp =
|
||||
"v=0\r\n"
|
||||
"o=- 0 3 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a=fingerprint:sha-256 "
|
||||
"A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
|
||||
"AD:7E:77:43:2A:29:EC:93\r\n"
|
||||
"a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
|
||||
"a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
|
||||
sdp +=
|
||||
"m=audio 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_audio_codec/8000\r\n";
|
||||
} else {
|
||||
sdp +=
|
||||
"m=video 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_video_codec/90000\r\n";
|
||||
}
|
||||
sdp +=
|
||||
"c=IN IP4 0.0.0.0\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=mid:audio\r\n"
|
||||
"a=setup:actpass\r\n"
|
||||
"a=extmap:1 uri1\r\n";
|
||||
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
|
||||
pc->SetRemoteDescription(std::move(offer));
|
||||
auto answer =
|
||||
pc->CreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
std::string modified_sdp;
|
||||
ASSERT_TRUE(answer->ToString(&modified_sdp));
|
||||
modified_sdp += "a=extmap:1 uri1\r\n";
|
||||
auto modified_answer =
|
||||
CreateSessionDescription(SdpType::kAnswer, modified_sdp);
|
||||
ASSERT_TRUE(pc->SetLocalDescription(std::move(modified_answer)));
|
||||
|
||||
auto session_description = pc->CreateOffer();
|
||||
EXPECT_THAT(session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri1"),
|
||||
Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3"),
|
||||
Field(&RtpExtension::uri, "uri4")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest,
|
||||
SdpMungingOfferWithoutApiUsageEnablesExtensions) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
pc->AddTransceiver(media_type);
|
||||
|
||||
auto offer =
|
||||
pc->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
std::string modified_sdp;
|
||||
ASSERT_TRUE(offer->ToString(&modified_sdp));
|
||||
modified_sdp += "a=extmap:1 uri1\r\n";
|
||||
auto modified_offer = CreateSessionDescription(SdpType::kOffer, modified_sdp);
|
||||
ASSERT_TRUE(pc->SetLocalDescription(std::move(modified_offer)));
|
||||
|
||||
auto offer2 =
|
||||
pc->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
EXPECT_THAT(offer2->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions(),
|
||||
ElementsAre(Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3"),
|
||||
Field(&RtpExtension::uri, "uri4"),
|
||||
Field(&RtpExtension::uri, "uri1")));
|
||||
}
|
||||
|
||||
TEST_P(PeerConnectionHeaderExtensionTest, EnablingExtensionsAfterRemoteOffer) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = GetParam();
|
||||
if (semantics != SdpSemantics::kUnifiedPlan)
|
||||
return;
|
||||
std::unique_ptr<PeerConnectionWrapper> pc =
|
||||
CreatePeerConnection(media_type, semantics);
|
||||
std::string sdp =
|
||||
"v=0\r\n"
|
||||
"o=- 0 3 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a=fingerprint:sha-256 "
|
||||
"A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
|
||||
"AD:7E:77:43:2A:29:EC:93\r\n"
|
||||
"a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
|
||||
"a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
|
||||
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
|
||||
sdp +=
|
||||
"m=audio 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_audio_codec/8000\r\n";
|
||||
} else {
|
||||
sdp +=
|
||||
"m=video 9 RTP/AVPF 111\r\n"
|
||||
"a=rtpmap:111 fake_video_codec/90000\r\n";
|
||||
}
|
||||
sdp +=
|
||||
"c=IN IP4 0.0.0.0\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=mid:audio\r\n"
|
||||
"a=setup:actpass\r\n"
|
||||
"a=extmap:5 uri1\r\n";
|
||||
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
|
||||
pc->SetRemoteDescription(std::move(offer));
|
||||
|
||||
ASSERT_GT(pc->pc()->GetTransceivers().size(), 0u);
|
||||
auto transceiver = pc->pc()->GetTransceivers()[0];
|
||||
auto modified_extensions = transceiver->GetHeaderExtensionsToNegotiate();
|
||||
modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv;
|
||||
transceiver->SetHeaderExtensionsToNegotiate(modified_extensions);
|
||||
|
||||
pc->CreateAnswerAndSetAsLocal(
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
|
||||
auto session_description = pc->CreateOffer();
|
||||
auto extensions = session_description->description()
|
||||
->contents()[0]
|
||||
.media_description()
|
||||
->rtp_header_extensions();
|
||||
EXPECT_THAT(extensions, ElementsAre(Field(&RtpExtension::uri, "uri1"),
|
||||
Field(&RtpExtension::uri, "uri2"),
|
||||
Field(&RtpExtension::uri, "uri3"),
|
||||
Field(&RtpExtension::uri, "uri4")));
|
||||
// Check uri1's id still matches the remote id.
|
||||
EXPECT_EQ(extensions[0].id, 5);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
,
|
||||
PeerConnectionHeaderExtensionTest,
|
||||
Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
|
||||
Values(cricket::MediaType::MEDIA_TYPE_AUDIO,
|
||||
cricket::MediaType::MEDIA_TYPE_VIDEO)),
|
||||
[](const testing::TestParamInfo<
|
||||
PeerConnectionHeaderExtensionTest::ParamType>& info) {
|
||||
cricket::MediaType media_type;
|
||||
SdpSemantics semantics;
|
||||
std::tie(media_type, semantics) = info.param;
|
||||
return (rtc::StringBuilder("With")
|
||||
<< (semantics == SdpSemantics::kPlanB_DEPRECATED ? "PlanB"
|
||||
: "UnifiedPlan")
|
||||
<< "And"
|
||||
<< (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ? "Voice"
|
||||
: "Video")
|
||||
<< "Engine")
|
||||
.str();
|
||||
});
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,781 @@
|
|||
/*
|
||||
* 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 <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/jsep_session_description.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/test/mock_async_dns_resolver.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/client/basic_port_allocator.h"
|
||||
#include "pc/peer_connection.h"
|
||||
#include "pc/peer_connection_factory.h"
|
||||
#include "pc/peer_connection_proxy.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/sdp_utils.h"
|
||||
#include "pc/test/enable_fake_media.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "pc/usage_pattern.h"
|
||||
#include "pc/webrtc_sdp.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/fake_mdns_responder.h"
|
||||
#include "rtc_base/fake_network.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/mdns_responder_interface.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/virtual_socket_server.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Values;
|
||||
|
||||
static const char kUsagePatternMetric[] = "WebRTC.PeerConnection.UsagePattern";
|
||||
static constexpr int kDefaultTimeout = 10000;
|
||||
static const rtc::SocketAddress kLocalAddrs[2] = {
|
||||
rtc::SocketAddress("1.1.1.1", 0), rtc::SocketAddress("2.2.2.2", 0)};
|
||||
static const rtc::SocketAddress kPrivateLocalAddress("10.1.1.1", 0);
|
||||
static const rtc::SocketAddress kPrivateIpv6LocalAddress("fd12:3456:789a:1::1",
|
||||
0);
|
||||
|
||||
int MakeUsageFingerprint(std::set<UsageEvent> events) {
|
||||
int signature = 0;
|
||||
for (const auto it : events) {
|
||||
signature |= static_cast<int>(it);
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
class PeerConnectionFactoryForUsageHistogramTest
|
||||
: public PeerConnectionFactory {
|
||||
public:
|
||||
PeerConnectionFactoryForUsageHistogramTest()
|
||||
: PeerConnectionFactory([] {
|
||||
PeerConnectionFactoryDependencies dependencies;
|
||||
dependencies.network_thread = rtc::Thread::Current();
|
||||
dependencies.worker_thread = rtc::Thread::Current();
|
||||
dependencies.signaling_thread = rtc::Thread::Current();
|
||||
dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
|
||||
EnableFakeMedia(dependencies);
|
||||
return dependencies;
|
||||
}()) {}
|
||||
};
|
||||
|
||||
class PeerConnectionWrapperForUsageHistogramTest;
|
||||
|
||||
typedef PeerConnectionWrapperForUsageHistogramTest* RawWrapperPtr;
|
||||
|
||||
class ObserverForUsageHistogramTest : public MockPeerConnectionObserver {
|
||||
public:
|
||||
void OnIceCandidate(const IceCandidateInterface* candidate) override;
|
||||
|
||||
void OnInterestingUsage(int usage_pattern) override {
|
||||
interesting_usage_detected_ = usage_pattern;
|
||||
}
|
||||
|
||||
void PrepareToExchangeCandidates(RawWrapperPtr other) {
|
||||
candidate_target_ = other;
|
||||
}
|
||||
|
||||
bool HaveDataChannel() { return last_datachannel_ != nullptr; }
|
||||
|
||||
absl::optional<int> interesting_usage_detected() {
|
||||
return interesting_usage_detected_;
|
||||
}
|
||||
|
||||
void ClearInterestingUsageDetector() {
|
||||
interesting_usage_detected_ = absl::optional<int>();
|
||||
}
|
||||
|
||||
bool candidate_gathered() const { return candidate_gathered_; }
|
||||
|
||||
private:
|
||||
absl::optional<int> interesting_usage_detected_;
|
||||
bool candidate_gathered_ = false;
|
||||
RawWrapperPtr candidate_target_; // Note: Not thread-safe against deletions.
|
||||
};
|
||||
|
||||
class PeerConnectionWrapperForUsageHistogramTest
|
||||
: public PeerConnectionWrapper {
|
||||
public:
|
||||
using PeerConnectionWrapper::PeerConnectionWrapper;
|
||||
|
||||
PeerConnection* GetInternalPeerConnection() {
|
||||
auto* pci =
|
||||
static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
|
||||
pc());
|
||||
return static_cast<PeerConnection*>(pci->internal());
|
||||
}
|
||||
|
||||
// Override with different return type
|
||||
ObserverForUsageHistogramTest* observer() {
|
||||
return static_cast<ObserverForUsageHistogramTest*>(
|
||||
PeerConnectionWrapper::observer());
|
||||
}
|
||||
|
||||
void PrepareToExchangeCandidates(
|
||||
PeerConnectionWrapperForUsageHistogramTest* other) {
|
||||
observer()->PrepareToExchangeCandidates(other);
|
||||
other->observer()->PrepareToExchangeCandidates(this);
|
||||
}
|
||||
|
||||
bool IsConnected() {
|
||||
return pc()->ice_connection_state() ==
|
||||
PeerConnectionInterface::kIceConnectionConnected ||
|
||||
pc()->ice_connection_state() ==
|
||||
PeerConnectionInterface::kIceConnectionCompleted;
|
||||
}
|
||||
|
||||
bool HaveDataChannel() {
|
||||
return static_cast<ObserverForUsageHistogramTest*>(observer())
|
||||
->HaveDataChannel();
|
||||
}
|
||||
void BufferIceCandidate(const IceCandidateInterface* candidate) {
|
||||
std::string sdp;
|
||||
EXPECT_TRUE(candidate->ToString(&sdp));
|
||||
std::unique_ptr<IceCandidateInterface> candidate_copy(CreateIceCandidate(
|
||||
candidate->sdp_mid(), candidate->sdp_mline_index(), sdp, nullptr));
|
||||
buffered_candidates_.push_back(std::move(candidate_copy));
|
||||
}
|
||||
|
||||
void AddBufferedIceCandidates() {
|
||||
for (const auto& candidate : buffered_candidates_) {
|
||||
EXPECT_TRUE(pc()->AddIceCandidate(candidate.get()));
|
||||
}
|
||||
buffered_candidates_.clear();
|
||||
}
|
||||
|
||||
// This method performs the following actions in sequence:
|
||||
// 1. Exchange Offer and Answer.
|
||||
// 2. Exchange ICE candidates after both caller and callee complete
|
||||
// gathering.
|
||||
// 3. Wait for ICE to connect.
|
||||
//
|
||||
// This guarantees a deterministic sequence of events and also rules out the
|
||||
// occurrence of prflx candidates if the offer/answer signaling and the
|
||||
// candidate trickling race in order. In case prflx candidates need to be
|
||||
// simulated, see the approach used by tests below for that.
|
||||
bool ConnectTo(PeerConnectionWrapperForUsageHistogramTest* callee) {
|
||||
PrepareToExchangeCandidates(callee);
|
||||
if (!ExchangeOfferAnswerWith(callee)) {
|
||||
return false;
|
||||
}
|
||||
// Wait until the gathering completes before we signal the candidate.
|
||||
WAIT(observer()->ice_gathering_complete_, kDefaultTimeout);
|
||||
WAIT(callee->observer()->ice_gathering_complete_, kDefaultTimeout);
|
||||
AddBufferedIceCandidates();
|
||||
callee->AddBufferedIceCandidates();
|
||||
WAIT(IsConnected(), kDefaultTimeout);
|
||||
WAIT(callee->IsConnected(), kDefaultTimeout);
|
||||
return IsConnected() && callee->IsConnected();
|
||||
}
|
||||
|
||||
bool GenerateOfferAndCollectCandidates() {
|
||||
auto offer = CreateOffer(RTCOfferAnswerOptions());
|
||||
if (!offer) {
|
||||
return false;
|
||||
}
|
||||
bool set_local_offer =
|
||||
SetLocalDescription(CloneSessionDescription(offer.get()));
|
||||
EXPECT_TRUE(set_local_offer);
|
||||
if (!set_local_offer) {
|
||||
return false;
|
||||
}
|
||||
EXPECT_TRUE_WAIT(observer()->ice_gathering_complete_, kDefaultTimeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
PeerConnectionInterface::IceGatheringState ice_gathering_state() {
|
||||
return pc()->ice_gathering_state();
|
||||
}
|
||||
|
||||
private:
|
||||
// Candidates that have been sent but not yet configured
|
||||
std::vector<std::unique_ptr<IceCandidateInterface>> buffered_candidates_;
|
||||
};
|
||||
|
||||
// Buffers candidates until we add them via AddBufferedIceCandidates.
|
||||
void ObserverForUsageHistogramTest::OnIceCandidate(
|
||||
const IceCandidateInterface* candidate) {
|
||||
// If target is not set, ignore. This happens in one-ended unit tests.
|
||||
if (candidate_target_) {
|
||||
this->candidate_target_->BufferIceCandidate(candidate);
|
||||
}
|
||||
candidate_gathered_ = true;
|
||||
}
|
||||
|
||||
class PeerConnectionUsageHistogramTest : public ::testing::Test {
|
||||
protected:
|
||||
typedef std::unique_ptr<PeerConnectionWrapperForUsageHistogramTest>
|
||||
WrapperPtr;
|
||||
|
||||
PeerConnectionUsageHistogramTest()
|
||||
: vss_(new rtc::VirtualSocketServer()),
|
||||
socket_factory_(new rtc::BasicPacketSocketFactory(vss_.get())),
|
||||
main_(vss_.get()) {
|
||||
metrics::Reset();
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection() {
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
return CreatePeerConnection(
|
||||
config, PeerConnectionFactoryInterface::Options(), nullptr);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
||||
return CreatePeerConnection(
|
||||
config, PeerConnectionFactoryInterface::Options(), nullptr);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnectionWithMdns(const RTCConfiguration& config) {
|
||||
auto resolver_factory =
|
||||
std::make_unique<NiceMock<MockAsyncDnsResolverFactory>>();
|
||||
|
||||
PeerConnectionDependencies deps(nullptr /* observer_in */);
|
||||
|
||||
auto fake_network = NewFakeNetwork();
|
||||
fake_network->set_mdns_responder(
|
||||
std::make_unique<FakeMdnsResponder>(rtc::Thread::Current()));
|
||||
fake_network->AddInterface(NextLocalAddress());
|
||||
|
||||
std::unique_ptr<cricket::BasicPortAllocator> port_allocator(
|
||||
new cricket::BasicPortAllocator(fake_network, socket_factory_.get()));
|
||||
|
||||
deps.async_dns_resolver_factory = std::move(resolver_factory);
|
||||
deps.allocator = std::move(port_allocator);
|
||||
|
||||
return CreatePeerConnection(
|
||||
config, PeerConnectionFactoryInterface::Options(), std::move(deps));
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnectionWithImmediateReport() {
|
||||
RTCConfiguration configuration;
|
||||
configuration.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
configuration.report_usage_pattern_delay_ms = 0;
|
||||
return CreatePeerConnection(
|
||||
configuration, PeerConnectionFactoryInterface::Options(), nullptr);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnectionWithPrivateLocalAddresses() {
|
||||
auto* fake_network = NewFakeNetwork();
|
||||
fake_network->AddInterface(NextLocalAddress());
|
||||
fake_network->AddInterface(kPrivateLocalAddress);
|
||||
|
||||
auto port_allocator = std::make_unique<cricket::BasicPortAllocator>(
|
||||
fake_network, socket_factory_.get());
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
return CreatePeerConnection(config,
|
||||
PeerConnectionFactoryInterface::Options(),
|
||||
std::move(port_allocator));
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnectionWithPrivateIpv6LocalAddresses() {
|
||||
auto* fake_network = NewFakeNetwork();
|
||||
fake_network->AddInterface(NextLocalAddress());
|
||||
fake_network->AddInterface(kPrivateIpv6LocalAddress);
|
||||
|
||||
auto port_allocator = std::make_unique<cricket::BasicPortAllocator>(
|
||||
fake_network, socket_factory_.get());
|
||||
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
return CreatePeerConnection(config,
|
||||
PeerConnectionFactoryInterface::Options(),
|
||||
std::move(port_allocator));
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(
|
||||
const RTCConfiguration& config,
|
||||
const PeerConnectionFactoryInterface::Options factory_options,
|
||||
std::unique_ptr<cricket::PortAllocator> allocator) {
|
||||
PeerConnectionDependencies deps(nullptr);
|
||||
deps.allocator = std::move(allocator);
|
||||
|
||||
return CreatePeerConnection(config, factory_options, std::move(deps));
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(
|
||||
const RTCConfiguration& config,
|
||||
const PeerConnectionFactoryInterface::Options factory_options,
|
||||
PeerConnectionDependencies deps) {
|
||||
auto pc_factory =
|
||||
rtc::make_ref_counted<PeerConnectionFactoryForUsageHistogramTest>();
|
||||
pc_factory->SetOptions(factory_options);
|
||||
|
||||
// If no allocator is provided, one will be created using a network manager
|
||||
// that uses the host network. This doesn't work on all trybots.
|
||||
if (!deps.allocator) {
|
||||
auto fake_network = NewFakeNetwork();
|
||||
fake_network->AddInterface(NextLocalAddress());
|
||||
deps.allocator = std::make_unique<cricket::BasicPortAllocator>(
|
||||
fake_network, socket_factory_.get());
|
||||
}
|
||||
|
||||
auto observer = std::make_unique<ObserverForUsageHistogramTest>();
|
||||
deps.observer = observer.get();
|
||||
|
||||
auto result =
|
||||
pc_factory->CreatePeerConnectionOrError(config, std::move(deps));
|
||||
if (!result.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
auto wrapper = std::make_unique<PeerConnectionWrapperForUsageHistogramTest>(
|
||||
pc_factory, result.MoveValue(), std::move(observer));
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
int ObservedFingerprint() {
|
||||
// This works correctly only if there is only one sample value
|
||||
// that has been counted.
|
||||
// Returns -1 for "not found".
|
||||
return metrics::MinSample(kUsagePatternMetric);
|
||||
}
|
||||
|
||||
// The PeerConnection's port allocator is tied to the PeerConnection's
|
||||
// lifetime and expects the underlying NetworkManager to outlive it. That
|
||||
// prevents us from having the PeerConnectionWrapper own the fake network.
|
||||
// Therefore, the test fixture will own all the fake networks even though
|
||||
// tests should access the fake network through the PeerConnectionWrapper.
|
||||
rtc::FakeNetworkManager* NewFakeNetwork() {
|
||||
fake_networks_.emplace_back(std::make_unique<rtc::FakeNetworkManager>());
|
||||
return fake_networks_.back().get();
|
||||
}
|
||||
|
||||
rtc::SocketAddress NextLocalAddress() {
|
||||
RTC_DCHECK(next_local_address_ < (int)arraysize(kLocalAddrs));
|
||||
return kLocalAddrs[next_local_address_++];
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
|
||||
int next_local_address_ = 0;
|
||||
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
||||
std::unique_ptr<rtc::BasicPacketSocketFactory> socket_factory_;
|
||||
rtc::AutoSocketServerThread main_;
|
||||
};
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) {
|
||||
auto pc = CreatePeerConnectionWithImmediateReport();
|
||||
|
||||
int expected_fingerprint = MakeUsageFingerprint({});
|
||||
EXPECT_METRIC_EQ_WAIT(1, metrics::NumSamples(kUsagePatternMetric),
|
||||
kDefaultTimeout);
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
|
||||
}
|
||||
|
||||
#ifndef WEBRTC_ANDROID
|
||||
// These tests do not work on Android. Why is unclear.
|
||||
// https://bugs.webrtc.org/9461
|
||||
|
||||
// Test getting the usage fingerprint for an audio/video connection.
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintAudioVideo) {
|
||||
auto caller = CreatePeerConnection();
|
||||
auto callee = CreatePeerConnection();
|
||||
caller->AddAudioTrack("audio");
|
||||
caller->AddVideoTrack("video");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED,
|
||||
UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
// In this case, we may or may not have PRIVATE_CANDIDATE_COLLECTED,
|
||||
// depending on the machine configuration.
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_TRUE(
|
||||
metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == 2 ||
|
||||
metrics::NumEvents(
|
||||
kUsagePatternMetric,
|
||||
expected_fingerprint |
|
||||
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2);
|
||||
}
|
||||
|
||||
// Test getting the usage fingerprint when the caller collects an mDNS
|
||||
// candidate.
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCaller) {
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
|
||||
// Enable hostname candidates with mDNS names.
|
||||
auto caller = CreatePeerConnectionWithMdns(config);
|
||||
auto callee = CreatePeerConnection(config);
|
||||
|
||||
caller->AddAudioTrack("audio");
|
||||
caller->AddVideoTrack("video");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
|
||||
int expected_fingerprint_caller = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED,
|
||||
UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED,
|
||||
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
|
||||
// Without a resolver, the callee cannot resolve the received mDNS candidate
|
||||
// but can still connect with the caller via a prflx candidate. As a result,
|
||||
// the bit for the direct connection should not be logged.
|
||||
int expected_fingerprint_callee = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED,
|
||||
UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
|
||||
}
|
||||
|
||||
// Test getting the usage fingerprint when the callee collects an mDNS
|
||||
// candidate.
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCallee) {
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
|
||||
// Enable hostname candidates with mDNS names.
|
||||
auto caller = CreatePeerConnection(config);
|
||||
auto callee = CreatePeerConnectionWithMdns(config);
|
||||
|
||||
caller->AddAudioTrack("audio");
|
||||
caller->AddVideoTrack("video");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
|
||||
// Similar to the test above, the caller connects with the callee via a prflx
|
||||
// candidate.
|
||||
int expected_fingerprint_caller = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED,
|
||||
UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::CLOSE_CALLED});
|
||||
|
||||
int expected_fingerprint_callee = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED,
|
||||
UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED,
|
||||
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_HAVE_SCTP
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintDataOnly) {
|
||||
auto caller = CreatePeerConnection();
|
||||
auto callee = CreatePeerConnection();
|
||||
caller->CreateDataChannel("foodata");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
ASSERT_TRUE_WAIT(callee->HaveDataChannel(), kDefaultTimeout);
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_TRUE(
|
||||
metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == 2 ||
|
||||
metrics::NumEvents(
|
||||
kUsagePatternMetric,
|
||||
expected_fingerprint |
|
||||
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2);
|
||||
}
|
||||
#endif // WEBRTC_HAVE_SCTP
|
||||
#endif // WEBRTC_ANDROID
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) {
|
||||
RTCConfiguration configuration;
|
||||
configuration.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnection::IceServer server;
|
||||
server.urls = {"stun:dummy.stun.server"};
|
||||
configuration.servers.push_back(server);
|
||||
server.urls = {"turn:dummy.turn.server"};
|
||||
server.username = "username";
|
||||
server.password = "password";
|
||||
configuration.servers.push_back(server);
|
||||
auto caller = CreatePeerConnection(configuration);
|
||||
ASSERT_TRUE(caller);
|
||||
caller->pc()->Close();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED,
|
||||
UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) {
|
||||
RTCConfiguration configuration;
|
||||
configuration.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnection::IceServer server;
|
||||
server.urls = {"stun:dummy.stun.server"};
|
||||
configuration.servers.push_back(server);
|
||||
server.urls = {"turn:dummy.turn.server"};
|
||||
server.username = "username";
|
||||
server.password = "password";
|
||||
configuration.servers.push_back(server);
|
||||
auto caller = CreatePeerConnection();
|
||||
ASSERT_TRUE(caller);
|
||||
ASSERT_TRUE(caller->pc()->SetConfiguration(configuration).ok());
|
||||
caller->pc()->Close();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED,
|
||||
UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIPCaller) {
|
||||
auto caller = CreatePeerConnectionWithPrivateLocalAddresses();
|
||||
auto callee = CreatePeerConnection();
|
||||
caller->AddAudioTrack("audio");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
|
||||
int expected_fingerprint_caller = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED,
|
||||
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
|
||||
int expected_fingerprint_callee = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED,
|
||||
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIpv6Callee) {
|
||||
auto caller = CreatePeerConnection();
|
||||
auto callee = CreatePeerConnectionWithPrivateIpv6LocalAddresses();
|
||||
caller->AddAudioTrack("audio");
|
||||
ASSERT_TRUE(caller->ConnectTo(callee.get()));
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
|
||||
int expected_fingerprint_caller = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED,
|
||||
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
|
||||
int expected_fingerprint_callee = MakeUsageFingerprint(
|
||||
{UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED,
|
||||
UsageEvent::IPV6_CANDIDATE_COLLECTED,
|
||||
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
|
||||
}
|
||||
|
||||
#ifndef WEBRTC_ANDROID
|
||||
#ifdef WEBRTC_HAVE_SCTP
|
||||
// Test that the usage pattern bits for adding remote (private IPv6) candidates
|
||||
// are set when the remote candidates are retrieved from the Offer SDP instead
|
||||
// of trickled ICE messages.
|
||||
TEST_F(PeerConnectionUsageHistogramTest,
|
||||
AddRemoteCandidatesFromRemoteDescription) {
|
||||
// We construct the following data-channel-only scenario. The caller collects
|
||||
// IPv6 private local candidates and appends them in the Offer as in
|
||||
// non-trickled sessions. The callee collects mDNS candidates that are not
|
||||
// contained in the Answer as in Trickle ICE. Only the Offer and Answer are
|
||||
// signaled and we expect a connection with prflx remote candidates at the
|
||||
// caller side.
|
||||
auto caller = CreatePeerConnectionWithPrivateIpv6LocalAddresses();
|
||||
RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
auto callee = CreatePeerConnectionWithMdns(config);
|
||||
caller->CreateDataChannel("test_channel");
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
// Wait until the gathering completes so that the session description would
|
||||
// have contained ICE candidates.
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
|
||||
caller->ice_gathering_state(), kDefaultTimeout);
|
||||
EXPECT_TRUE(caller->observer()->candidate_gathered());
|
||||
// Get the current offer that contains candidates and pass it to the callee.
|
||||
//
|
||||
// Note that we cannot use CloneSessionDescription on `cur_offer` to obtain an
|
||||
// SDP with candidates. The method above does not strictly copy everything, in
|
||||
// particular, not copying the ICE candidates.
|
||||
// TODO(qingsi): Technically, this is a bug. Fix it.
|
||||
auto cur_offer = caller->pc()->local_description();
|
||||
ASSERT_TRUE(cur_offer);
|
||||
std::string sdp_with_candidates_str;
|
||||
cur_offer->ToString(&sdp_with_candidates_str);
|
||||
auto offer = std::make_unique<JsepSessionDescription>(SdpType::kOffer);
|
||||
ASSERT_TRUE(SdpDeserialize(sdp_with_candidates_str, offer.get(),
|
||||
nullptr /* error */));
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
// By default, the Answer created does not contain ICE candidates.
|
||||
auto answer = callee->CreateAnswer();
|
||||
callee->SetLocalDescription(CloneSessionDescription(answer.get()));
|
||||
caller->SetRemoteDescription(std::move(answer));
|
||||
EXPECT_TRUE_WAIT(caller->IsConnected(), kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT(callee->IsConnected(), kDefaultTimeout);
|
||||
// The callee needs to process the open message to have the data channel open.
|
||||
EXPECT_TRUE_WAIT(callee->observer()->last_datachannel_ != nullptr,
|
||||
kDefaultTimeout);
|
||||
caller->pc()->Close();
|
||||
callee->pc()->Close();
|
||||
|
||||
// The caller should not have added any remote candidate either via
|
||||
// AddIceCandidate or from the remote description. Also, the caller connects
|
||||
// with the callee via a prflx candidate and hence no direct connection bit
|
||||
// should be set.
|
||||
int expected_fingerprint_caller = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED,
|
||||
UsageEvent::IPV6_CANDIDATE_COLLECTED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::CLOSE_CALLED});
|
||||
|
||||
int expected_fingerprint_callee = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED,
|
||||
UsageEvent::REMOTE_CANDIDATE_ADDED,
|
||||
UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED,
|
||||
UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
|
||||
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
|
||||
EXPECT_METRIC_EQ(
|
||||
1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, NotableUsageNoted) {
|
||||
auto caller = CreatePeerConnection();
|
||||
caller->CreateDataChannel("foo");
|
||||
caller->GenerateOfferAndCollectCandidates();
|
||||
caller->pc()->Close();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
|
||||
EXPECT_METRIC_TRUE(
|
||||
expected_fingerprint == ObservedFingerprint() ||
|
||||
(expected_fingerprint |
|
||||
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) ==
|
||||
ObservedFingerprint());
|
||||
EXPECT_METRIC_EQ(absl::make_optional(ObservedFingerprint()),
|
||||
caller->observer()->interesting_usage_detected());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest, NotableUsageOnEventFiring) {
|
||||
auto caller = CreatePeerConnection();
|
||||
caller->CreateDataChannel("foo");
|
||||
caller->GenerateOfferAndCollectCandidates();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED});
|
||||
EXPECT_METRIC_EQ(0, metrics::NumSamples(kUsagePatternMetric));
|
||||
caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting();
|
||||
EXPECT_METRIC_EQ_WAIT(1, metrics::NumSamples(kUsagePatternMetric),
|
||||
kDefaultTimeout);
|
||||
EXPECT_METRIC_TRUE(
|
||||
expected_fingerprint == ObservedFingerprint() ||
|
||||
(expected_fingerprint |
|
||||
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) ==
|
||||
ObservedFingerprint());
|
||||
EXPECT_METRIC_EQ(absl::make_optional(ObservedFingerprint()),
|
||||
caller->observer()->interesting_usage_detected());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionUsageHistogramTest,
|
||||
NoNotableUsageOnEventFiringAfterClose) {
|
||||
auto caller = CreatePeerConnection();
|
||||
caller->CreateDataChannel("foo");
|
||||
caller->GenerateOfferAndCollectCandidates();
|
||||
int expected_fingerprint = MakeUsageFingerprint(
|
||||
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
|
||||
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED});
|
||||
EXPECT_METRIC_EQ(0, metrics::NumSamples(kUsagePatternMetric));
|
||||
caller->pc()->Close();
|
||||
EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
|
||||
caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting();
|
||||
caller->observer()->ClearInterestingUsageDetector();
|
||||
EXPECT_METRIC_EQ_WAIT(2, metrics::NumSamples(kUsagePatternMetric),
|
||||
kDefaultTimeout);
|
||||
EXPECT_METRIC_TRUE(
|
||||
expected_fingerprint == ObservedFingerprint() ||
|
||||
(expected_fingerprint |
|
||||
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) ==
|
||||
ObservedFingerprint());
|
||||
// After close, the usage-detection callback should NOT have been called.
|
||||
EXPECT_METRIC_FALSE(caller->observer()->interesting_usage_detected());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace webrtc
|
||||
1587
TMessagesProj/jni/voip/webrtc/pc/peer_connection_ice_unittest.cc
Normal file
1587
TMessagesProj/jni/voip/webrtc/pc/peer_connection_ice_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
3877
TMessagesProj/jni/voip/webrtc/pc/peer_connection_integrationtest.cc
Normal file
3877
TMessagesProj/jni/voip/webrtc/pc/peer_connection_integrationtest.cc
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
192
TMessagesProj/jni/voip/webrtc/pc/peer_connection_internal.h
Normal file
192
TMessagesProj/jni/voip/webrtc/pc/peer_connection_internal.h
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_INTERNAL_H_
|
||||
#define PC_PEER_CONNECTION_INTERNAL_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "call/call.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "pc/jsep_transport_controller.h"
|
||||
#include "pc/peer_connection_message_handler.h"
|
||||
#include "pc/rtp_transceiver.h"
|
||||
#include "pc/rtp_transmission_manager.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class DataChannelController;
|
||||
class LegacyStatsCollector;
|
||||
|
||||
// This interface defines the functions that are needed for
|
||||
// SdpOfferAnswerHandler to access PeerConnection internal state.
|
||||
class PeerConnectionSdpMethods {
|
||||
public:
|
||||
virtual ~PeerConnectionSdpMethods() = default;
|
||||
|
||||
// The SDP session ID as defined by RFC 3264.
|
||||
virtual std::string session_id() const = 0;
|
||||
|
||||
// Returns true if the ICE restart flag above was set, and no ICE restart has
|
||||
// occurred yet for this transport (by applying a local description with
|
||||
// changed ufrag/password). If the transport has been deleted as a result of
|
||||
// bundling, returns false.
|
||||
virtual bool NeedsIceRestart(const std::string& content_name) const = 0;
|
||||
|
||||
virtual absl::optional<std::string> sctp_mid() const = 0;
|
||||
|
||||
// Functions below this comment are known to only be accessed
|
||||
// from SdpOfferAnswerHandler.
|
||||
// Return a pointer to the active configuration.
|
||||
virtual const PeerConnectionInterface::RTCConfiguration* configuration()
|
||||
const = 0;
|
||||
|
||||
// Report the UMA metric BundleUsage for the given remote description.
|
||||
virtual void ReportSdpBundleUsage(
|
||||
const SessionDescriptionInterface& remote_description) = 0;
|
||||
|
||||
virtual PeerConnectionMessageHandler* message_handler() = 0;
|
||||
virtual RtpTransmissionManager* rtp_manager() = 0;
|
||||
virtual const RtpTransmissionManager* rtp_manager() const = 0;
|
||||
virtual bool dtls_enabled() const = 0;
|
||||
virtual const PeerConnectionFactoryInterface::Options* options() const = 0;
|
||||
|
||||
// Returns the CryptoOptions for this PeerConnection. This will always
|
||||
// return the RTCConfiguration.crypto_options if set and will only default
|
||||
// back to the PeerConnectionFactory settings if nothing was set.
|
||||
virtual CryptoOptions GetCryptoOptions() = 0;
|
||||
virtual JsepTransportController* transport_controller_s() = 0;
|
||||
virtual JsepTransportController* transport_controller_n() = 0;
|
||||
virtual DataChannelController* data_channel_controller() = 0;
|
||||
virtual cricket::PortAllocator* port_allocator() = 0;
|
||||
virtual LegacyStatsCollector* legacy_stats() = 0;
|
||||
// Returns the observer. Will crash on CHECK if the observer is removed.
|
||||
virtual PeerConnectionObserver* Observer() const = 0;
|
||||
virtual absl::optional<rtc::SSLRole> GetSctpSslRole_n() = 0;
|
||||
virtual PeerConnectionInterface::IceConnectionState
|
||||
ice_connection_state_internal() = 0;
|
||||
virtual void SetIceConnectionState(
|
||||
PeerConnectionInterface::IceConnectionState new_state) = 0;
|
||||
virtual void NoteUsageEvent(UsageEvent event) = 0;
|
||||
virtual bool IsClosed() const = 0;
|
||||
// Returns true if the PeerConnection is configured to use Unified Plan
|
||||
// semantics for creating offers/answers and setting local/remote
|
||||
// descriptions. If this is true the RtpTransceiver API will also be available
|
||||
// to the user. If this is false, Plan B semantics are assumed.
|
||||
// TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once
|
||||
// sufficient time has passed.
|
||||
virtual bool IsUnifiedPlan() const = 0;
|
||||
virtual bool ValidateBundleSettings(
|
||||
const cricket::SessionDescription* desc,
|
||||
const std::map<std::string, const cricket::ContentGroup*>&
|
||||
bundle_groups_by_mid) = 0;
|
||||
|
||||
// Internal implementation for AddTransceiver family of methods. If
|
||||
// `fire_callback` is set, fires OnRenegotiationNeeded callback if successful.
|
||||
virtual RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>
|
||||
AddTransceiver(cricket::MediaType media_type,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const RtpTransceiverInit& init,
|
||||
bool fire_callback = true) = 0;
|
||||
// Asynchronously calls SctpTransport::Start() on the network thread for
|
||||
// `sctp_mid()` if set. Called as part of setting the local description.
|
||||
virtual void StartSctpTransport(int local_port,
|
||||
int remote_port,
|
||||
int max_message_size) = 0;
|
||||
|
||||
// Asynchronously adds a remote candidate on the network thread.
|
||||
virtual void AddRemoteCandidate(const std::string& mid,
|
||||
const cricket::Candidate& candidate) = 0;
|
||||
|
||||
virtual Call* call_ptr() = 0;
|
||||
// Returns true if SRTP (either using DTLS-SRTP or SDES) is required by
|
||||
// this session.
|
||||
virtual bool SrtpRequired() const = 0;
|
||||
// Initializes the data channel transport for the peerconnection instance.
|
||||
// This will have the effect that `sctp_mid()` and `sctp_transport_name()`
|
||||
// will return a set value (even though it might be an empty string) and the
|
||||
// dc transport will be initialized on the network thread.
|
||||
virtual bool CreateDataChannelTransport(absl::string_view mid) = 0;
|
||||
// Tears down the data channel transport state and clears the `sctp_mid()` and
|
||||
// `sctp_transport_name()` properties.
|
||||
virtual void DestroyDataChannelTransport(RTCError error) = 0;
|
||||
virtual const FieldTrialsView& trials() const = 0;
|
||||
|
||||
virtual void ClearStatsCache() = 0;
|
||||
};
|
||||
|
||||
// Functions defined in this class are called by other objects,
|
||||
// but not by SdpOfferAnswerHandler.
|
||||
class PeerConnectionInternal : public PeerConnectionInterface,
|
||||
public PeerConnectionSdpMethods {
|
||||
public:
|
||||
virtual rtc::Thread* network_thread() const = 0;
|
||||
virtual rtc::Thread* worker_thread() const = 0;
|
||||
|
||||
// Returns true if we were the initial offerer.
|
||||
virtual bool initial_offerer() const = 0;
|
||||
|
||||
virtual std::vector<
|
||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
||||
GetTransceiversInternal() const = 0;
|
||||
|
||||
// Call on the network thread to fetch stats for all the data channels.
|
||||
// TODO(tommi): Make pure virtual after downstream updates.
|
||||
virtual std::vector<DataChannelStats> GetDataChannelStats() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual absl::optional<std::string> sctp_transport_name() const = 0;
|
||||
|
||||
virtual cricket::CandidateStatsList GetPooledCandidateStats() const = 0;
|
||||
|
||||
// Returns a map from transport name to transport stats for all given
|
||||
// transport names.
|
||||
// Must be called on the network thread.
|
||||
virtual std::map<std::string, cricket::TransportStats>
|
||||
GetTransportStatsByNames(const std::set<std::string>& transport_names) = 0;
|
||||
|
||||
virtual Call::Stats GetCallStats() = 0;
|
||||
|
||||
virtual absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() = 0;
|
||||
|
||||
virtual bool GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) = 0;
|
||||
virtual std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain(
|
||||
const std::string& transport_name) = 0;
|
||||
|
||||
// Returns true if there was an ICE restart initiated by the remote offer.
|
||||
virtual bool IceRestartPending(const std::string& content_name) const = 0;
|
||||
|
||||
// Get SSL role for an arbitrary m= section (handles bundling correctly).
|
||||
virtual bool GetSslRole(const std::string& content_name,
|
||||
rtc::SSLRole* role) = 0;
|
||||
// Functions needed by DataChannelController
|
||||
virtual void NoteDataAddedEvent() {}
|
||||
// Handler for sctp data channel state changes.
|
||||
// The `channel_id` is the same unique identifier as used in
|
||||
// `DataChannelStats::internal_id and
|
||||
// `RTCDataChannelStats::data_channel_identifier`.
|
||||
virtual void OnSctpDataChannelStateChanged(
|
||||
int channel_id,
|
||||
DataChannelInterface::DataState state) {}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_INTERNAL_H_
|
||||
2415
TMessagesProj/jni/voip/webrtc/pc/peer_connection_jsep_unittest.cc
Normal file
2415
TMessagesProj/jni/voip/webrtc/pc/peer_connection_jsep_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
2125
TMessagesProj/jni/voip/webrtc/pc/peer_connection_media_unittest.cc
Normal file
2125
TMessagesProj/jni/voip/webrtc/pc/peer_connection_media_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2020 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 "pc/peer_connection_message_handler.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "api/jsep.h"
|
||||
#include "api/legacy_stats_types.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "pc/legacy_stats_collector_interface.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
rtc::scoped_refptr<T> WrapScoped(T* ptr) {
|
||||
return rtc::scoped_refptr<T>(ptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void PeerConnectionMessageHandler::PostSetSessionDescriptionSuccess(
|
||||
SetSessionDescriptionObserver* observer) {
|
||||
signaling_thread_->PostTask(
|
||||
SafeTask(safety_.flag(),
|
||||
[observer = WrapScoped(observer)] { observer->OnSuccess(); }));
|
||||
}
|
||||
|
||||
void PeerConnectionMessageHandler::PostSetSessionDescriptionFailure(
|
||||
SetSessionDescriptionObserver* observer,
|
||||
RTCError&& error) {
|
||||
RTC_DCHECK(!error.ok());
|
||||
signaling_thread_->PostTask(SafeTask(
|
||||
safety_.flag(),
|
||||
[observer = WrapScoped(observer), error = std::move(error)]() mutable {
|
||||
observer->OnFailure(std::move(error));
|
||||
}));
|
||||
}
|
||||
|
||||
void PeerConnectionMessageHandler::PostCreateSessionDescriptionFailure(
|
||||
CreateSessionDescriptionObserver* observer,
|
||||
RTCError error) {
|
||||
RTC_DCHECK(!error.ok());
|
||||
// Do not protect this task with the safety_.flag() to ensure
|
||||
// observer is invoked even if the PeerConnection is destroyed early.
|
||||
signaling_thread_->PostTask(
|
||||
[observer = WrapScoped(observer), error = std::move(error)]() mutable {
|
||||
observer->OnFailure(std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
void PeerConnectionMessageHandler::PostGetStats(
|
||||
StatsObserver* observer,
|
||||
LegacyStatsCollectorInterface* legacy_stats,
|
||||
MediaStreamTrackInterface* track) {
|
||||
signaling_thread_->PostTask(
|
||||
SafeTask(safety_.flag(), [observer = WrapScoped(observer), legacy_stats,
|
||||
track = WrapScoped(track)] {
|
||||
StatsReports reports;
|
||||
legacy_stats->GetStats(track.get(), &reports);
|
||||
observer->OnComplete(reports);
|
||||
}));
|
||||
}
|
||||
|
||||
void PeerConnectionMessageHandler::RequestUsagePatternReport(
|
||||
std::function<void()> func,
|
||||
int delay_ms) {
|
||||
signaling_thread_->PostDelayedTask(SafeTask(safety_.flag(), std::move(func)),
|
||||
TimeDelta::Millis(delay_ms));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_MESSAGE_HANDLER_H_
|
||||
#define PC_PEER_CONNECTION_MESSAGE_HANDLER_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "api/jsep.h"
|
||||
#include "api/legacy_stats_types.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "pc/legacy_stats_collector_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnectionMessageHandler {
|
||||
public:
|
||||
explicit PeerConnectionMessageHandler(rtc::Thread* signaling_thread)
|
||||
: signaling_thread_(signaling_thread) {}
|
||||
~PeerConnectionMessageHandler() = default;
|
||||
|
||||
void PostSetSessionDescriptionSuccess(
|
||||
SetSessionDescriptionObserver* observer);
|
||||
void PostSetSessionDescriptionFailure(SetSessionDescriptionObserver* observer,
|
||||
RTCError&& error);
|
||||
void PostCreateSessionDescriptionFailure(
|
||||
CreateSessionDescriptionObserver* observer,
|
||||
RTCError error);
|
||||
void PostGetStats(StatsObserver* observer,
|
||||
LegacyStatsCollectorInterface* legacy_stats,
|
||||
MediaStreamTrackInterface* track);
|
||||
void RequestUsagePatternReport(std::function<void()>, int delay_ms);
|
||||
|
||||
private:
|
||||
ScopedTaskSafety safety_;
|
||||
TaskQueueBase* const signaling_thread_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_MESSAGE_HANDLER_H_
|
||||
174
TMessagesProj/jni/voip/webrtc/pc/peer_connection_proxy.h
Normal file
174
TMessagesProj/jni/voip/webrtc/pc/peer_connection_proxy.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_PEER_CONNECTION_PROXY_H_
|
||||
#define PC_PEER_CONNECTION_PROXY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/transport/bandwidth_estimation_settings.h"
|
||||
#include "pc/proxy.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// PeerConnection proxy objects will be constructed with two thread pointers,
|
||||
// signaling and network. The proxy macros don't have 'network' specific macros
|
||||
// and support for a secondary thread is provided via 'SECONDARY' macros.
|
||||
// TODO(deadbeef): Move this to .cc file. What threads methods are called on is
|
||||
// an implementation detail.
|
||||
BEGIN_PROXY_MAP(PeerConnection)
|
||||
PROXY_PRIMARY_THREAD_DESTRUCTOR()
|
||||
PROXY_METHOD0(rtc::scoped_refptr<StreamCollectionInterface>, local_streams)
|
||||
PROXY_METHOD0(rtc::scoped_refptr<StreamCollectionInterface>, remote_streams)
|
||||
PROXY_METHOD1(bool, AddStream, MediaStreamInterface*)
|
||||
PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*)
|
||||
PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>>,
|
||||
AddTrack,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
const std::vector<std::string>&)
|
||||
PROXY_METHOD3(RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>>,
|
||||
AddTrack,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
const std::vector<std::string>&,
|
||||
const std::vector<RtpEncodingParameters>&)
|
||||
PROXY_METHOD1(RTCError,
|
||||
RemoveTrackOrError,
|
||||
rtc::scoped_refptr<RtpSenderInterface>)
|
||||
PROXY_METHOD1(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
AddTransceiver,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface>)
|
||||
PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
AddTransceiver,
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
const RtpTransceiverInit&)
|
||||
PROXY_METHOD1(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
AddTransceiver,
|
||||
cricket::MediaType)
|
||||
PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
AddTransceiver,
|
||||
cricket::MediaType,
|
||||
const RtpTransceiverInit&)
|
||||
PROXY_METHOD2(rtc::scoped_refptr<RtpSenderInterface>,
|
||||
CreateSender,
|
||||
const std::string&,
|
||||
const std::string&)
|
||||
PROXY_CONSTMETHOD0(std::vector<rtc::scoped_refptr<RtpSenderInterface>>,
|
||||
GetSenders)
|
||||
PROXY_CONSTMETHOD0(std::vector<rtc::scoped_refptr<RtpReceiverInterface>>,
|
||||
GetReceivers)
|
||||
PROXY_CONSTMETHOD0(std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
GetTransceivers)
|
||||
PROXY_METHOD3(bool,
|
||||
GetStats,
|
||||
StatsObserver*,
|
||||
MediaStreamTrackInterface*,
|
||||
StatsOutputLevel)
|
||||
PROXY_METHOD1(void, GetStats, RTCStatsCollectorCallback*)
|
||||
PROXY_METHOD2(void,
|
||||
GetStats,
|
||||
rtc::scoped_refptr<RtpSenderInterface>,
|
||||
rtc::scoped_refptr<RTCStatsCollectorCallback>)
|
||||
PROXY_METHOD2(void,
|
||||
GetStats,
|
||||
rtc::scoped_refptr<RtpReceiverInterface>,
|
||||
rtc::scoped_refptr<RTCStatsCollectorCallback>)
|
||||
PROXY_METHOD0(void, ClearStatsCache)
|
||||
PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>>,
|
||||
CreateDataChannelOrError,
|
||||
const std::string&,
|
||||
const DataChannelInit*)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, remote_description)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*,
|
||||
current_local_description)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*,
|
||||
current_remote_description)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*,
|
||||
pending_local_description)
|
||||
PROXY_CONSTMETHOD0(const SessionDescriptionInterface*,
|
||||
pending_remote_description)
|
||||
PROXY_METHOD0(void, RestartIce)
|
||||
PROXY_METHOD2(void,
|
||||
CreateOffer,
|
||||
CreateSessionDescriptionObserver*,
|
||||
const RTCOfferAnswerOptions&)
|
||||
PROXY_METHOD2(void,
|
||||
CreateAnswer,
|
||||
CreateSessionDescriptionObserver*,
|
||||
const RTCOfferAnswerOptions&)
|
||||
PROXY_METHOD2(void,
|
||||
SetLocalDescription,
|
||||
std::unique_ptr<SessionDescriptionInterface>,
|
||||
rtc::scoped_refptr<SetLocalDescriptionObserverInterface>)
|
||||
PROXY_METHOD1(void,
|
||||
SetLocalDescription,
|
||||
rtc::scoped_refptr<SetLocalDescriptionObserverInterface>)
|
||||
PROXY_METHOD2(void,
|
||||
SetLocalDescription,
|
||||
SetSessionDescriptionObserver*,
|
||||
SessionDescriptionInterface*)
|
||||
PROXY_METHOD1(void, SetLocalDescription, SetSessionDescriptionObserver*)
|
||||
PROXY_METHOD2(void,
|
||||
SetRemoteDescription,
|
||||
std::unique_ptr<SessionDescriptionInterface>,
|
||||
rtc::scoped_refptr<SetRemoteDescriptionObserverInterface>)
|
||||
PROXY_METHOD2(void,
|
||||
SetRemoteDescription,
|
||||
SetSessionDescriptionObserver*,
|
||||
SessionDescriptionInterface*)
|
||||
PROXY_METHOD1(bool, ShouldFireNegotiationNeededEvent, uint32_t)
|
||||
PROXY_METHOD0(PeerConnectionInterface::RTCConfiguration, GetConfiguration)
|
||||
PROXY_METHOD1(RTCError,
|
||||
SetConfiguration,
|
||||
const PeerConnectionInterface::RTCConfiguration&)
|
||||
PROXY_METHOD1(bool, AddIceCandidate, const IceCandidateInterface*)
|
||||
PROXY_METHOD2(void,
|
||||
AddIceCandidate,
|
||||
std::unique_ptr<IceCandidateInterface>,
|
||||
std::function<void(RTCError)>)
|
||||
PROXY_METHOD1(bool, RemoveIceCandidates, const std::vector<cricket::Candidate>&)
|
||||
PROXY_METHOD1(RTCError, SetBitrate, const BitrateSettings&)
|
||||
PROXY_METHOD1(void,
|
||||
ReconfigureBandwidthEstimation,
|
||||
const BandwidthEstimationSettings&)
|
||||
PROXY_METHOD1(void, SetAudioPlayout, bool)
|
||||
PROXY_METHOD1(void, SetAudioRecording, bool)
|
||||
// This method will be invoked on the network thread. See
|
||||
// PeerConnectionFactory::CreatePeerConnectionOrError for more details.
|
||||
PROXY_SECONDARY_METHOD1(rtc::scoped_refptr<DtlsTransportInterface>,
|
||||
LookupDtlsTransportByMid,
|
||||
const std::string&)
|
||||
// This method will be invoked on the network thread. See
|
||||
// PeerConnectionFactory::CreatePeerConnectionOrError for more details.
|
||||
PROXY_SECONDARY_CONSTMETHOD0(rtc::scoped_refptr<SctpTransportInterface>,
|
||||
GetSctpTransport)
|
||||
PROXY_METHOD0(SignalingState, signaling_state)
|
||||
PROXY_METHOD0(IceConnectionState, ice_connection_state)
|
||||
PROXY_METHOD0(IceConnectionState, standardized_ice_connection_state)
|
||||
PROXY_METHOD0(PeerConnectionState, peer_connection_state)
|
||||
PROXY_METHOD0(IceGatheringState, ice_gathering_state)
|
||||
PROXY_METHOD0(absl::optional<bool>, can_trickle_ice_candidates)
|
||||
PROXY_METHOD1(void, AddAdaptationResource, rtc::scoped_refptr<Resource>)
|
||||
PROXY_METHOD2(bool,
|
||||
StartRtcEventLog,
|
||||
std::unique_ptr<RtcEventLogOutput>,
|
||||
int64_t)
|
||||
PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr<RtcEventLogOutput>)
|
||||
PROXY_METHOD0(void, StopRtcEventLog)
|
||||
PROXY_METHOD0(void, Close)
|
||||
BYPASS_PROXY_CONSTMETHOD0(rtc::Thread*, signaling_thread)
|
||||
END_PROXY_MAP(PeerConnection)
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // PC_PEER_CONNECTION_PROXY_H_
|
||||
457
TMessagesProj/jni/voip/webrtc/pc/peer_connection_rampup_tests.cc
Normal file
457
TMessagesProj/jni/voip/webrtc/pc/peer_connection_rampup_tests.cc
Normal file
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Copyright 2018 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 <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/audio_options.h"
|
||||
#include "api/create_peerconnection_factory.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/stats/rtc_stats.h"
|
||||
#include "api/stats/rtc_stats_report.h"
|
||||
#include "api/stats/rtcstats_objects.h"
|
||||
#include "api/test/metrics/global_metrics_logger_and_exporter.h"
|
||||
#include "api/test/metrics/metric.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "modules/audio_processing/include/audio_processing.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "p2p/base/test_turn_server.h"
|
||||
#include "p2p/client/basic_port_allocator.h"
|
||||
#include "pc/peer_connection.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/test/fake_audio_capture_module.h"
|
||||
#include "pc/test/frame_generator_capturer_video_track_source.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/fake_network.h"
|
||||
#include "rtc_base/firewall_socket_server.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/socket_factory.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/task_queue_for_test.h"
|
||||
#include "rtc_base/test_certificate_verifier.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/virtual_socket_server.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::webrtc::test::GetGlobalMetricsLogger;
|
||||
using ::webrtc::test::ImprovementDirection;
|
||||
using ::webrtc::test::Unit;
|
||||
|
||||
static const int kDefaultTestTimeMs = 15000;
|
||||
static const int kRampUpTimeMs = 5000;
|
||||
static const int kPollIntervalTimeMs = 50;
|
||||
static const int kDefaultTimeoutMs = 10000;
|
||||
static const rtc::SocketAddress kDefaultLocalAddress("1.1.1.1", 0);
|
||||
static const char kTurnInternalAddress[] = "88.88.88.0";
|
||||
static const char kTurnExternalAddress[] = "88.88.88.1";
|
||||
static const int kTurnInternalPort = 3478;
|
||||
static const int kTurnExternalPort = 0;
|
||||
// The video's configured max bitrate in webrtcvideoengine.cc is 1.7 Mbps.
|
||||
// Setting the network bandwidth to 1 Mbps allows the video's bitrate to push
|
||||
// the network's limitations.
|
||||
static const int kNetworkBandwidth = 1000000;
|
||||
|
||||
} // namespace
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
|
||||
// This is an end to end test to verify that BWE is functioning when setting
|
||||
// up a one to one call at the PeerConnection level. The intention of the test
|
||||
// is to catch potential regressions for different ICE path configurations. The
|
||||
// test uses a VirtualSocketServer for it's underlying simulated network and
|
||||
// fake audio and video sources. The test is based upon rampup_tests.cc, but
|
||||
// instead is at the PeerConnection level and uses a different fake network
|
||||
// (rampup_tests.cc uses SimulatedNetwork). In the future, this test could
|
||||
// potentially test different network conditions and test video quality as well
|
||||
// (video_quality_test.cc does this, but at the call level).
|
||||
//
|
||||
// The perf test results are printed using the perf test support. If the
|
||||
// isolated_script_test_perf_output flag is specified in test_main.cc, then
|
||||
// the results are written to a JSON formatted file for the Chrome perf
|
||||
// dashboard. Since this test is a webrtc_perf_test, it will be run in the perf
|
||||
// console every webrtc commit.
|
||||
class PeerConnectionWrapperForRampUpTest : public PeerConnectionWrapper {
|
||||
public:
|
||||
using PeerConnectionWrapper::PeerConnectionWrapper;
|
||||
|
||||
PeerConnectionWrapperForRampUpTest(
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
|
||||
rtc::scoped_refptr<PeerConnectionInterface> pc,
|
||||
std::unique_ptr<MockPeerConnectionObserver> observer)
|
||||
: PeerConnectionWrapper::PeerConnectionWrapper(pc_factory,
|
||||
pc,
|
||||
std::move(observer)) {}
|
||||
|
||||
bool AddIceCandidates(std::vector<const IceCandidateInterface*> candidates) {
|
||||
bool success = true;
|
||||
for (const auto candidate : candidates) {
|
||||
if (!pc()->AddIceCandidate(candidate)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack(
|
||||
FrameGeneratorCapturerVideoTrackSource::Config config,
|
||||
Clock* clock) {
|
||||
video_track_sources_.emplace_back(
|
||||
rtc::make_ref_counted<FrameGeneratorCapturerVideoTrackSource>(
|
||||
config, clock, /*is_screencast=*/false));
|
||||
video_track_sources_.back()->Start();
|
||||
return rtc::scoped_refptr<VideoTrackInterface>(
|
||||
pc_factory()->CreateVideoTrack(video_track_sources_.back(),
|
||||
rtc::CreateRandomUuid()));
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack(
|
||||
const cricket::AudioOptions options) {
|
||||
rtc::scoped_refptr<AudioSourceInterface> source =
|
||||
pc_factory()->CreateAudioSource(options);
|
||||
return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(),
|
||||
source.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
|
||||
video_track_sources_;
|
||||
};
|
||||
|
||||
// TODO(shampson): Paramaterize the test to run for both Plan B & Unified Plan.
|
||||
class PeerConnectionRampUpTest : public ::testing::Test {
|
||||
public:
|
||||
PeerConnectionRampUpTest()
|
||||
: clock_(Clock::GetRealTimeClock()),
|
||||
virtual_socket_server_(new rtc::VirtualSocketServer()),
|
||||
firewall_socket_server_(
|
||||
new rtc::FirewallSocketServer(virtual_socket_server_.get())),
|
||||
firewall_socket_factory_(
|
||||
new rtc::BasicPacketSocketFactory(firewall_socket_server_.get())),
|
||||
network_thread_(new rtc::Thread(firewall_socket_server_.get())),
|
||||
worker_thread_(rtc::Thread::Create()) {
|
||||
network_thread_->SetName("PCNetworkThread", this);
|
||||
worker_thread_->SetName("PCWorkerThread", this);
|
||||
RTC_CHECK(network_thread_->Start());
|
||||
RTC_CHECK(worker_thread_->Start());
|
||||
|
||||
virtual_socket_server_->set_bandwidth(kNetworkBandwidth / 8);
|
||||
pc_factory_ = CreatePeerConnectionFactory(
|
||||
network_thread_.get(), worker_thread_.get(), rtc::Thread::Current(),
|
||||
rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
|
||||
CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
|
||||
std::make_unique<VideoEncoderFactoryTemplate<
|
||||
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
|
||||
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>(),
|
||||
std::make_unique<VideoDecoderFactoryTemplate<
|
||||
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
|
||||
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
|
||||
nullptr /* audio_mixer */, nullptr /* audio_processing */);
|
||||
}
|
||||
|
||||
virtual ~PeerConnectionRampUpTest() {
|
||||
SendTask(network_thread(), [this] { turn_servers_.clear(); });
|
||||
}
|
||||
|
||||
bool CreatePeerConnectionWrappers(const RTCConfiguration& caller_config,
|
||||
const RTCConfiguration& callee_config) {
|
||||
caller_ = CreatePeerConnectionWrapper(caller_config);
|
||||
callee_ = CreatePeerConnectionWrapper(callee_config);
|
||||
return caller_ && callee_;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerConnectionWrapperForRampUpTest>
|
||||
CreatePeerConnectionWrapper(const RTCConfiguration& config) {
|
||||
auto* fake_network_manager = new rtc::FakeNetworkManager();
|
||||
fake_network_manager->AddInterface(kDefaultLocalAddress);
|
||||
fake_network_managers_.emplace_back(fake_network_manager);
|
||||
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
PeerConnectionDependencies dependencies(observer.get());
|
||||
cricket::BasicPortAllocator* port_allocator =
|
||||
new cricket::BasicPortAllocator(fake_network_manager,
|
||||
firewall_socket_factory_.get());
|
||||
|
||||
port_allocator->set_step_delay(cricket::kDefaultStepDelay);
|
||||
dependencies.allocator =
|
||||
std::unique_ptr<cricket::BasicPortAllocator>(port_allocator);
|
||||
dependencies.tls_cert_verifier =
|
||||
std::make_unique<rtc::TestCertificateVerifier>();
|
||||
|
||||
auto result = pc_factory_->CreatePeerConnectionOrError(
|
||||
config, std::move(dependencies));
|
||||
if (!result.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<PeerConnectionWrapperForRampUpTest>(
|
||||
pc_factory_, result.MoveValue(), std::move(observer));
|
||||
}
|
||||
|
||||
void SetupOneWayCall() {
|
||||
ASSERT_TRUE(caller_);
|
||||
ASSERT_TRUE(callee_);
|
||||
FrameGeneratorCapturerVideoTrackSource::Config config;
|
||||
caller_->AddTrack(caller_->CreateLocalVideoTrack(config, clock_));
|
||||
// Disable highpass filter so that we can get all the test audio frames.
|
||||
cricket::AudioOptions options;
|
||||
options.highpass_filter = false;
|
||||
caller_->AddTrack(caller_->CreateLocalAudioTrack(options));
|
||||
|
||||
// Do the SDP negotiation, and also exchange ice candidates.
|
||||
ASSERT_TRUE(caller_->ExchangeOfferAnswerWith(callee_.get()));
|
||||
ASSERT_TRUE_WAIT(
|
||||
caller_->signaling_state() == PeerConnectionInterface::kStable,
|
||||
kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(caller_->IsIceGatheringDone(), kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(callee_->IsIceGatheringDone(), kDefaultTimeoutMs);
|
||||
|
||||
// Connect an ICE candidate pairs.
|
||||
ASSERT_TRUE(
|
||||
callee_->AddIceCandidates(caller_->observer()->GetAllCandidates()));
|
||||
ASSERT_TRUE(
|
||||
caller_->AddIceCandidates(callee_->observer()->GetAllCandidates()));
|
||||
// This means that ICE and DTLS are connected.
|
||||
ASSERT_TRUE_WAIT(callee_->IsIceConnected(), kDefaultTimeoutMs);
|
||||
ASSERT_TRUE_WAIT(caller_->IsIceConnected(), kDefaultTimeoutMs);
|
||||
}
|
||||
|
||||
void CreateTurnServer(cricket::ProtocolType type,
|
||||
const std::string& common_name = "test turn server") {
|
||||
rtc::Thread* thread = network_thread();
|
||||
rtc::SocketFactory* factory = firewall_socket_server_.get();
|
||||
std::unique_ptr<cricket::TestTurnServer> turn_server;
|
||||
SendTask(network_thread_.get(), [&] {
|
||||
static const rtc::SocketAddress turn_server_internal_address{
|
||||
kTurnInternalAddress, kTurnInternalPort};
|
||||
static const rtc::SocketAddress turn_server_external_address{
|
||||
kTurnExternalAddress, kTurnExternalPort};
|
||||
turn_server = std::make_unique<cricket::TestTurnServer>(
|
||||
thread, factory, turn_server_internal_address,
|
||||
turn_server_external_address, type, true /*ignore_bad_certs=*/,
|
||||
common_name);
|
||||
});
|
||||
turn_servers_.push_back(std::move(turn_server));
|
||||
}
|
||||
|
||||
// First runs the call for kRampUpTimeMs to ramp up the bandwidth estimate.
|
||||
// Then runs the test for the remaining test time, grabbing the bandwidth
|
||||
// estimation stat, every kPollIntervalTimeMs. When finished, averages the
|
||||
// bandwidth estimations and prints the bandwidth estimation result as a perf
|
||||
// metric.
|
||||
void RunTest(const std::string& test_string) {
|
||||
rtc::Thread::Current()->ProcessMessages(kRampUpTimeMs);
|
||||
int number_of_polls =
|
||||
(kDefaultTestTimeMs - kRampUpTimeMs) / kPollIntervalTimeMs;
|
||||
int total_bwe = 0;
|
||||
for (int i = 0; i < number_of_polls; ++i) {
|
||||
rtc::Thread::Current()->ProcessMessages(kPollIntervalTimeMs);
|
||||
total_bwe += static_cast<int>(GetCallerAvailableBitrateEstimate());
|
||||
}
|
||||
double average_bandwidth_estimate = total_bwe / number_of_polls;
|
||||
std::string value_description =
|
||||
"bwe_after_" + std::to_string(kDefaultTestTimeMs / 1000) + "_seconds";
|
||||
GetGlobalMetricsLogger()->LogSingleValueMetric(
|
||||
"peerconnection_ramp_up_" + test_string, value_description,
|
||||
average_bandwidth_estimate, Unit::kUnitless,
|
||||
ImprovementDirection::kNeitherIsBetter);
|
||||
}
|
||||
|
||||
rtc::Thread* network_thread() { return network_thread_.get(); }
|
||||
|
||||
rtc::FirewallSocketServer* firewall_socket_server() {
|
||||
return firewall_socket_server_.get();
|
||||
}
|
||||
|
||||
PeerConnectionWrapperForRampUpTest* caller() { return caller_.get(); }
|
||||
|
||||
PeerConnectionWrapperForRampUpTest* callee() { return callee_.get(); }
|
||||
|
||||
private:
|
||||
// Gets the caller's outgoing available bitrate from the stats. Returns 0 if
|
||||
// something went wrong. It takes the outgoing bitrate from the current
|
||||
// selected ICE candidate pair's stats.
|
||||
double GetCallerAvailableBitrateEstimate() {
|
||||
auto stats = caller_->GetStats();
|
||||
auto transport_stats = stats->GetStatsOfType<RTCTransportStats>();
|
||||
if (transport_stats.size() == 0u ||
|
||||
!transport_stats[0]->selected_candidate_pair_id.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
std::string selected_ice_id =
|
||||
transport_stats[0]
|
||||
->GetAttribute(transport_stats[0]->selected_candidate_pair_id)
|
||||
.ToString();
|
||||
// Use the selected ICE candidate pair ID to get the appropriate ICE stats.
|
||||
const RTCIceCandidatePairStats ice_candidate_pair_stats =
|
||||
stats->Get(selected_ice_id)->cast_to<const RTCIceCandidatePairStats>();
|
||||
if (ice_candidate_pair_stats.available_outgoing_bitrate.has_value()) {
|
||||
return *ice_candidate_pair_stats.available_outgoing_bitrate;
|
||||
}
|
||||
// We couldn't get the `available_outgoing_bitrate` for the active candidate
|
||||
// pair.
|
||||
return 0;
|
||||
}
|
||||
|
||||
Clock* const clock_;
|
||||
// The turn servers should be accessed & deleted on the network thread to
|
||||
// avoid a race with the socket read/write which occurs on the network thread.
|
||||
std::vector<std::unique_ptr<cricket::TestTurnServer>> turn_servers_;
|
||||
// `virtual_socket_server_` is used by `network_thread_` so it must be
|
||||
// destroyed later.
|
||||
// TODO(bugs.webrtc.org/7668): We would like to update the virtual network we
|
||||
// use for this test. VirtualSocketServer isn't ideal because:
|
||||
// 1) It uses the same queue & network capacity for both directions.
|
||||
// 2) VirtualSocketServer implements how the network bandwidth affects the
|
||||
// send delay differently than the SimulatedNetwork, used by the
|
||||
// FakeNetworkPipe. It would be ideal if all of levels of virtual
|
||||
// networks used in testing were consistent.
|
||||
// We would also like to update this test to record the time to ramp up,
|
||||
// down, and back up (similar to in rampup_tests.cc). This is problematic with
|
||||
// the VirtualSocketServer. The first ramp down time is very noisy and the
|
||||
// second ramp up time can take up to 300 seconds, most likely due to a built
|
||||
// up queue.
|
||||
std::unique_ptr<rtc::VirtualSocketServer> virtual_socket_server_;
|
||||
std::unique_ptr<rtc::FirewallSocketServer> firewall_socket_server_;
|
||||
std::unique_ptr<rtc::BasicPacketSocketFactory> firewall_socket_factory_;
|
||||
|
||||
std::unique_ptr<rtc::Thread> network_thread_;
|
||||
std::unique_ptr<rtc::Thread> worker_thread_;
|
||||
// The `pc_factory` uses `network_thread_` & `worker_thread_`, so it must be
|
||||
// destroyed first.
|
||||
std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_network_managers_;
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
|
||||
std::unique_ptr<PeerConnectionWrapperForRampUpTest> caller_;
|
||||
std::unique_ptr<PeerConnectionWrapperForRampUpTest> callee_;
|
||||
};
|
||||
|
||||
TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverTCP) {
|
||||
CreateTurnServer(cricket::ProtocolType::PROTO_TCP);
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
|
||||
":" + std::to_string(kTurnInternalPort) +
|
||||
"?transport=tcp";
|
||||
ice_server.urls.push_back(ice_server_url);
|
||||
ice_server.username = "test";
|
||||
ice_server.password = "test";
|
||||
PeerConnectionInterface::RTCConfiguration client_1_config;
|
||||
client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_1_config.servers.push_back(ice_server);
|
||||
client_1_config.type = PeerConnectionInterface::kRelay;
|
||||
PeerConnectionInterface::RTCConfiguration client_2_config;
|
||||
client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_2_config.servers.push_back(ice_server);
|
||||
client_2_config.type = PeerConnectionInterface::kRelay;
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
|
||||
|
||||
SetupOneWayCall();
|
||||
RunTest("turn_over_tcp");
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverUDP) {
|
||||
CreateTurnServer(cricket::ProtocolType::PROTO_UDP);
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
|
||||
":" + std::to_string(kTurnInternalPort);
|
||||
|
||||
ice_server.urls.push_back(ice_server_url);
|
||||
ice_server.username = "test";
|
||||
ice_server.password = "test";
|
||||
PeerConnectionInterface::RTCConfiguration client_1_config;
|
||||
client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_1_config.servers.push_back(ice_server);
|
||||
client_1_config.type = PeerConnectionInterface::kRelay;
|
||||
PeerConnectionInterface::RTCConfiguration client_2_config;
|
||||
client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_2_config.servers.push_back(ice_server);
|
||||
client_2_config.type = PeerConnectionInterface::kRelay;
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
|
||||
|
||||
SetupOneWayCall();
|
||||
RunTest("turn_over_udp");
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverTLS) {
|
||||
CreateTurnServer(cricket::ProtocolType::PROTO_TLS, kTurnInternalAddress);
|
||||
PeerConnectionInterface::IceServer ice_server;
|
||||
std::string ice_server_url = "turns:" + std::string(kTurnInternalAddress) +
|
||||
":" + std::to_string(kTurnInternalPort) +
|
||||
"?transport=tcp";
|
||||
ice_server.urls.push_back(ice_server_url);
|
||||
ice_server.username = "test";
|
||||
ice_server.password = "test";
|
||||
PeerConnectionInterface::RTCConfiguration client_1_config;
|
||||
client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_1_config.servers.push_back(ice_server);
|
||||
client_1_config.type = PeerConnectionInterface::kRelay;
|
||||
PeerConnectionInterface::RTCConfiguration client_2_config;
|
||||
client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_2_config.servers.push_back(ice_server);
|
||||
client_2_config.type = PeerConnectionInterface::kRelay;
|
||||
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
|
||||
|
||||
SetupOneWayCall();
|
||||
RunTest("turn_over_tls");
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionRampUpTest, Bwe_After_UDPPeerToPeer) {
|
||||
PeerConnectionInterface::RTCConfiguration client_1_config;
|
||||
client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_1_config.tcp_candidate_policy =
|
||||
PeerConnection::kTcpCandidatePolicyDisabled;
|
||||
PeerConnectionInterface::RTCConfiguration client_2_config;
|
||||
client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
client_2_config.tcp_candidate_policy =
|
||||
PeerConnection::kTcpCandidatePolicyDisabled;
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
|
||||
|
||||
SetupOneWayCall();
|
||||
RunTest("udp_peer_to_peer");
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionRampUpTest, Bwe_After_TCPPeerToPeer) {
|
||||
firewall_socket_server()->set_udp_sockets_enabled(false);
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers(config, config));
|
||||
|
||||
SetupOneWayCall();
|
||||
RunTest("tcp_peer_to_peer");
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
1990
TMessagesProj/jni/voip/webrtc/pc/peer_connection_rtp_unittest.cc
Normal file
1990
TMessagesProj/jni/voip/webrtc/pc/peer_connection_rtp_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright 2019 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 <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/audio_codecs/opus_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/opus_audio_encoder_factory.h"
|
||||
#include "api/create_peerconnection_factory.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "api/rtp_transceiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/uma_metrics.h"
|
||||
#include "api/video/video_codec_constants.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
|
||||
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "media/base/rid_description.h"
|
||||
#include "media/base/stream_params.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "modules/audio_processing/include/audio_processing.h"
|
||||
#include "pc/channel_interface.h"
|
||||
#include "pc/peer_connection_wrapper.h"
|
||||
#include "pc/sdp_utils.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/simulcast_description.h"
|
||||
#include "pc/test/fake_audio_capture_module.h"
|
||||
#include "pc/test/mock_peer_connection_observers.h"
|
||||
#include "pc/test/simulcast_layer_util.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/unique_id_generator.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using ::testing::Contains;
|
||||
using ::testing::Each;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::Le;
|
||||
using ::testing::Ne;
|
||||
using ::testing::Pair;
|
||||
using ::testing::Property;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
using cricket::MediaContentDescription;
|
||||
using cricket::RidDescription;
|
||||
using cricket::SimulcastDescription;
|
||||
using cricket::SimulcastLayer;
|
||||
using cricket::StreamParams;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& os, // no-presubmit-check TODO(webrtc:8982)
|
||||
const SimulcastLayer& layer) {
|
||||
if (layer.is_paused) {
|
||||
os << "~";
|
||||
}
|
||||
return os << layer.rid;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnectionSimulcastTests : public ::testing::Test {
|
||||
public:
|
||||
PeerConnectionSimulcastTests()
|
||||
: pc_factory_(CreatePeerConnectionFactory(
|
||||
rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
FakeAudioCaptureModule::Create(),
|
||||
CreateBuiltinAudioEncoderFactory(),
|
||||
CreateBuiltinAudioDecoderFactory(),
|
||||
std::make_unique<
|
||||
VideoEncoderFactoryTemplate<LibvpxVp8EncoderTemplateAdapter,
|
||||
LibvpxVp9EncoderTemplateAdapter,
|
||||
OpenH264EncoderTemplateAdapter,
|
||||
LibaomAv1EncoderTemplateAdapter>>(),
|
||||
std::make_unique<
|
||||
VideoDecoderFactoryTemplate<LibvpxVp8DecoderTemplateAdapter,
|
||||
LibvpxVp9DecoderTemplateAdapter,
|
||||
OpenH264DecoderTemplateAdapter,
|
||||
Dav1dDecoderTemplateAdapter>>(),
|
||||
nullptr,
|
||||
nullptr)) {}
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
|
||||
MockPeerConnectionObserver* observer) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
PeerConnectionDependencies pcd(observer);
|
||||
auto result =
|
||||
pc_factory_->CreatePeerConnectionOrError(config, std::move(pcd));
|
||||
EXPECT_TRUE(result.ok());
|
||||
observer->SetPeerConnectionInterface(result.value().get());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnectionWrapper() {
|
||||
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
||||
auto pc = CreatePeerConnection(observer.get());
|
||||
return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
|
||||
std::move(observer));
|
||||
}
|
||||
|
||||
void ExchangeOfferAnswer(PeerConnectionWrapper* local,
|
||||
PeerConnectionWrapper* remote,
|
||||
const std::vector<SimulcastLayer>& answer_layers) {
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
// Remove simulcast as the second peer connection won't support it.
|
||||
RemoveSimulcast(offer.get());
|
||||
std::string err;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &err)) << err;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
// Setup the answer to look like a server response.
|
||||
auto mcd_answer = answer->description()->contents()[0].media_description();
|
||||
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
||||
for (const SimulcastLayer& layer : answer_layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &err)) << err;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverInterface> AddTransceiver(
|
||||
PeerConnectionWrapper* pc,
|
||||
const std::vector<SimulcastLayer>& layers,
|
||||
cricket::MediaType media_type = cricket::MEDIA_TYPE_VIDEO) {
|
||||
auto init = CreateTransceiverInit(layers);
|
||||
return pc->AddTransceiver(media_type, init);
|
||||
}
|
||||
|
||||
void AddRequestToReceiveSimulcast(const std::vector<SimulcastLayer>& layers,
|
||||
SessionDescriptionInterface* sd) {
|
||||
auto mcd = sd->description()->contents()[0].media_description();
|
||||
SimulcastDescription simulcast;
|
||||
auto& receive_layers = simulcast.receive_layers();
|
||||
for (const SimulcastLayer& layer : layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
mcd->set_simulcast_description(simulcast);
|
||||
}
|
||||
|
||||
void ValidateTransceiverParameters(
|
||||
rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
|
||||
const std::vector<SimulcastLayer>& layers) {
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
std::vector<SimulcastLayer> result_layers;
|
||||
absl::c_transform(parameters.encodings, std::back_inserter(result_layers),
|
||||
[](const RtpEncodingParameters& encoding) {
|
||||
return SimulcastLayer(encoding.rid, !encoding.active);
|
||||
});
|
||||
EXPECT_THAT(result_layers, ElementsAreArray(layers));
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
|
||||
};
|
||||
|
||||
// Validates that RIDs are supported arguments when adding a transceiver.
|
||||
TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithRid) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f"}, true);
|
||||
auto transceiver = AddTransceiver(pc.get(), layers);
|
||||
ASSERT_TRUE(transceiver);
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
// Single RID should be removed.
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithSimulcast) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f", "h", "q"}, true);
|
||||
auto transceiver = AddTransceiver(pc.get(), layers);
|
||||
ASSERT_TRUE(transceiver);
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSimulcastTests, RidsAreAutogeneratedIfNotProvided) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto init = CreateTransceiverInit(CreateLayers({"f", "h", "q"}, true));
|
||||
for (RtpEncodingParameters& parameters : init.send_encodings) {
|
||||
parameters.rid = "";
|
||||
}
|
||||
auto transceiver = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(3u, parameters.encodings.size());
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
Each(Field("rid", &RtpEncodingParameters::rid, Ne(""))));
|
||||
}
|
||||
|
||||
// Validates that an error is returned when there is a mix of supplied and not
|
||||
// supplied RIDs in a call to AddTransceiver.
|
||||
TEST_F(PeerConnectionSimulcastTests, MustSupplyAllOrNoRidsInSimulcast) {
|
||||
auto pc_wrapper = CreatePeerConnectionWrapper();
|
||||
auto pc = pc_wrapper->pc();
|
||||
// Cannot create a layer with empty RID. Remove the RID after init is created.
|
||||
auto layers = CreateLayers({"f", "h", "remove"}, true);
|
||||
auto init = CreateTransceiverInit(layers);
|
||||
init.send_encodings[2].rid = "";
|
||||
auto error = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
||||
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
|
||||
}
|
||||
|
||||
// Validates that an error is returned when illegal RIDs are supplied.
|
||||
TEST_F(PeerConnectionSimulcastTests, ChecksForIllegalRidValues) {
|
||||
auto pc_wrapper = CreatePeerConnectionWrapper();
|
||||
auto pc = pc_wrapper->pc();
|
||||
auto layers = CreateLayers({"f", "h", "~q"}, true);
|
||||
auto init = CreateTransceiverInit(layers);
|
||||
auto error = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
||||
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
|
||||
}
|
||||
|
||||
// Validates that a single RID is removed from the encoding layer.
|
||||
TEST_F(PeerConnectionSimulcastTests, SingleRidIsRemovedFromSessionDescription) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto transceiver = AddTransceiver(pc.get(), CreateLayers({"1"}, true));
|
||||
auto offer = pc->CreateOfferAndSetAsLocal();
|
||||
ASSERT_TRUE(offer);
|
||||
auto contents = offer->description()->contents();
|
||||
ASSERT_EQ(1u, contents.size());
|
||||
EXPECT_THAT(contents[0].media_description()->streams(),
|
||||
ElementsAre(Property(&StreamParams::has_rids, false)));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastLayersRemovedFromTail) {
|
||||
static_assert(
|
||||
kMaxSimulcastStreams < 8,
|
||||
"Test assumes that the platform does not allow 8 simulcast layers");
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3", "4", "5", "6", "7", "8"}, true);
|
||||
std::vector<SimulcastLayer> expected_layers;
|
||||
std::copy_n(layers.begin(), kMaxSimulcastStreams,
|
||||
std::back_inserter(expected_layers));
|
||||
auto transceiver = AddTransceiver(pc.get(), layers);
|
||||
ValidateTransceiverParameters(transceiver, expected_layers);
|
||||
}
|
||||
|
||||
// Checks that an offfer to send simulcast contains a SimulcastDescription.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastAppearsInSessionDescription) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
std::vector<std::string> rids({"f", "h", "q"});
|
||||
auto layers = CreateLayers(rids, true);
|
||||
auto transceiver = AddTransceiver(pc.get(), layers);
|
||||
auto offer = pc->CreateOffer();
|
||||
ASSERT_TRUE(offer);
|
||||
auto contents = offer->description()->contents();
|
||||
ASSERT_EQ(1u, contents.size());
|
||||
auto content = contents[0];
|
||||
auto mcd = content.media_description();
|
||||
ASSERT_TRUE(mcd->HasSimulcast());
|
||||
auto simulcast = mcd->simulcast_description();
|
||||
EXPECT_THAT(simulcast.receive_layers(), IsEmpty());
|
||||
// The size is validated separately because GetAllLayers() flattens the list.
|
||||
EXPECT_THAT(simulcast.send_layers(), SizeIs(3));
|
||||
std::vector<SimulcastLayer> result = simulcast.send_layers().GetAllLayers();
|
||||
EXPECT_THAT(result, ElementsAreArray(layers));
|
||||
auto streams = mcd->streams();
|
||||
ASSERT_EQ(1u, streams.size());
|
||||
auto stream = streams[0];
|
||||
EXPECT_FALSE(stream.has_ssrcs());
|
||||
EXPECT_TRUE(stream.has_rids());
|
||||
std::vector<std::string> result_rids;
|
||||
absl::c_transform(stream.rids(), std::back_inserter(result_rids),
|
||||
[](const RidDescription& rid) { return rid.rid; });
|
||||
EXPECT_THAT(result_rids, ElementsAreArray(rids));
|
||||
}
|
||||
|
||||
// Checks that Simulcast layers propagate to the sender parameters.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastLayersAreSetInSender) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f", "h", "q"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
{
|
||||
SCOPED_TRACE("after create offer");
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
// Remove simulcast as the second peer connection won't support it.
|
||||
auto simulcast = RemoveSimulcast(offer.get());
|
||||
std::string error;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
|
||||
// Setup an answer that mimics a server accepting simulcast.
|
||||
auto mcd_answer = answer->description()->contents()[0].media_description();
|
||||
mcd_answer->mutable_streams().clear();
|
||||
auto simulcast_layers = simulcast.send_layers().GetAllLayers();
|
||||
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
||||
for (const auto& layer : simulcast_layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
||||
{
|
||||
SCOPED_TRACE("after set remote");
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that paused Simulcast layers propagate to the sender parameters.
|
||||
TEST_F(PeerConnectionSimulcastTests, PausedSimulcastLayersAreDisabledInSender) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f", "h", "q"}, {true, false, true});
|
||||
auto server_layers = CreateLayers({"f", "h", "q"}, {true, false, false});
|
||||
RTC_DCHECK_EQ(layers.size(), server_layers.size());
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
{
|
||||
SCOPED_TRACE("after create offer");
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
|
||||
// Remove simulcast as the second peer connection won't support it.
|
||||
RemoveSimulcast(offer.get());
|
||||
std::string error;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
|
||||
// Setup an answer that mimics a server accepting simulcast.
|
||||
auto mcd_answer = answer->description()->contents()[0].media_description();
|
||||
mcd_answer->mutable_streams().clear();
|
||||
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
||||
for (const SimulcastLayer& layer : server_layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
||||
{
|
||||
SCOPED_TRACE("after set remote");
|
||||
ValidateTransceiverParameters(transceiver, server_layers);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that when Simulcast is not supported by the remote party, then all
|
||||
// the layers (except the first) are removed.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedRemovesExtraLayers) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
ExchangeOfferAnswer(local.get(), remote.get(), {});
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
// Should only have the first layer.
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq("1"))));
|
||||
}
|
||||
|
||||
// Checks that if Simulcast is supported by remote party, but some layers are
|
||||
// rejected, then only rejected layers are removed from the sender.
|
||||
TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3"}, true);
|
||||
auto expected_layers = CreateLayers({"2", "3"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
{
|
||||
SCOPED_TRACE("after create offer");
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
// Remove simulcast as the second peer connection won't support it.
|
||||
auto removed_simulcast = RemoveSimulcast(offer.get());
|
||||
std::string error;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
auto mcd_answer = answer->description()->contents()[0].media_description();
|
||||
// Setup the answer to look like a server response.
|
||||
// Remove one of the layers to reject it in the answer.
|
||||
auto simulcast_layers = removed_simulcast.send_layers().GetAllLayers();
|
||||
simulcast_layers.erase(simulcast_layers.begin());
|
||||
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
||||
for (const auto& layer : simulcast_layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
ASSERT_TRUE(mcd_answer->HasSimulcast());
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
||||
{
|
||||
SCOPED_TRACE("after set remote");
|
||||
ValidateTransceiverParameters(transceiver, expected_layers);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that simulcast is set up correctly when the server sends an offer
|
||||
// requesting to receive simulcast.
|
||||
TEST_F(PeerConnectionSimulcastTests, ServerSendsOfferToReceiveSimulcast) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f", "h", "q"}, true);
|
||||
AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
// Remove simulcast as a sender and set it up as a receiver.
|
||||
RemoveSimulcast(offer.get());
|
||||
AddRequestToReceiveSimulcast(layers, offer.get());
|
||||
std::string error;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto transceiver = remote->pc()->GetTransceivers()[0];
|
||||
transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
|
||||
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
|
||||
// Checks that SetRemoteDescription doesn't attempt to associate a transceiver
|
||||
// when simulcast is requested by the server.
|
||||
TEST_F(PeerConnectionSimulcastTests, TransceiverIsNotRecycledWithSimulcast) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"f", "h", "q"}, true);
|
||||
AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
// Remove simulcast as a sender and set it up as a receiver.
|
||||
RemoveSimulcast(offer.get());
|
||||
AddRequestToReceiveSimulcast(layers, offer.get());
|
||||
// Call AddTrack so that a transceiver is created.
|
||||
remote->AddVideoTrack("fake_track");
|
||||
std::string error;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto transceivers = remote->pc()->GetTransceivers();
|
||||
ASSERT_EQ(2u, transceivers.size());
|
||||
auto transceiver = transceivers[1];
|
||||
transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
|
||||
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
|
||||
ValidateTransceiverParameters(transceiver, layers);
|
||||
}
|
||||
|
||||
// Checks that if the number of layers changes during negotiation, then any
|
||||
// outstanding get/set parameters transaction is invalidated.
|
||||
TEST_F(PeerConnectionSimulcastTests, ParametersAreInvalidatedWhenLayersChange) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(3u, parameters.encodings.size());
|
||||
// Response will reject simulcast altogether.
|
||||
ExchangeOfferAnswer(local.get(), remote.get(), {});
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_EQ(RTCErrorType::INVALID_STATE, result.type());
|
||||
}
|
||||
|
||||
// Checks that even though negotiation modifies the sender's parameters, an
|
||||
// outstanding get/set parameters transaction is not invalidated.
|
||||
// This test negotiates twice because initial parameters before negotiation
|
||||
// is missing critical information and cannot be set on the sender.
|
||||
TEST_F(PeerConnectionSimulcastTests,
|
||||
NegotiationDoesNotInvalidateParameterTransactions) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3"}, true);
|
||||
auto expected_layers = CreateLayers({"1", "2", "3"}, false);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
|
||||
|
||||
// Verify that negotiation does not invalidate the parameters.
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
|
||||
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_TRUE(result.ok());
|
||||
ValidateTransceiverParameters(transceiver, expected_layers);
|
||||
}
|
||||
|
||||
// Tests that a simulcast answer is rejected if the RID extension is not
|
||||
// negotiated.
|
||||
TEST_F(PeerConnectionSimulcastTests, NegotiationDoesNotHaveRidExtensionFails) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3"}, true);
|
||||
auto expected_layers = CreateLayers({"1"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
// Remove simulcast as the second peer connection won't support it.
|
||||
RemoveSimulcast(offer.get());
|
||||
std::string err;
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &err)) << err;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
// Setup the answer to look like a server response.
|
||||
// Drop the RID header extension.
|
||||
auto mcd_answer = answer->description()->contents()[0].media_description();
|
||||
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
||||
for (const SimulcastLayer& layer : layers) {
|
||||
receive_layers.AddLayer(layer);
|
||||
}
|
||||
cricket::RtpHeaderExtensions extensions;
|
||||
for (auto extension : mcd_answer->rtp_header_extensions()) {
|
||||
if (extension.uri != RtpExtension::kRidUri) {
|
||||
extensions.push_back(extension);
|
||||
}
|
||||
}
|
||||
mcd_answer->set_rtp_header_extensions(extensions);
|
||||
EXPECT_EQ(layers.size(), mcd_answer->simulcast_description()
|
||||
.receive_layers()
|
||||
.GetAllLayers()
|
||||
.size());
|
||||
EXPECT_FALSE(local->SetRemoteDescription(std::move(answer), &err)) << err;
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastAudioRejected) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
||||
auto transceiver =
|
||||
AddTransceiver(local.get(), layers, cricket::MEDIA_TYPE_AUDIO);
|
||||
// Should only have the first layer.
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
EXPECT_EQ(1u, parameters.encodings.size());
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
|
||||
ExchangeOfferAnswer(local.get(), remote.get(), {});
|
||||
// Still have a single layer after negotiation
|
||||
parameters = transceiver->sender()->GetParameters();
|
||||
EXPECT_EQ(1u, parameters.encodings.size());
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
|
||||
}
|
||||
|
||||
// Check that modifying the offer to remove simulcast and at the same
|
||||
// time leaving in a RID line does not cause an exception.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastSldModificationRejected) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3"}, true);
|
||||
AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOffer();
|
||||
std::string as_string;
|
||||
EXPECT_TRUE(offer->ToString(&as_string));
|
||||
auto simulcast_marker = "a=rid:3 send\r\na=simulcast:send 1;2;3\r\n";
|
||||
auto pos = as_string.find(simulcast_marker);
|
||||
EXPECT_NE(pos, std::string::npos);
|
||||
as_string.erase(pos, strlen(simulcast_marker));
|
||||
SdpParseError parse_error;
|
||||
auto modified_offer =
|
||||
CreateSessionDescription(SdpType::kOffer, as_string, &parse_error);
|
||||
EXPECT_TRUE(modified_offer);
|
||||
EXPECT_TRUE(local->SetLocalDescription(std::move(modified_offer)));
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
// Integration tests for PeerConnection.
|
||||
// These tests exercise a full stack for the SVC extension.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "pc/test/integration_test_helpers.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
class PeerConnectionSVCIntegrationTest
|
||||
: public PeerConnectionIntegrationBaseTest {
|
||||
protected:
|
||||
PeerConnectionSVCIntegrationTest()
|
||||
: PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {}
|
||||
|
||||
RTCError SetCodecPreferences(
|
||||
rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
|
||||
absl::string_view codec_name) {
|
||||
RtpCapabilities capabilities =
|
||||
caller()->pc_factory()->GetRtpSenderCapabilities(
|
||||
cricket::MEDIA_TYPE_VIDEO);
|
||||
std::vector<RtpCodecCapability> codecs;
|
||||
for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
|
||||
if (codec_capability.name == codec_name)
|
||||
codecs.push_back(codec_capability);
|
||||
}
|
||||
return transceiver->SetCodecPreferences(codecs);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL1T1) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
encoding_parameters.scalability_mode = "L1T1";
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
EXPECT_TRUE(transceiver_or_error.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL3T3) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
encoding_parameters.scalability_mode = "L3T3";
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
EXPECT_TRUE(transceiver_or_error.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest,
|
||||
AddTransceiverRejectsUnknownScalabilityMode) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
encoding_parameters.scalability_mode = "FOOBAR";
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
EXPECT_FALSE(transceiver_or_error.ok());
|
||||
EXPECT_EQ(transceiver_or_error.error().type(),
|
||||
RTCErrorType::UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpCapabilities capabilities =
|
||||
caller()->pc_factory()->GetRtpSenderCapabilities(
|
||||
cricket::MEDIA_TYPE_VIDEO);
|
||||
std::vector<RtpCodecCapability> vp8_codec;
|
||||
for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
|
||||
if (codec_capability.name == cricket::kVp8CodecName)
|
||||
vp8_codec.push_back(codec_capability);
|
||||
}
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(transceiver->SetCodecPreferences(vp8_codec).ok());
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L1T3";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_TRUE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest, SetParametersRejectsL3T3WithVP8) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L3T3";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest,
|
||||
SetParametersAcceptsL1T3WithVP8AfterNegotiation) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
|
||||
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L1T3";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_TRUE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest,
|
||||
SetParametersAcceptsL3T3WithVP9AfterNegotiation) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp9CodecName).ok());
|
||||
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L3T3";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_TRUE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest,
|
||||
SetParametersRejectsL3T3WithVP8AfterNegotiation) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
|
||||
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L3T3";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest,
|
||||
SetParametersRejectsInvalidModeWithVP9AfterNegotiation) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto transceiver = transceiver_or_error.MoveValue();
|
||||
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp9CodecName).ok());
|
||||
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
RtpParameters parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "FOOBAR";
|
||||
auto result = transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
|
||||
RtpTransceiverInit init;
|
||||
RtpEncodingParameters encoding_parameters;
|
||||
init.send_encodings.push_back(encoding_parameters);
|
||||
auto transceiver_or_error =
|
||||
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
|
||||
ASSERT_TRUE(transceiver_or_error.ok());
|
||||
auto caller_transceiver = transceiver_or_error.MoveValue();
|
||||
|
||||
RtpCapabilities capabilities =
|
||||
caller()->pc_factory()->GetRtpSenderCapabilities(
|
||||
cricket::MEDIA_TYPE_VIDEO);
|
||||
std::vector<RtpCodecCapability> send_codecs = capabilities.codecs;
|
||||
// Only keep VP9 in the caller
|
||||
send_codecs.erase(std::partition(send_codecs.begin(), send_codecs.end(),
|
||||
[](const auto& codec) -> bool {
|
||||
return codec.name ==
|
||||
cricket::kVp9CodecName;
|
||||
}),
|
||||
send_codecs.end());
|
||||
ASSERT_FALSE(send_codecs.empty());
|
||||
caller_transceiver->SetCodecPreferences(send_codecs);
|
||||
|
||||
// L3T3 should be supported by VP9
|
||||
RtpParameters parameters = caller_transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(parameters.encodings.size(), 1u);
|
||||
parameters.encodings[0].scalability_mode = "L3T3";
|
||||
auto result = caller_transceiver->sender()->SetParameters(parameters);
|
||||
EXPECT_TRUE(result.ok());
|
||||
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
parameters = caller_transceiver->sender()->GetParameters();
|
||||
ASSERT_TRUE(parameters.encodings[0].scalability_mode.has_value());
|
||||
EXPECT_TRUE(
|
||||
absl::StartsWith(*parameters.encodings[0].scalability_mode, "L3T3"));
|
||||
|
||||
// Keep only VP8 in the caller
|
||||
send_codecs = capabilities.codecs;
|
||||
send_codecs.erase(std::partition(send_codecs.begin(), send_codecs.end(),
|
||||
[](const auto& codec) -> bool {
|
||||
return codec.name ==
|
||||
cricket::kVp8CodecName;
|
||||
}),
|
||||
send_codecs.end());
|
||||
ASSERT_FALSE(send_codecs.empty());
|
||||
caller_transceiver->SetCodecPreferences(send_codecs);
|
||||
|
||||
// Renegotiate to force the new codec list to be used
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
|
||||
// Fallback should happen and L3T3 is not used anymore
|
||||
parameters = caller_transceiver->sender()->GetParameters();
|
||||
ASSERT_TRUE(parameters.encodings[0].scalability_mode.has_value());
|
||||
EXPECT_TRUE(
|
||||
absl::StartsWith(*parameters.encodings[0].scalability_mode, "L1T"));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
} // namespace webrtc
|
||||
349
TMessagesProj/jni/voip/webrtc/pc/peer_connection_wrapper.cc
Normal file
349
TMessagesProj/jni/voip/webrtc/pc/peer_connection_wrapper.cc
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* 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 "pc/peer_connection_wrapper.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/function_view.h"
|
||||
#include "api/set_remote_description_observer_interface.h"
|
||||
#include "pc/sdp_utils.h"
|
||||
#include "pc/test/fake_video_track_source.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
||||
|
||||
namespace {
|
||||
const uint32_t kDefaultTimeout = 10000U;
|
||||
}
|
||||
|
||||
PeerConnectionWrapper::PeerConnectionWrapper(
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
|
||||
rtc::scoped_refptr<PeerConnectionInterface> pc,
|
||||
std::unique_ptr<MockPeerConnectionObserver> observer)
|
||||
: pc_factory_(std::move(pc_factory)),
|
||||
observer_(std::move(observer)),
|
||||
pc_(std::move(pc)) {
|
||||
RTC_DCHECK(pc_factory_);
|
||||
RTC_DCHECK(pc_);
|
||||
RTC_DCHECK(observer_);
|
||||
observer_->SetPeerConnectionInterface(pc_.get());
|
||||
}
|
||||
|
||||
PeerConnectionWrapper::~PeerConnectionWrapper() {
|
||||
if (pc_)
|
||||
pc_->Close();
|
||||
}
|
||||
|
||||
PeerConnectionFactoryInterface* PeerConnectionWrapper::pc_factory() {
|
||||
return pc_factory_.get();
|
||||
}
|
||||
|
||||
PeerConnectionInterface* PeerConnectionWrapper::pc() {
|
||||
return pc_.get();
|
||||
}
|
||||
|
||||
MockPeerConnectionObserver* PeerConnectionWrapper::observer() {
|
||||
return observer_.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateOffer() {
|
||||
return CreateOffer(RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
|
||||
std::string* error_out) {
|
||||
return CreateSdp(
|
||||
[this, options](CreateSessionDescriptionObserver* observer) {
|
||||
pc()->CreateOffer(observer, options);
|
||||
},
|
||||
error_out);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateOfferAndSetAsLocal() {
|
||||
return CreateOfferAndSetAsLocal(RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateOfferAndSetAsLocal(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
|
||||
auto offer = CreateOffer(options);
|
||||
if (!offer) {
|
||||
return nullptr;
|
||||
}
|
||||
EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(offer.get())));
|
||||
return offer;
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateAnswer() {
|
||||
return CreateAnswer(RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateAnswer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
|
||||
std::string* error_out) {
|
||||
return CreateSdp(
|
||||
[this, options](CreateSessionDescriptionObserver* observer) {
|
||||
pc()->CreateAnswer(observer, options);
|
||||
},
|
||||
error_out);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateAnswerAndSetAsLocal() {
|
||||
return CreateAnswerAndSetAsLocal(RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateAnswerAndSetAsLocal(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
|
||||
auto answer = CreateAnswer(options);
|
||||
if (!answer) {
|
||||
return nullptr;
|
||||
}
|
||||
EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(answer.get())));
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface>
|
||||
PeerConnectionWrapper::CreateRollback() {
|
||||
return CreateSessionDescription(SdpType::kRollback, "");
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
|
||||
rtc::FunctionView<void(CreateSessionDescriptionObserver*)> fn,
|
||||
std::string* error_out) {
|
||||
auto observer = rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
|
||||
fn(observer.get());
|
||||
EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout);
|
||||
if (error_out && !observer->result()) {
|
||||
*error_out = observer->error();
|
||||
}
|
||||
return observer->MoveDescription();
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::SetLocalDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface> desc,
|
||||
std::string* error_out) {
|
||||
return SetSdp(
|
||||
[this, &desc](SetSessionDescriptionObserver* observer) {
|
||||
pc()->SetLocalDescription(observer, desc.release());
|
||||
},
|
||||
error_out);
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::SetRemoteDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface> desc,
|
||||
std::string* error_out) {
|
||||
return SetSdp(
|
||||
[this, &desc](SetSessionDescriptionObserver* observer) {
|
||||
pc()->SetRemoteDescription(observer, desc.release());
|
||||
},
|
||||
error_out);
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::SetRemoteDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface> desc,
|
||||
RTCError* error_out) {
|
||||
auto observer = rtc::make_ref_counted<FakeSetRemoteDescriptionObserver>();
|
||||
pc()->SetRemoteDescription(std::move(desc), observer);
|
||||
EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout);
|
||||
bool ok = observer->error().ok();
|
||||
if (error_out)
|
||||
*error_out = std::move(observer->error());
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::SetSdp(
|
||||
rtc::FunctionView<void(SetSessionDescriptionObserver*)> fn,
|
||||
std::string* error_out) {
|
||||
auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
|
||||
fn(observer.get());
|
||||
EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout);
|
||||
if (error_out && !observer->result()) {
|
||||
*error_out = observer->error();
|
||||
}
|
||||
return observer->result();
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
|
||||
PeerConnectionWrapper* answerer) {
|
||||
return ExchangeOfferAnswerWith(answerer, RTCOfferAnswerOptions(),
|
||||
RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
|
||||
PeerConnectionWrapper* answerer,
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_options,
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& answer_options) {
|
||||
RTC_DCHECK(answerer);
|
||||
if (answerer == this) {
|
||||
RTC_LOG(LS_ERROR) << "Cannot exchange offer/answer with ourself!";
|
||||
return false;
|
||||
}
|
||||
auto offer = CreateOffer(offer_options);
|
||||
EXPECT_TRUE(offer);
|
||||
if (!offer) {
|
||||
return false;
|
||||
}
|
||||
bool set_local_offer =
|
||||
SetLocalDescription(CloneSessionDescription(offer.get()));
|
||||
EXPECT_TRUE(set_local_offer);
|
||||
if (!set_local_offer) {
|
||||
return false;
|
||||
}
|
||||
bool set_remote_offer = answerer->SetRemoteDescription(std::move(offer));
|
||||
EXPECT_TRUE(set_remote_offer);
|
||||
if (!set_remote_offer) {
|
||||
return false;
|
||||
}
|
||||
auto answer = answerer->CreateAnswer(answer_options);
|
||||
EXPECT_TRUE(answer);
|
||||
if (!answer) {
|
||||
return false;
|
||||
}
|
||||
bool set_local_answer =
|
||||
answerer->SetLocalDescription(CloneSessionDescription(answer.get()));
|
||||
EXPECT_TRUE(set_local_answer);
|
||||
if (!set_local_answer) {
|
||||
return false;
|
||||
}
|
||||
bool set_remote_answer = SetRemoteDescription(std::move(answer));
|
||||
EXPECT_TRUE(set_remote_answer);
|
||||
return set_remote_answer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverInterface>
|
||||
PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
||||
pc()->AddTransceiver(media_type);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverInterface>
|
||||
PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type,
|
||||
const RtpTransceiverInit& init) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
||||
pc()->AddTransceiver(media_type, init);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverInterface>
|
||||
PeerConnectionWrapper::AddTransceiver(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
||||
pc()->AddTransceiver(track);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpTransceiverInterface>
|
||||
PeerConnectionWrapper::AddTransceiver(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const RtpTransceiverInit& init) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
||||
pc()->AddTransceiver(track, init);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioTrackInterface> PeerConnectionWrapper::CreateAudioTrack(
|
||||
const std::string& label) {
|
||||
return pc_factory()->CreateAudioTrack(label, nullptr);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> PeerConnectionWrapper::CreateVideoTrack(
|
||||
const std::string& label) {
|
||||
return pc_factory()->CreateVideoTrack(FakeVideoTrackSource::Create(), label);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddTrack(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> result =
|
||||
pc()->AddTrack(track, stream_ids);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddTrack(
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids,
|
||||
const std::vector<RtpEncodingParameters>& init_send_encodings) {
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> result =
|
||||
pc()->AddTrack(track, stream_ids, init_send_encodings);
|
||||
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddAudioTrack(
|
||||
const std::string& track_label,
|
||||
const std::vector<std::string>& stream_ids) {
|
||||
return AddTrack(CreateAudioTrack(track_label), stream_ids);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddVideoTrack(
|
||||
const std::string& track_label,
|
||||
const std::vector<std::string>& stream_ids) {
|
||||
return AddTrack(CreateVideoTrack(track_label), stream_ids);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<DataChannelInterface>
|
||||
PeerConnectionWrapper::CreateDataChannel(
|
||||
const std::string& label,
|
||||
const absl::optional<DataChannelInit>& config) {
|
||||
const DataChannelInit* config_ptr = config.has_value() ? &(*config) : nullptr;
|
||||
auto result = pc()->CreateDataChannelOrError(label, config_ptr);
|
||||
if (!result.ok()) {
|
||||
RTC_LOG(LS_ERROR) << "CreateDataChannel failed: "
|
||||
<< ToString(result.error().type()) << " "
|
||||
<< result.error().message();
|
||||
return nullptr;
|
||||
}
|
||||
return result.MoveValue();
|
||||
}
|
||||
|
||||
PeerConnectionInterface::SignalingState
|
||||
PeerConnectionWrapper::signaling_state() {
|
||||
return pc()->signaling_state();
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::IsIceGatheringDone() {
|
||||
return observer()->ice_gathering_complete_;
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::IsIceConnected() {
|
||||
return observer()->ice_connected_;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> PeerConnectionWrapper::GetStats() {
|
||||
auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
|
||||
pc()->GetStats(callback.get());
|
||||
EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout);
|
||||
return callback->report();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue