blob: 6cfa167f8e10e8edbe87f69032368cb990181319 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/values.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/peerconnection/peer_connection_tracker.mojom-blink.h"
#include "third_party/blink/public/platform/modules/mediastream/web_media_stream.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h"
#include "third_party/blink/renderer/platform/mediastream/media_constraints.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
using webrtc::StatsReport;
using webrtc::StatsReports;
namespace blink {
class InternalStandardStatsObserver;
}
namespace WTF {
template <>
struct CrossThreadCopier<scoped_refptr<blink::InternalStandardStatsObserver>>
: public CrossThreadCopierPassThrough<
scoped_refptr<blink::InternalStandardStatsObserver>> {
STATIC_ONLY(CrossThreadCopier);
};
template <typename T>
struct CrossThreadCopier<rtc::scoped_refptr<T>> {
STATIC_ONLY(CrossThreadCopier);
using Type = rtc::scoped_refptr<T>;
static Type Copy(Type pointer) { return pointer; }
};
} // namespace WTF
namespace blink {
// TODO(hta): This module should be redesigned to reduce string copies.
namespace {
String SerializeBoolean(bool value) {
return value ? "true" : "false";
}
String SerializeServers(
const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) {
StringBuilder result;
result.Append("[");
bool following = false;
for (const auto& server : servers) {
for (const auto& url : server.urls) {
if (following)
result.Append(", ");
else
following = true;
result.Append(String::FromUTF8(url));
}
}
result.Append("]");
return result.ToString();
}
String SerializeMediaConstraints(const MediaConstraints& constraints) {
return String(constraints.ToString());
}
String SerializeOfferOptions(blink::RTCOfferOptionsPlatform* options) {
if (!options)
return "null";
StringBuilder result;
result.Append("offerToReceiveVideo: ");
result.AppendNumber(options->OfferToReceiveVideo());
result.Append(", offerToReceiveAudio: ");
result.AppendNumber(options->OfferToReceiveAudio());
result.Append(", voiceActivityDetection: ");
result.Append(SerializeBoolean(options->VoiceActivityDetection()));
result.Append(", iceRestart: ");
result.Append(SerializeBoolean(options->IceRestart()));
return result.ToString();
}
String SerializeAnswerOptions(blink::RTCAnswerOptionsPlatform* options) {
if (!options)
return "null";
StringBuilder result;
result.Append(", voiceActivityDetection: ");
result.Append(SerializeBoolean(options->VoiceActivityDetection()));
return result.ToString();
}
String SerializeMediaStreamIds(const Vector<String>& stream_ids) {
if (!stream_ids.size())
return "[]";
StringBuilder result;
result.Append("[");
for (const auto& stream_id : stream_ids) {
if (result.length() > 2u)
result.Append(",");
result.Append("'");
result.Append(stream_id);
result.Append("'");
}
result.Append("]");
return result.ToString();
}
String SerializeDirection(webrtc::RtpTransceiverDirection direction) {
switch (direction) {
case webrtc::RtpTransceiverDirection::kSendRecv:
return "'sendrecv'";
case webrtc::RtpTransceiverDirection::kSendOnly:
return "'sendonly'";
case webrtc::RtpTransceiverDirection::kRecvOnly:
return "'recvonly'";
case webrtc::RtpTransceiverDirection::kInactive:
return "'inactive'";
case webrtc::RtpTransceiverDirection::kStopped:
return "'stopped'";
default:
NOTREACHED();
return String();
}
}
String SerializeOptionalDirection(
const base::Optional<webrtc::RtpTransceiverDirection>& direction) {
return direction ? SerializeDirection(*direction) : "null";
}
String SerializeSender(const String& indent,
const blink::RTCRtpSenderPlatform& sender) {
StringBuilder result;
result.Append("{\n");
// track:'id',
result.Append(indent);
result.Append(" track:");
if (!sender.Track()) {
result.Append("null");
} else {
result.Append("'");
result.Append(sender.Track()->Id());
result.Append("'");
}
result.Append(",\n");
// streams:['id,'id'],
result.Append(indent);
result.Append(" streams:");
result.Append(SerializeMediaStreamIds(sender.StreamIds()));
result.Append(",\n");
result.Append(indent);
result.Append("}");
return result.ToString();
}
String SerializeReceiver(const String& indent,
const RTCRtpReceiverPlatform& receiver) {
StringBuilder result;
result.Append("{\n");
// track:'id',
DCHECK(receiver.Track());
result.Append(indent);
result.Append(" track:'");
result.Append(receiver.Track()->Id());
result.Append("',\n");
// streams:['id,'id'],
result.Append(indent);
result.Append(" streams:");
result.Append(SerializeMediaStreamIds(receiver.StreamIds()));
result.Append(",\n");
result.Append(indent);
result.Append("}");
return result.ToString();
}
String SerializeTransceiver(const RTCRtpTransceiverPlatform& transceiver) {
if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
StringBuilder result;
result.Append("{\n");
// mid:'foo',
if (transceiver.Mid().IsNull()) {
result.Append(" mid:null,\n");
} else {
result.Append(" mid:'");
result.Append(String(transceiver.Mid()));
result.Append("',\n");
}
// sender:{...},
result.Append(" sender:");
result.Append(SerializeSender(" ", *transceiver.Sender()));
result.Append(",\n");
// receiver:{...},
result.Append(" receiver:");
result.Append(SerializeReceiver(" ", *transceiver.Receiver()));
result.Append(",\n");
// stopped:false,
result.Append(" stopped:");
result.Append(SerializeBoolean(transceiver.Stopped()));
result.Append(",\n");
// direction:'sendrecv',
result.Append(" direction:");
result.Append(SerializeDirection(transceiver.Direction()));
result.Append(",\n");
// currentDirection:null,
result.Append(" currentDirection:");
result.Append(SerializeOptionalDirection(transceiver.CurrentDirection()));
result.Append(",\n");
result.Append("}");
return result.ToString();
}
if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
return SerializeSender("", *transceiver.Sender());
}
DCHECK(transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
return SerializeReceiver("", *transceiver.Receiver());
}
String SerializeIceTransportType(
webrtc::PeerConnectionInterface::IceTransportsType type) {
String transport_type("");
switch (type) {
case webrtc::PeerConnectionInterface::kNone:
transport_type = "none";
break;
case webrtc::PeerConnectionInterface::kRelay:
transport_type = "relay";
break;
case webrtc::PeerConnectionInterface::kAll:
transport_type = "all";
break;
case webrtc::PeerConnectionInterface::kNoHost:
transport_type = "noHost";
break;
default:
NOTREACHED();
}
return transport_type;
}
String SerializeBundlePolicy(
webrtc::PeerConnectionInterface::BundlePolicy policy) {
String policy_str("");
switch (policy) {
case webrtc::PeerConnectionInterface::kBundlePolicyBalanced:
policy_str = "balanced";
break;
case webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle:
policy_str = "max-bundle";
break;
case webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat:
policy_str = "max-compat";
break;
default:
NOTREACHED();
}
return policy_str;
}
String SerializeRtcpMuxPolicy(
webrtc::PeerConnectionInterface::RtcpMuxPolicy policy) {
String policy_str("");
switch (policy) {
case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate:
policy_str = "negotiate";
break;
case webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire:
policy_str = "require";
break;
default:
NOTREACHED();
}
return policy_str;
}
String SerializeSdpSemantics(webrtc::SdpSemantics sdp_semantics) {
String sdp_semantics_str("");
switch (sdp_semantics) {
case webrtc::SdpSemantics::kPlanB:
sdp_semantics_str = "plan-b";
break;
case webrtc::SdpSemantics::kUnifiedPlan:
sdp_semantics_str = "unified-plan";
break;
default:
NOTREACHED();
}
return sdp_semantics_str;
}
String SerializeConfiguration(
const webrtc::PeerConnectionInterface::RTCConfiguration& config) {
StringBuilder result;
// TODO(hbos): Add serialization of certificate.
result.Append("{ iceServers: ");
result.Append(SerializeServers(config.servers));
result.Append(", iceTransportPolicy: ");
result.Append(SerializeIceTransportType(config.type));
result.Append(", bundlePolicy: ");
result.Append(SerializeBundlePolicy(config.bundle_policy));
result.Append(", rtcpMuxPolicy: ");
result.Append(SerializeRtcpMuxPolicy(config.rtcp_mux_policy));
result.Append(", iceCandidatePoolSize: ");
result.AppendNumber(config.ice_candidate_pool_size);
result.Append(", sdpSemantics: \"");
result.Append(SerializeSdpSemantics(config.sdp_semantics));
result.Append("\" }");
return result.ToString();
}
// Note: All of these strings need to be kept in sync with
// peer_connection_update_table.js, in order to be displayed as friendly
// strings on chrome://webrtc-internals.
const char* GetSignalingStateString(
webrtc::PeerConnectionInterface::SignalingState state) {
const char* result = "";
switch (state) {
case webrtc::PeerConnectionInterface::SignalingState::kStable:
return "SignalingStateStable";
case webrtc::PeerConnectionInterface::SignalingState::kHaveLocalOffer:
return "SignalingStateHaveLocalOffer";
case webrtc::PeerConnectionInterface::SignalingState::kHaveRemoteOffer:
return "SignalingStateHaveRemoteOffer";
case webrtc::PeerConnectionInterface::SignalingState::kHaveLocalPrAnswer:
return "SignalingStateHaveLocalPrAnswer";
case webrtc::PeerConnectionInterface::SignalingState::kHaveRemotePrAnswer:
return "SignalingStateHaveRemotePrAnswer";
case webrtc::PeerConnectionInterface::SignalingState::kClosed:
return "SignalingStateClosed";
default:
NOTREACHED();
break;
}
return result;
}
const char* GetIceConnectionStateString(
webrtc::PeerConnectionInterface::IceConnectionState state) {
switch (state) {
case webrtc::PeerConnectionInterface::kIceConnectionNew:
return "new";
case webrtc::PeerConnectionInterface::kIceConnectionChecking:
return "checking";
case webrtc::PeerConnectionInterface::kIceConnectionConnected:
return "connected";
case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
return "completed";
case webrtc::PeerConnectionInterface::kIceConnectionFailed:
return "failed";
case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
return "disconnected";
case webrtc::PeerConnectionInterface::kIceConnectionClosed:
return "closed";
default:
NOTREACHED();
return "";
}
}
const char* GetConnectionStateString(
webrtc::PeerConnectionInterface::PeerConnectionState state) {
switch (state) {
case webrtc::PeerConnectionInterface::PeerConnectionState::kNew:
return "new";
case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting:
return "connecting";
case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
return "connected";
case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
return "disconnected";
case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed:
return "failed";
case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed:
return "closed";
default:
NOTREACHED();
return "";
}
}
const char* GetIceGatheringStateString(
webrtc::PeerConnectionInterface::IceGatheringState state) {
switch (state) {
case webrtc::PeerConnectionInterface::kIceGatheringNew:
return "new";
case webrtc::PeerConnectionInterface::kIceGatheringGathering:
return "gathering";
case webrtc::PeerConnectionInterface::kIceGatheringComplete:
return "complete";
default:
NOTREACHED();
return "";
}
}
const char* GetTransceiverUpdatedReasonString(
PeerConnectionTracker::TransceiverUpdatedReason reason) {
switch (reason) {
case PeerConnectionTracker::TransceiverUpdatedReason::kAddTransceiver:
return "addTransceiver";
case PeerConnectionTracker::TransceiverUpdatedReason::kAddTrack:
return "addTrack";
case PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack:
return "removeTrack";
case PeerConnectionTracker::TransceiverUpdatedReason::kSetLocalDescription:
return "setLocalDescription";
case PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription:
return "setRemoteDescription";
}
NOTREACHED();
return nullptr;
}
// Builds a DictionaryValue from the StatsReport.
// Note:
// The format must be consistent with what webrtc_internals.js expects.
// If you change it here, you must change webrtc_internals.js as well.
std::unique_ptr<base::DictionaryValue> GetDictValueStats(
const StatsReport& report) {
if (report.values().empty())
return nullptr;
auto values = std::make_unique<base::ListValue>();
for (const auto& v : report.values()) {
const StatsReport::ValuePtr& value = v.second;
values->AppendString(value->display_name());
switch (value->type()) {
case StatsReport::Value::kInt:
values->AppendInteger(value->int_val());
break;
case StatsReport::Value::kFloat:
values->AppendDouble(value->float_val());
break;
case StatsReport::Value::kString:
values->AppendString(value->string_val());
break;
case StatsReport::Value::kStaticString:
values->AppendString(value->static_string_val());
break;
case StatsReport::Value::kBool:
values->AppendBoolean(value->bool_val());
break;
case StatsReport::Value::kInt64: // int64_t isn't supported, so use
// string.
case StatsReport::Value::kId:
default:
values->AppendString(value->ToString());
break;
}
}
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetDouble("timestamp", report.timestamp());
dict->Set("values", std::move(values));
return dict;
}
// Builds a DictionaryValue from the StatsReport.
// The caller takes the ownership of the returned value.
std::unique_ptr<base::DictionaryValue> GetDictValue(const StatsReport& report) {
std::unique_ptr<base::DictionaryValue> stats = GetDictValueStats(report);
if (!stats)
return nullptr;
// Note:
// The format must be consistent with what webrtc_internals.js expects.
// If you change it here, you must change webrtc_internals.js as well.
auto result = std::make_unique<base::DictionaryValue>();
result->Set("stats", std::move(stats));
result->SetString("id", report.id()->ToString());
result->SetString("type", report.TypeToString());
return result;
}
} // namespace
// chrome://webrtc-internals displays stats and stats graphs. The call path
// involves thread and process hops (IPC). This is the webrtc::StatsObserver
// that is used when webrtc-internals wants legacy stats. It starts in
// webrtc_internals.js performing requestLegacyStats and the result gets
// asynchronously delivered to webrtc_internals.js at addLegacyStats.
class InternalLegacyStatsObserver : public webrtc::StatsObserver {
public:
InternalLegacyStatsObserver(
int lid,
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
CrossThreadOnceFunction<void(int, base::Value)> completion_callback)
: lid_(lid),
main_thread_(std::move(main_thread)),
completion_callback_(std::move(completion_callback)) {}
void OnComplete(const StatsReports& reports) override {
auto list = std::make_unique<base::ListValue>();
for (const auto* r : reports) {
std::unique_ptr<base::DictionaryValue> report = GetDictValue(*r);
if (report)
list->Append(std::move(report));
}
if (!list->empty()) {
PostCrossThreadTask(
*main_thread_.get(), FROM_HERE,
CrossThreadBindOnce(&InternalLegacyStatsObserver::OnCompleteImpl,
std::move(list), lid_,
std::move(completion_callback_)));
}
}
protected:
~InternalLegacyStatsObserver() override {
// Will be destructed on libjingle's signaling thread.
// The signaling thread is where libjingle's objects live and from where
// libjingle makes callbacks. This may or may not be the same thread as
// the main thread.
}
private:
// Static since |this| will most likely have been deleted by the time we
// get here.
static void OnCompleteImpl(
std::unique_ptr<base::ListValue> list,
int lid,
CrossThreadOnceFunction<void(int, base::Value)> completion_callback) {
DCHECK(!list->empty());
std::move(completion_callback).Run(lid, std::move(*list.get()));
}
const int lid_;
const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
CrossThreadOnceFunction<void(int, base::Value)> completion_callback_;
};
// chrome://webrtc-internals displays stats and stats graphs. The call path
// involves thread and process hops (IPC). This is the ----webrtc::StatsObserver
// that is used when webrtc-internals wants standard stats. It starts in
// webrtc_internals.js performing requestStandardStats and the result gets
// asynchronously delivered to webrtc_internals.js at addStandardStats.
class InternalStandardStatsObserver : public webrtc::RTCStatsCollectorCallback {
public:
InternalStandardStatsObserver(
int lid,
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
CrossThreadOnceFunction<void(int, base::Value)> completion_callback)
: lid_(lid),
main_thread_(std::move(main_thread)),
completion_callback_(std::move(completion_callback)) {}
void OnStatsDelivered(
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override {
// We're on the signaling thread.
DCHECK(!main_thread_->BelongsToCurrentThread());
PostCrossThreadTask(
*main_thread_.get(), FROM_HERE,
CrossThreadBindOnce(
&InternalStandardStatsObserver::OnStatsDeliveredOnMainThread,
scoped_refptr<InternalStandardStatsObserver>(this), report));
}
protected:
~InternalStandardStatsObserver() override {}
private:
void OnStatsDeliveredOnMainThread(
rtc::scoped_refptr<const webrtc::RTCStatsReport> report) {
auto list = ReportToList(report);
std::move(completion_callback_).Run(lid_, std::move(*list.get()));
}
std::unique_ptr<base::ListValue> ReportToList(
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) {
std::unique_ptr<base::ListValue> result_list(new base::ListValue());
for (const auto& stats : *report) {
// The format of "stats_subdictionary" is:
// {timestamp:<milliseconds>, values: [<key-value pairs>]}
auto stats_subdictionary = std::make_unique<base::DictionaryValue>();
// Timestamp is reported in milliseconds.
stats_subdictionary->SetDouble("timestamp",
stats.timestamp_us() / 1000.0);
// Values are reported as
// "values": ["member1", value, "member2", value...]
auto name_value_pairs = std::make_unique<base::ListValue>();
for (const auto* member : stats.Members()) {
if (!member->is_defined())
continue;
// Non-standardized / provisional stats which are not exposed
// to Javascript are postfixed with an asterisk.
std::string postfix = member->is_standardized() ? "" : "*";
name_value_pairs->AppendString(member->name() + postfix);
name_value_pairs->Append(MemberToValue(*member));
}
stats_subdictionary->Set("values", std::move(name_value_pairs));
// The format of "stats_dictionary" is:
// {id:<string>, stats:<stats_subdictionary>, type:<string>}
auto stats_dictionary = std::make_unique<base::DictionaryValue>();
stats_dictionary->Set("stats", std::move(stats_subdictionary));
stats_dictionary->SetString("id", stats.id());
stats_dictionary->SetString("type", stats.type());
result_list->Append(std::move(stats_dictionary));
}
return result_list;
}
std::unique_ptr<base::Value> MemberToValue(
const webrtc::RTCStatsMemberInterface& member) {
switch (member.type()) {
// Types supported by base::Value are passed as the appropriate type.
case webrtc::RTCStatsMemberInterface::Type::kBool:
return std::make_unique<base::Value>(
*member.cast_to<webrtc::RTCStatsMember<bool>>());
case webrtc::RTCStatsMemberInterface::Type::kInt32:
return std::make_unique<base::Value>(
*member.cast_to<webrtc::RTCStatsMember<int32_t>>());
case webrtc::RTCStatsMemberInterface::Type::kString:
return std::make_unique<base::Value>(
*member.cast_to<webrtc::RTCStatsMember<std::string>>());
// Types not supported by base::Value are converted to string.
case webrtc::RTCStatsMemberInterface::Type::kUint32:
case webrtc::RTCStatsMemberInterface::Type::kInt64:
case webrtc::RTCStatsMemberInterface::Type::kUint64:
case webrtc::RTCStatsMemberInterface::Type::kDouble:
case webrtc::RTCStatsMemberInterface::Type::kSequenceBool:
case webrtc::RTCStatsMemberInterface::Type::kSequenceInt32:
case webrtc::RTCStatsMemberInterface::Type::kSequenceUint32:
case webrtc::RTCStatsMemberInterface::Type::kSequenceInt64:
case webrtc::RTCStatsMemberInterface::Type::kSequenceUint64:
case webrtc::RTCStatsMemberInterface::Type::kSequenceDouble:
case webrtc::RTCStatsMemberInterface::Type::kSequenceString:
default:
return std::make_unique<base::Value>(member.ValueToString());
}
}
const int lid_;
const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
CrossThreadOnceFunction<void(int, base::Value)> completion_callback_;
};
// static
PeerConnectionTracker* PeerConnectionTracker::GetInstance() {
DEFINE_STATIC_LOCAL(PeerConnectionTracker, instance,
(Thread::MainThread()->GetTaskRunner()));
return &instance;
}
PeerConnectionTracker::PeerConnectionTracker(
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
: next_local_id_(1),
main_thread_task_runner_(std::move(main_thread_task_runner)) {
blink::Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
peer_connection_tracker_host_.BindNewPipeAndPassReceiver());
}
PeerConnectionTracker::PeerConnectionTracker(
mojo::Remote<blink::mojom::blink::PeerConnectionTrackerHost> host,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
: next_local_id_(1),
peer_connection_tracker_host_(std::move(host)),
main_thread_task_runner_(std::move(main_thread_task_runner)) {}
PeerConnectionTracker::~PeerConnectionTracker() {}
void PeerConnectionTracker::Bind(
mojo::PendingReceiver<blink::mojom::blink::PeerConnectionManager>
receiver) {
DCHECK(!receiver_.is_bound());
receiver_.Bind(std::move(receiver));
}
void PeerConnectionTracker::OnSuspend() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
// Closing peer connections fires events. If JavaScript triggers the creation
// or garbage collection of more peer connections, this would invalidate the
// |peer_connection_local_id_map_| iterator. Therefor we iterate on a copy.
PeerConnectionLocalIdMap peer_connection_map_copy =
peer_connection_local_id_map_;
for (const auto& pair : peer_connection_map_copy) {
RTCPeerConnectionHandler* peer_connection_handler = pair.key;
if (!base::Contains(peer_connection_local_id_map_,
peer_connection_handler)) {
// Skip peer connections that have been unregistered during this method
// call. Avoids use-after-free.
continue;
}
peer_connection_handler->CloseClientPeerConnection();
}
}
void PeerConnectionTracker::OnThermalStateChange(
mojom::blink::DeviceThermalState thermal_state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
current_thermal_state_ = thermal_state;
for (auto& entry : peer_connection_local_id_map_) {
entry.key->OnThermalStateChange(current_thermal_state_);
}
}
void PeerConnectionTracker::StartEventLog(int peer_connection_local_id,
int output_period_ms) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
for (auto& it : peer_connection_local_id_map_) {
if (it.value == peer_connection_local_id) {
it.key->StartEventLog(output_period_ms);
return;
}
}
}
void PeerConnectionTracker::StopEventLog(int peer_connection_local_id) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
for (auto& it : peer_connection_local_id_map_) {
if (it.value == peer_connection_local_id) {
it.key->StopEventLog();
return;
}
}
}
void PeerConnectionTracker::GetStandardStats() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
for (const auto& pair : peer_connection_local_id_map_) {
scoped_refptr<InternalStandardStatsObserver> observer(
new rtc::RefCountedObject<InternalStandardStatsObserver>(
pair.value, main_thread_task_runner_,
CrossThreadBindOnce(&PeerConnectionTracker::AddStandardStats,
AsWeakPtr())));
pair.key->GetStandardStatsForTracker(observer);
}
}
void PeerConnectionTracker::GetLegacyStats() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
for (const auto& pair : peer_connection_local_id_map_) {
rtc::scoped_refptr<InternalLegacyStatsObserver> observer(
new rtc::RefCountedObject<InternalLegacyStatsObserver>(
pair.value, main_thread_task_runner_,
CrossThreadBindOnce(&PeerConnectionTracker::AddLegacyStats,
AsWeakPtr())));
pair.key->GetStats(observer,
webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
nullptr);
}
}
void PeerConnectionTracker::RegisterPeerConnection(
RTCPeerConnectionHandler* pc_handler,
const webrtc::PeerConnectionInterface::RTCConfiguration& config,
const MediaConstraints& constraints,
const blink::WebLocalFrame* frame) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
DCHECK(pc_handler);
DCHECK_EQ(GetLocalIDForHandler(pc_handler), -1);
DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
auto info = blink::mojom::blink::PeerConnectionInfo::New();
info->lid = GetNextLocalID();
info->rtc_configuration = SerializeConfiguration(config);
info->constraints = SerializeMediaConstraints(constraints);
if (frame)
info->url = frame->GetDocument().Url().GetString();
else
info->url = "test:testing";
int32_t lid = info->lid;
peer_connection_tracker_host_->AddPeerConnection(std::move(info));
peer_connection_local_id_map_.insert(pc_handler, lid);
if (current_thermal_state_ != mojom::blink::DeviceThermalState::kUnknown) {
pc_handler->OnThermalStateChange(current_thermal_state_);
}
}
void PeerConnectionTracker::UnregisterPeerConnection(
RTCPeerConnectionHandler* pc_handler) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
auto it = peer_connection_local_id_map_.find(pc_handler);
if (it == peer_connection_local_id_map_.end()) {
// The PeerConnection might not have been registered if its initilization
// failed.
return;
}
peer_connection_tracker_host_->RemovePeerConnection(it->value);
peer_connection_local_id_map_.erase(it);
}
void PeerConnectionTracker::TrackCreateOffer(
RTCPeerConnectionHandler* pc_handler,
RTCOfferOptionsPlatform* options) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "createOffer",
"options: {" + SerializeOfferOptions(options) + "}");
}
void PeerConnectionTracker::TrackCreateOffer(
RTCPeerConnectionHandler* pc_handler,
const MediaConstraints& constraints) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(
id, "createOffer",
"constraints: {" + SerializeMediaConstraints(constraints) + "}");
}
void PeerConnectionTracker::TrackCreateAnswer(
RTCPeerConnectionHandler* pc_handler,
blink::RTCAnswerOptionsPlatform* options) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(
id, "createAnswer", "options: {" + SerializeAnswerOptions(options) + "}");
}
void PeerConnectionTracker::TrackCreateAnswer(
RTCPeerConnectionHandler* pc_handler,
const MediaConstraints& constraints) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(
id, "createAnswer",
"constraints: {" + SerializeMediaConstraints(constraints) + "}");
}
void PeerConnectionTracker::TrackSetSessionDescription(
RTCPeerConnectionHandler* pc_handler,
const String& sdp,
const String& type,
Source source) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String value = "type: " + type + ", sdp: " + sdp;
SendPeerConnectionUpdate(
id,
source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
value);
}
void PeerConnectionTracker::TrackSetSessionDescriptionImplicit(
RTCPeerConnectionHandler* pc_handler) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "setLocalDescriptionImplicitCreateOfferOrAnswer",
"");
}
void PeerConnectionTracker::TrackSetConfiguration(
RTCPeerConnectionHandler* pc_handler,
const webrtc::PeerConnectionInterface::RTCConfiguration& config) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "setConfiguration",
SerializeConfiguration(config));
}
void PeerConnectionTracker::TrackAddIceCandidate(
RTCPeerConnectionHandler* pc_handler,
RTCIceCandidatePlatform* candidate,
Source source,
bool succeeded) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String value =
"sdpMid: " + String(candidate->SdpMid()) + ", " + "sdpMLineIndex: " +
(candidate->SdpMLineIndex() ? String::Number(*candidate->SdpMLineIndex())
: "null") +
", " + "candidate: " + String(candidate->Candidate());
// OnIceCandidate always succeeds as it's a callback from the browser.
DCHECK(source != SOURCE_LOCAL || succeeded);
const char* event =
(source == SOURCE_LOCAL)
? "onIceCandidate"
: (succeeded ? "addIceCandidate" : "addIceCandidateFailed");
SendPeerConnectionUpdate(id, event, value);
}
void PeerConnectionTracker::TrackIceCandidateError(
RTCPeerConnectionHandler* pc_handler,
const String& address,
base::Optional<uint16_t> port,
const String& host_candidate,
const String& url,
int error_code,
const String& error_text) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String address_string = address ? "address: " + address + "\n" : String();
String port_string =
port.has_value() ? String::Format("port: %d\n", port.value()) : "";
String value = "url: " + url + "\n" + address_string + port_string +
"host_candidate: " + host_candidate + "\n" +
"error_text: " + error_text + "\n" +
"error_code: " + String::Number(error_code);
SendPeerConnectionUpdate(id, "icecandidateerror", value);
}
void PeerConnectionTracker::TrackAddTransceiver(
RTCPeerConnectionHandler* pc_handler,
PeerConnectionTracker::TransceiverUpdatedReason reason,
const RTCRtpTransceiverPlatform& transceiver,
size_t transceiver_index) {
TrackTransceiver("Added", pc_handler, reason, transceiver, transceiver_index);
}
void PeerConnectionTracker::TrackModifyTransceiver(
RTCPeerConnectionHandler* pc_handler,
PeerConnectionTracker::TransceiverUpdatedReason reason,
const RTCRtpTransceiverPlatform& transceiver,
size_t transceiver_index) {
TrackTransceiver("Modified", pc_handler, reason, transceiver,
transceiver_index);
}
void PeerConnectionTracker::TrackRemoveTransceiver(
RTCPeerConnectionHandler* pc_handler,
PeerConnectionTracker::TransceiverUpdatedReason reason,
const RTCRtpTransceiverPlatform& transceiver,
size_t transceiver_index) {
TrackTransceiver("Removed", pc_handler, reason, transceiver,
transceiver_index);
}
void PeerConnectionTracker::TrackTransceiver(
const char* callback_type_ending,
RTCPeerConnectionHandler* pc_handler,
PeerConnectionTracker::TransceiverUpdatedReason reason,
const RTCRtpTransceiverPlatform& transceiver,
size_t transceiver_index) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String callback_type;
if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
callback_type = "transceiver";
} else if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
callback_type = "sender";
} else {
callback_type = "receiver";
}
callback_type = callback_type + callback_type_ending;
StringBuilder result;
result.Append("Caused by: ");
result.Append(GetTransceiverUpdatedReasonString(reason));
result.Append("\n\n");
if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
result.Append("getTransceivers()");
} else if (transceiver.ImplementationType() ==
RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
result.Append("getSenders()");
} else {
DCHECK_EQ(transceiver.ImplementationType(),
RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
result.Append("getReceivers()");
}
result.Append(String("[" + String::Number(transceiver_index) + "]:"));
result.Append(SerializeTransceiver(transceiver));
SendPeerConnectionUpdate(id, callback_type, result.ToString());
}
void PeerConnectionTracker::TrackCreateDataChannel(
RTCPeerConnectionHandler* pc_handler,
const webrtc::DataChannelInterface* data_channel,
Source source) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String value = "label: " + String::FromUTF8(data_channel->label()) +
", reliable: " + SerializeBoolean(data_channel->reliable());
SendPeerConnectionUpdate(
id,
source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel",
value);
}
void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "stop", String(""));
}
void PeerConnectionTracker::TrackSignalingStateChange(
RTCPeerConnectionHandler* pc_handler,
webrtc::PeerConnectionInterface::SignalingState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "signalingStateChange",
GetSignalingStateString(state));
}
void PeerConnectionTracker::TrackLegacyIceConnectionStateChange(
RTCPeerConnectionHandler* pc_handler,
webrtc::PeerConnectionInterface::IceConnectionState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "legacyIceConnectionStateChange",
GetIceConnectionStateString(state));
}
void PeerConnectionTracker::TrackIceConnectionStateChange(
RTCPeerConnectionHandler* pc_handler,
webrtc::PeerConnectionInterface::IceConnectionState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "iceConnectionStateChange",
GetIceConnectionStateString(state));
}
void PeerConnectionTracker::TrackConnectionStateChange(
RTCPeerConnectionHandler* pc_handler,
webrtc::PeerConnectionInterface::PeerConnectionState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "connectionStateChange",
GetConnectionStateString(state));
}
void PeerConnectionTracker::TrackIceGatheringStateChange(
RTCPeerConnectionHandler* pc_handler,
webrtc::PeerConnectionInterface::IceGatheringState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "iceGatheringStateChange",
GetIceGatheringStateString(state));
}
void PeerConnectionTracker::TrackSessionDescriptionCallback(
RTCPeerConnectionHandler* pc_handler,
Action action,
const String& callback_type,
const String& value) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
String update_type;
switch (action) {
case ACTION_SET_LOCAL_DESCRIPTION:
update_type = "setLocalDescription";
break;
case ACTION_SET_LOCAL_DESCRIPTION_IMPLICIT:
update_type = "setLocalDescriptionImplicitCreateOfferOrAnswer";
break;
case ACTION_SET_REMOTE_DESCRIPTION:
update_type = "setRemoteDescription";
break;
case ACTION_CREATE_OFFER:
update_type = "createOffer";
break;
case ACTION_CREATE_ANSWER:
update_type = "createAnswer";
break;
default:
NOTREACHED();
break;
}
update_type = update_type + callback_type;
SendPeerConnectionUpdate(id, update_type, value);
}
void PeerConnectionTracker::TrackSessionId(RTCPeerConnectionHandler* pc_handler,
const String& session_id) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
DCHECK(pc_handler);
DCHECK(!session_id.IsEmpty());
const int local_id = GetLocalIDForHandler(pc_handler);
if (local_id == -1) {
return;
}
String non_null_session_id =
session_id.IsNull() ? WTF::g_empty_string : session_id;
peer_connection_tracker_host_->OnPeerConnectionSessionIdSet(
local_id, non_null_session_id);
}
void PeerConnectionTracker::TrackOnRenegotiationNeeded(
RTCPeerConnectionHandler* pc_handler) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
SendPeerConnectionUpdate(id, "onRenegotiationNeeded", String(""));
}
void PeerConnectionTracker::TrackGetUserMedia(
UserMediaRequest* user_media_request) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
// When running tests, it is possible that UserMediaRequest's
// ExecutionContext is null.
//
// TODO(crbug.com/704136): Is there a better way to do this?
String security_origin;
if (!user_media_request->GetExecutionContext()) {
security_origin =
SecurityOrigin::CreateFromString("test://test")->ToString();
} else {
security_origin = user_media_request->GetExecutionContext()
->GetSecurityOrigin()
->ToString();
}
peer_connection_tracker_host_->GetUserMedia(
security_origin, user_media_request->Audio(), user_media_request->Video(),
SerializeMediaConstraints(user_media_request->AudioConstraints()),
SerializeMediaConstraints(user_media_request->VideoConstraints()));
}
void PeerConnectionTracker::TrackRtcEventLogWrite(
RTCPeerConnectionHandler* pc_handler,
const WTF::Vector<uint8_t>& output) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
int id = GetLocalIDForHandler(pc_handler);
if (id == -1)
return;
peer_connection_tracker_host_->WebRtcEventLogWrite(id, output);
}
int PeerConnectionTracker::GetNextLocalID() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
if (next_local_id_ < 0)
next_local_id_ = 1;
return next_local_id_++;
}
int PeerConnectionTracker::GetLocalIDForHandler(
RTCPeerConnectionHandler* handler) const {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
const auto found = peer_connection_local_id_map_.find(handler);
if (found == peer_connection_local_id_map_.end())
return -1;
DCHECK_NE(found->value, -1);
return found->value;
}
void PeerConnectionTracker::SendPeerConnectionUpdate(
int local_id,
const String& callback_type,
const String& value) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
peer_connection_tracker_host_->UpdatePeerConnection(local_id, callback_type,
value);
}
void PeerConnectionTracker::AddStandardStats(int lid, base::Value value) {
peer_connection_tracker_host_->AddStandardStats(lid, std::move(value));
}
void PeerConnectionTracker::AddLegacyStats(int lid, base::Value value) {
peer_connection_tracker_host_->AddLegacyStats(lid, std::move(value));
}
} // namespace blink