| // Copyright 2013 The Chromium Authors |
| // 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 <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/contains.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/types/pass_key.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "build/chromecast_buildflags.h" |
| #include "third_party/blink/public/mojom/peerconnection/peer_connection_tracker.mojom-blink.h" |
| #include "third_party/blink/public/platform/browser_interface_broker_proxy.h" |
| #include "third_party/blink/public/platform/interface_registry.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/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/modules/mediastream/media_constraints.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/json/json_values.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h" |
| #include "third_party/blink/renderer/platform/mojo/mojo_binding_context.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/peerconnection/webrtc_util.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" |
| #include "third_party/webrtc/api/stats/rtcstats_objects.h" |
| |
| using webrtc::StatsReport; |
| using webrtc::StatsReports; |
| |
| namespace blink { |
| |
| class InternalStandardStatsObserver; |
| |
| template <> |
| struct CrossThreadCopier<scoped_refptr<InternalStandardStatsObserver>> |
| : public CrossThreadCopierPassThrough< |
| scoped_refptr<InternalStandardStatsObserver>> { |
| STATIC_ONLY(CrossThreadCopier); |
| }; |
| |
| template <typename T> |
| struct CrossThreadCopier<webrtc::scoped_refptr<T>> { |
| STATIC_ONLY(CrossThreadCopier); |
| using Type = webrtc::scoped_refptr<T>; |
| static Type Copy(Type pointer) { return pointer; } |
| }; |
| |
| template <> |
| struct CrossThreadCopier<base::Value::List> |
| : public CrossThreadCopierByValuePassThrough<base::Value::List> { |
| STATIC_ONLY(CrossThreadCopier); |
| }; |
| |
| // TODO(hta): This module should be redesigned to reduce string copies. |
| |
| namespace { |
| |
| String SerializeGetUserMediaMediaConstraints( |
| const MediaConstraints& constraints) { |
| return constraints.ToString(); |
| } |
| |
| String SerializeOfferOptions(blink::RTCOfferOptionsPlatform* options) { |
| if (!options) { |
| return "null"; |
| } |
| |
| auto json = std::make_unique<JSONObject>(); |
| if (options->OfferToReceiveAudio()) { |
| json->SetBoolean("offerToReceiveAudio", true); |
| } |
| if (options->OfferToReceiveVideo()) { |
| json->SetBoolean("offerToReceiveVideo", true); |
| } |
| if (options->VoiceActivityDetection()) { |
| json->SetBoolean("voiceActivityDetection", true); |
| } |
| if (options->IceRestart()) { |
| json->SetBoolean("iceRestart", true); |
| } |
| StringBuilder value; |
| json->WriteJSON(&value); |
| return value.ToString(); |
| } |
| |
| String SerializeAnswerOptions(blink::RTCAnswerOptionsPlatform* options) { |
| if (!options) { |
| return "null"; |
| } |
| |
| auto json = std::make_unique<JSONObject>(); |
| if (options->VoiceActivityDetection()) { |
| json->SetBoolean("voiceActivityDetection", true); |
| } |
| StringBuilder value; |
| json->WriteJSON(&value); |
| return value.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(); |
| } |
| } |
| |
| String SerializeTransceiverKind(const RTCRtpTransceiverPlatform& transceiver) { |
| DCHECK(transceiver.Receiver()); |
| DCHECK(transceiver.Receiver()->Track()); |
| |
| switch (transceiver.Receiver()->Track()->GetSourceType()) { |
| case MediaStreamSource::StreamType::kTypeAudio: |
| return "audio"; |
| case MediaStreamSource::StreamType::kTypeVideo: |
| return "video"; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| std::unique_ptr<JSONArray> SerializeEncodingParameters( |
| const std::vector<webrtc::RtpEncodingParameters>& send_encodings) { |
| auto encodings = std::make_unique<JSONArray>(); |
| for (const auto& encoding : send_encodings) { |
| auto obj = std::make_unique<JSONObject>(); |
| obj->SetBoolean("active", encoding.active); |
| if (!encoding.rid.empty()) { |
| obj->SetString("rid", String(encoding.rid)); |
| } |
| if (encoding.max_framerate) { |
| obj->SetDouble("maxFramerate", *encoding.max_framerate); |
| } |
| if (encoding.max_bitrate_bps) { |
| obj->SetInteger("maxBitrate", *encoding.max_bitrate_bps); |
| } |
| if (encoding.scale_resolution_down_by) { |
| obj->SetDouble("scaleResolutionDownBy", |
| *encoding.scale_resolution_down_by); |
| } |
| if (encoding.scale_resolution_down_to) { |
| auto res = std::make_unique<JSONObject>(); |
| res->SetInteger("width", encoding.scale_resolution_down_to->width); |
| res->SetInteger("height", encoding.scale_resolution_down_to->height); |
| obj->SetObject("scaleResolutionDownTo", std::move(res)); |
| } |
| if (encoding.scalability_mode) { |
| obj->SetString("scalabilityMode", String(*encoding.scalability_mode)); |
| } |
| if (encoding.adaptive_ptime) { |
| obj->SetBoolean("adpativePtime", true); |
| } |
| encodings->PushObject(std::move(obj)); |
| } |
| return encodings; |
| } |
| |
| std::unique_ptr<JSONObject> SerializeSender( |
| const blink::RTCRtpSenderPlatform& sender) { |
| auto json = std::make_unique<JSONObject>(); |
| if (sender.Track()) { |
| json->SetString("track", String(sender.Track()->Id())); |
| } else { |
| json->SetValue("track", JSONValue::Null()); |
| } |
| |
| auto stream_ids = std::make_unique<JSONArray>(); |
| for (const auto& stream_id : sender.StreamIds()) { |
| stream_ids->PushString(String(stream_id)); |
| } |
| json->SetArray("streams", std::move(stream_ids)); |
| |
| json->SetArray("encodings", SerializeEncodingParameters( |
| sender.GetParameters()->encodings)); |
| return json; |
| } |
| |
| std::unique_ptr<JSONObject> SerializeReceiver( |
| const RTCRtpReceiverPlatform& receiver) { |
| auto json = std::make_unique<JSONObject>(); |
| DCHECK(receiver.Track()); |
| json->SetString("track", String(receiver.Track()->Id())); |
| auto stream_ids = std::make_unique<JSONArray>(); |
| for (const auto& stream_id : receiver.StreamIds()) { |
| stream_ids->PushString(String(stream_id)); |
| } |
| json->SetArray("streams", std::move(stream_ids)); |
| return json; |
| } |
| |
| std::unique_ptr<JSONObject> SerializeTransceiver( |
| const RTCRtpTransceiverPlatform& transceiver) { |
| auto json = std::make_unique<JSONObject>(); |
| if (transceiver.Mid().IsNull()) { |
| json->SetValue("mid", JSONValue::Null()); |
| } else { |
| json->SetString("mid", String(transceiver.Mid())); |
| } |
| json->SetString("kind", SerializeTransceiverKind(transceiver)); |
| json->SetValue("sender", SerializeSender(*transceiver.Sender())); |
| json->SetValue("receiver", SerializeReceiver(*transceiver.Receiver())); |
| json->SetString("direction", SerializeDirection(transceiver.Direction())); |
| if (transceiver.CurrentDirection().has_value()) { |
| json->SetString("currentDirection", |
| SerializeDirection(*transceiver.CurrentDirection())); |
| } else { |
| json->SetValue("currentDirection", JSONValue::Null()); |
| } |
| return json; |
| } |
| |
| // Serializes things that are of interest from the RTCConfiguration. |
| String SerializeConfiguration( |
| const webrtc::PeerConnectionInterface::RTCConfiguration& config, |
| bool usesInsertableStreams) { |
| auto json = std::make_unique<JSONObject>(); |
| // Serialize iceServers (without username and credential). |
| if (!config.servers.empty()) { |
| auto servers = std::make_unique<JSONArray>(); |
| for (const auto& ice_server : config.servers) { |
| auto server = std::make_unique<JSONObject>(); |
| auto urls = std::make_unique<JSONArray>(); |
| for (const auto& url : ice_server.urls) { |
| urls->PushString(String(url)); |
| } |
| server->SetArray("urls", std::move(urls)); |
| servers->PushObject(std::move(server)); |
| } |
| json->SetArray("iceServers", std::move(servers)); |
| } |
| // Serialize iceTransportPolicy. |
| switch (config.type) { |
| case webrtc::PeerConnectionInterface::kRelay: |
| json->SetString("iceTransportPolicy", "relay"); |
| break; |
| default: |
| // The other values are the default or not web-exposed. |
| break; |
| } |
| // Serialize iceCandidatePoolSize. |
| if (config.ice_candidate_pool_size > 0) { |
| json->SetInteger("iceCandidatePoolSize", config.ice_candidate_pool_size); |
| } |
| // Serialize bundlePolicy. |
| switch (config.bundle_policy) { |
| case webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle: |
| json->SetString("bundlePolicy", "max-bundle"); |
| break; |
| case webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat: |
| json->SetString("bundlePolicy", "max-compat"); |
| break; |
| default: |
| // "balanced" is the default and not serialized. |
| break; |
| } |
| // Serialize rtcpMuxPolicy. |
| switch (config.rtcp_mux_policy) { |
| case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate: |
| // No longer standard. |
| json->SetString("rtcpMuxPolicy", "negotiate"); |
| break; |
| default: |
| // "require" is the default and not serialized. |
| break; |
| } |
| // Serialize (non-standard and obsolete) encodedInsertableStreams. |
| if (usesInsertableStreams) { |
| json->SetBoolean("encodedInsertableStreams", true); |
| } |
| // TODO(hbos): Add serialization of certificate. |
| StringBuilder value; |
| json->WriteJSON(&value); |
| return value.ToString(); |
| } |
| |
| 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(); |
| } |
| |
| int GetNextProcessLocalID() { |
| static int next_local_id = 1; |
| return next_local_id++; |
| } |
| |
| } // namespace |
| |
| // chrome://webrtc-internals displays stats and stats graphs. The call path |
| // involves thread and process hops (IPC). This is the stats observer 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( |
| const base::WeakPtr<RTCPeerConnectionHandler> pc_handler, |
| int lid, |
| scoped_refptr<base::SingleThreadTaskRunner> main_thread, |
| Vector<std::unique_ptr<blink::RTCRtpSenderPlatform>> senders, |
| CrossThreadOnceFunction<void(int, base::Value::List)> completion_callback) |
| : pc_handler_(pc_handler), |
| lid_(lid), |
| main_thread_(std::move(main_thread)), |
| senders_(std::move(senders)), |
| completion_callback_(std::move(completion_callback)) {} |
| |
| void OnStatsDelivered( |
| const webrtc::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( |
| webrtc::scoped_refptr<const webrtc::RTCStatsReport> report) { |
| std::move(completion_callback_).Run(lid_, ReportToList(report)); |
| } |
| |
| base::Value::List ReportToList( |
| const webrtc::scoped_refptr<const webrtc::RTCStatsReport>& report) { |
| std::map<std::string, MediaStreamTrackPlatform*> tracks_by_id; |
| for (const auto& sender : senders_) { |
| MediaStreamComponent* track_component = sender->Track(); |
| if (!track_component) { |
| continue; |
| } |
| tracks_by_id.insert(std::make_pair(track_component->Id().Utf8(), |
| track_component->GetPlatformTrack())); |
| } |
| |
| base::Value::List result_list; |
| |
| if (!pc_handler_) { |
| return result_list; |
| } |
| auto* local_frame = To<WebLocalFrameImpl>(*pc_handler_->frame()).GetFrame(); |
| DocumentLoadTiming& time_converter = |
| local_frame->Loader().GetDocumentLoader()->GetTiming(); |
| // Used for string comparisons with const char* below. |
| const std::string kTypeMediaSource = "media-source"; |
| for (const auto& stats : *report) { |
| base::Value::Dict stats_dictionary; |
| stats_dictionary.Set("id", stats.id()); |
| stats_dictionary.Set("type", stats.type()); |
| // The timestamp unit is milliseconds but we want decimal |
| // precision so we convert ourselves. |
| base::TimeDelta monotonic_time = |
| time_converter.MonotonicTimeToPseudoWallTime( |
| ConvertToBaseTimeTicks(stats.timestamp())); |
| stats_dictionary.Set( |
| "timestamp", |
| monotonic_time.InMicrosecondsF() / |
| static_cast<double>(base::Time::kMicrosecondsPerMillisecond)); |
| for (const auto& attribute : stats.Attributes()) { |
| if (!attribute.has_value()) { |
| continue; |
| } |
| stats_dictionary.Set(attribute.name(), AttributeToValue(attribute)); |
| } |
| // Modify "media-source" to also contain the result of the |
| // MediaStreamTrack Statistics API, if applicable. |
| if (stats.type() == kTypeMediaSource) { |
| const webrtc::RTCMediaSourceStats& media_source = |
| static_cast<const webrtc::RTCMediaSourceStats&>(stats); |
| if (media_source.kind.has_value() && *media_source.kind == "video" && |
| media_source.track_identifier.has_value()) { |
| auto it = tracks_by_id.find(*media_source.track_identifier); |
| if (it != tracks_by_id.end()) { |
| MediaStreamTrackPlatform::VideoFrameStats video_frame_stats = |
| it->second->GetVideoFrameStats(); |
| stats_dictionary.Set("track.deliveredFrames", |
| base::Value(static_cast<int>( |
| video_frame_stats.deliverable_frames))); |
| stats_dictionary.Set("track.discardedFrames", |
| base::Value(static_cast<int>( |
| video_frame_stats.discarded_frames))); |
| stats_dictionary.Set("track.totalFrames", |
| base::Value(static_cast<int>( |
| video_frame_stats.deliverable_frames + |
| video_frame_stats.discarded_frames + |
| video_frame_stats.dropped_frames))); |
| } |
| } |
| } |
| base::Value::List list; |
| list.Append(stats.id()); |
| list.Append(std::move(stats_dictionary)); |
| result_list.Append(std::move(list)); |
| } |
| return result_list; |
| } |
| |
| base::Value AttributeToValue(const webrtc::Attribute& attribute) { |
| // Types supported by `base::Value` are passed as the appropriate type. |
| if (attribute.holds_alternative<bool>()) { |
| return base::Value(attribute.get<bool>()); |
| } |
| if (attribute.holds_alternative<int>()) { |
| return base::Value(attribute.get<int>()); |
| } |
| if (attribute.holds_alternative<int32_t>()) { |
| return base::Value(attribute.get<int32_t>()); |
| } |
| if (attribute.holds_alternative<uint32_t>()) { |
| uint32_t value = attribute.get<uint32_t>(); |
| return base::Value(static_cast<double>(value)); |
| } |
| if (attribute.holds_alternative<int64_t>()) { |
| int64_t value = attribute.get<int64_t>(); |
| return base::Value(static_cast<double>(value)); |
| } |
| if (attribute.holds_alternative<uint64_t>()) { |
| uint64_t value = attribute.get<uint64_t>(); |
| return base::Value(static_cast<double>(value)); |
| } |
| if (attribute.holds_alternative<double>()) { |
| return base::Value(attribute.get<double>()); |
| } |
| if (attribute.holds_alternative<std::string>()) { |
| return base::Value(attribute.get<std::string>()); |
| } |
| if (attribute.holds_alternative<std::map<std::string, double>>()) { |
| base::Value::Dict dict; |
| for (auto& value : attribute.get<std::map<std::string, double>>()) { |
| dict.Set(value.first, value.second); |
| } |
| return base::Value(std::move(dict)); |
| } |
| DCHECK(false) << "Unimplemented native stats type."; |
| return base::Value(attribute.ToString()); |
| } |
| |
| const base::WeakPtr<RTCPeerConnectionHandler> pc_handler_; |
| const int lid_; |
| const scoped_refptr<base::SingleThreadTaskRunner> main_thread_; |
| const Vector<std::unique_ptr<blink::RTCRtpSenderPlatform>> senders_; |
| CrossThreadOnceFunction<void(int, base::Value::List)> completion_callback_; |
| }; |
| |
| // static |
| const char PeerConnectionTracker::kSupplementName[] = "PeerConnectionTracker"; |
| |
| PeerConnectionTracker& PeerConnectionTracker::From(LocalDOMWindow& window) { |
| PeerConnectionTracker* tracker = |
| Supplement<LocalDOMWindow>::From<PeerConnectionTracker>(window); |
| if (!tracker) { |
| tracker = MakeGarbageCollected<PeerConnectionTracker>( |
| window, window.GetTaskRunner(TaskType::kNetworking), |
| base::PassKey<PeerConnectionTracker>()); |
| ProvideTo(window, tracker); |
| } |
| return *tracker; |
| } |
| |
| PeerConnectionTracker* PeerConnectionTracker::From(LocalFrame& frame) { |
| auto* window = frame.DomWindow(); |
| return window ? &From(*window) : nullptr; |
| } |
| |
| PeerConnectionTracker* PeerConnectionTracker::From(WebLocalFrame& frame) { |
| auto* local_frame = To<WebLocalFrameImpl>(frame).GetFrame(); |
| return local_frame ? From(*local_frame) : nullptr; |
| } |
| |
| void PeerConnectionTracker::BindToFrame( |
| LocalFrame* frame, |
| mojo::PendingReceiver<blink::mojom::blink::PeerConnectionManager> |
| receiver) { |
| if (!frame) |
| return; |
| |
| if (auto* tracker = From(*frame)) |
| tracker->Bind(std::move(receiver)); |
| } |
| |
| PeerConnectionTracker::PeerConnectionTracker( |
| LocalDOMWindow& window, |
| scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, |
| base::PassKey<PeerConnectionTracker>) |
| : Supplement<LocalDOMWindow>(window), |
| // Do not set a lifecycle notifier for `peer_connection_tracker_host_` to |
| // ensure that its mojo pipe stays alive until the execution context is |
| // destroyed. `RTCPeerConnection`, which owns a `RTCPeerConnectionHandler` |
| // which keeps `this` alive, will to close and unregister the peer |
| // connection when the execution context is destroyed. For this to happen, |
| // the mojo pipe _must_ be alive to relay. See https://crbug.com/1426377 |
| // for details. |
| peer_connection_tracker_host_(nullptr), |
| receiver_(this, &window), |
| main_thread_task_runner_(std::move(main_thread_task_runner)) { |
| window.GetBrowserInterfaceBroker().GetInterface( |
| peer_connection_tracker_host_.BindNewPipeAndPassReceiver( |
| main_thread_task_runner_)); |
| } |
| |
| // Constructor used for testing. Note that receiver_ doesn't have a context |
| // notifier in this case. |
| PeerConnectionTracker::PeerConnectionTracker( |
| mojo::PendingRemote<blink::mojom::blink::PeerConnectionTrackerHost> host, |
| scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) |
| : Supplement(nullptr), |
| peer_connection_tracker_host_(nullptr), |
| receiver_(this, nullptr), |
| main_thread_task_runner_(std::move(main_thread_task_runner)) { |
| peer_connection_tracker_host_.Bind(std::move(host), main_thread_task_runner_); |
| } |
| |
| PeerConnectionTracker::~PeerConnectionTracker() {} |
| |
| void PeerConnectionTracker::Bind( |
| mojo::PendingReceiver<blink::mojom::blink::PeerConnectionManager> |
| receiver) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| receiver_.Bind(std::move(receiver), GetSupplementable()->GetTaskRunner( |
| TaskType::kMiscPlatformAPI)); |
| } |
| |
| 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::StartDataChannelLog(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->StartDataChannelLog(); |
| return; |
| } |
| } |
| } |
| |
| void PeerConnectionTracker::StopDataChannelLog(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->StopDataChannelLog(); |
| return; |
| } |
| } |
| } |
| |
| void PeerConnectionTracker::GetStandardStats() { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| for (const auto& pair : peer_connection_local_id_map_) { |
| Vector<std::unique_ptr<blink::RTCRtpSenderPlatform>> senders = |
| pair.key->GetPlatformSenders(); |
| webrtc::scoped_refptr<InternalStandardStatsObserver> observer( |
| new webrtc::RefCountedObject<InternalStandardStatsObserver>( |
| pair.key->GetWeakPtr(), pair.value, main_thread_task_runner_, |
| std::move(senders), |
| CrossThreadBindOnce(&PeerConnectionTracker::AddStandardStats, |
| WrapCrossThreadWeakPersistent(this)))); |
| pair.key->GetStandardStatsForTracker(observer); |
| } |
| } |
| |
| void PeerConnectionTracker::GetCurrentState() { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| for (const auto& pair : peer_connection_local_id_map_) { |
| pair.key->EmitCurrentStateForTracker(); |
| } |
| } |
| |
| void PeerConnectionTracker::RegisterPeerConnection( |
| RTCPeerConnectionHandler* pc_handler, |
| const webrtc::PeerConnectionInterface::RTCConfiguration& config, |
| 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, pc_handler->encoded_insertable_streams()); |
| |
| 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 initialization |
| // 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", SerializeOfferOptions(options)); |
| } |
| |
| void PeerConnectionTracker::TrackCreateAnswer( |
| RTCPeerConnectionHandler* pc_handler, |
| RTCAnswerOptionsPlatform* options) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| int id = GetLocalIDForHandler(pc_handler); |
| if (id == -1) |
| return; |
| SendPeerConnectionUpdate(id, "createAnswer", SerializeAnswerOptions(options)); |
| } |
| |
| 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; |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("type", type); |
| if (!sdp.empty()) { |
| json->SetString("sdp", sdp); |
| } |
| StringBuilder value; |
| json->WriteJSON(&value); |
| SendPeerConnectionUpdate( |
| id, |
| source == kSourceLocal ? "setLocalDescription" : "setRemoteDescription", |
| value.ToString()); |
| } |
| |
| void PeerConnectionTracker::TrackSetSessionDescriptionImplicit( |
| RTCPeerConnectionHandler* pc_handler) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| int id = GetLocalIDForHandler(pc_handler); |
| if (id == -1) |
| return; |
| SendPeerConnectionUpdate(id, "setLocalDescription", ""); |
| } |
| |
| 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, pc_handler->encoded_insertable_streams())); |
| } |
| |
| 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 relay_protocol = candidate->RelayProtocol(); |
| String url = candidate->Url(); |
| |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("sdpMid", candidate->SdpMid()); |
| if (candidate->SdpMLineIndex()) { |
| json->SetInteger("sdpMLineIndex", *candidate->SdpMLineIndex()); |
| } |
| json->SetString("candidate", candidate->Candidate()); |
| if (!url.empty()) { |
| json->SetString("url", url); |
| } |
| if (!relay_protocol.empty()) { |
| json->SetString("relayProtocol", relay_protocol); |
| } |
| |
| // OnIceCandidate always succeeds as it's a callback from the browser. |
| DCHECK(source != kSourceLocal || succeeded); |
| |
| StringBuilder value; |
| json->WriteJSON(&value); |
| const char* event = |
| (source == kSourceLocal) |
| ? "onicecandidate" |
| : (succeeded ? "addIceCandidate" : "addIceCandidateFailed"); |
| |
| SendPeerConnectionUpdate(id, event, value.ToString()); |
| } |
| |
| void PeerConnectionTracker::TrackIceCandidateError( |
| RTCPeerConnectionHandler* pc_handler, |
| const String& address, |
| std::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; |
| |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("url", url); |
| if (address) { |
| json->SetString("address", address); |
| } |
| if (port.has_value()) { |
| json->SetInteger("port", *port); |
| } |
| json->SetString("host_candidate", host_candidate); |
| json->SetString("error_text", error_text); |
| json->SetInteger("error_code", error_code); |
| StringBuilder value; |
| json->WriteJSON(&value); |
| SendPeerConnectionUpdate(id, "onicecandidateerror", value.ToString()); |
| } |
| |
| 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::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 = |
| StrCat({"transceiver", String::FromUTF8(callback_type_ending)}); |
| std::unique_ptr<JSONObject> json = SerializeTransceiver(transceiver); |
| json->SetString("reason", GetTransceiverUpdatedReasonString(reason)); |
| json->SetInteger("transceiverIndex", transceiver_index); |
| |
| StringBuilder value; |
| json->WriteJSON(&value); |
| SendPeerConnectionUpdate(id, callback_type, value.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; |
| // See https://w3c.github.io/webrtc-pc/#dom-rtcdatachannelinit |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("label", String::FromUTF8(data_channel->label())); |
| json->SetBoolean("ordered", data_channel->ordered()); |
| std::optional<uint16_t> maxPacketLifeTime = data_channel->maxPacketLifeTime(); |
| if (maxPacketLifeTime.has_value()) { |
| json->SetInteger("maxPacketLifeTime", *maxPacketLifeTime); |
| } |
| std::optional<uint16_t> maxRetransmits = data_channel->maxRetransmitsOpt(); |
| if (maxRetransmits.has_value()) { |
| json->SetInteger("maxRetransmits", *maxRetransmits); |
| } |
| if (!data_channel->protocol().empty()) { |
| json->SetString("protocol", String::FromUTF8(data_channel->protocol())); |
| } |
| bool negotiated = data_channel->negotiated(); |
| if (negotiated) { |
| json->SetBoolean("negotiated", true); |
| json->SetInteger("id", data_channel->id()); |
| } |
| // TODO(crbug.com/1455847): add priority |
| // https://w3c.github.io/webrtc-priority/#new-rtcdatachannelinit-member |
| StringBuilder value; |
| json->WriteJSON(&value); |
| SendPeerConnectionUpdate( |
| id, source == kSourceLocal ? "createDataChannel" : "ondatachannel", |
| value.ToString()); |
| } |
| |
| void PeerConnectionTracker::TrackClose(RTCPeerConnectionHandler* pc_handler) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| int id = GetLocalIDForHandler(pc_handler); |
| if (id == -1) |
| return; |
| SendPeerConnectionUpdate(id, "close", g_empty_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, "onsignalingstatechange", |
| StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(), |
| "\""})); |
| } |
| |
| 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, "oniceconnectionstatechange", |
| StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(), |
| "\""})); |
| } |
| |
| 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, "onconnectionstatechange", |
| StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(), |
| "\""})); |
| } |
| |
| 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, "onicegatheringstatechange", |
| StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(), |
| "\""})); |
| } |
| |
| 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 kActionSetLocalDescription: |
| update_type = "setLocalDescription"; |
| break; |
| case kActionSetLocalDescriptionImplicit: |
| update_type = "setLocalDescription"; |
| break; |
| case kActionSetRemoteDescription: |
| update_type = "setRemoteDescription"; |
| break; |
| case kActionCreateOffer: |
| update_type = "createOffer"; |
| break; |
| case kActionCreateAnswer: |
| update_type = "createAnswer"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| update_type = StrCat({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.empty()); |
| const int local_id = GetLocalIDForHandler(pc_handler); |
| if (local_id == -1) { |
| return; |
| } |
| |
| String non_null_session_id = |
| session_id.IsNull() ? 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, "onnegotiationneeded", g_empty_string); |
| } |
| |
| void PeerConnectionTracker::TrackGetUserMedia( |
| UserMediaRequest* user_media_request) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| peer_connection_tracker_host_->GetUserMedia( |
| user_media_request->request_id(), user_media_request->Audio(), |
| user_media_request->Video(), |
| SerializeGetUserMediaMediaConstraints( |
| user_media_request->AudioConstraints()), |
| SerializeGetUserMediaMediaConstraints( |
| user_media_request->VideoConstraints())); |
| } |
| |
| void PeerConnectionTracker::TrackGetUserMediaSuccess( |
| UserMediaRequest* user_media_request, |
| const MediaStream* stream) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| // Serialize audio and video track information (id and label) or "null" |
| // when there is no such track. |
| String audio_track_info; |
| if (!stream->getAudioTracks().empty()) { |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("id", stream->getAudioTracks()[0]->id()); |
| json->SetString("label", stream->getAudioTracks()[0]->label()); |
| StringBuilder value; |
| json->WriteJSON(&value); |
| audio_track_info = value.ToString(); |
| } else { |
| audio_track_info = "null"; |
| } |
| String video_track_info; |
| if (!stream->getVideoTracks().empty()) { |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("id", stream->getVideoTracks()[0]->id()); |
| json->SetString("label", stream->getVideoTracks()[0]->label()); |
| StringBuilder value; |
| json->WriteJSON(&value); |
| video_track_info = value.ToString(); |
| } else { |
| video_track_info = "null"; |
| } |
| peer_connection_tracker_host_->GetUserMediaSuccess( |
| user_media_request->request_id(), stream->id(), audio_track_info, |
| video_track_info); |
| } |
| |
| void PeerConnectionTracker::TrackGetUserMediaFailure( |
| UserMediaRequest* user_media_request, |
| const String& error, |
| const String& error_message) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| peer_connection_tracker_host_->GetUserMediaFailure( |
| user_media_request->request_id(), error, error_message); |
| } |
| |
| void PeerConnectionTracker::TrackGetDisplayMedia( |
| UserMediaRequest* user_media_request) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| peer_connection_tracker_host_->GetDisplayMedia( |
| user_media_request->request_id(), user_media_request->Audio(), |
| user_media_request->Video(), |
| SerializeGetUserMediaMediaConstraints( |
| user_media_request->AudioConstraints()), |
| SerializeGetUserMediaMediaConstraints( |
| user_media_request->VideoConstraints())); |
| } |
| |
| void PeerConnectionTracker::TrackGetDisplayMediaSuccess( |
| UserMediaRequest* user_media_request, |
| MediaStream* stream) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| // Serialize audio and video track information (id and label) or "null" |
| // when there is no such track. |
| String audio_track_info; |
| if (!stream->getAudioTracks().empty()) { |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("id", stream->getAudioTracks()[0]->id()); |
| json->SetString("label", stream->getAudioTracks()[0]->label()); |
| StringBuilder value; |
| json->WriteJSON(&value); |
| audio_track_info = value.ToString(); |
| } else { |
| audio_track_info = "null"; |
| } |
| String video_track_info; |
| if (!stream->getVideoTracks().empty()) { |
| auto json = std::make_unique<JSONObject>(); |
| json->SetString("id", stream->getVideoTracks()[0]->id()); |
| json->SetString("label", stream->getVideoTracks()[0]->label()); |
| StringBuilder value; |
| json->WriteJSON(&value); |
| video_track_info = value.ToString(); |
| } else { |
| video_track_info = "null"; |
| } |
| peer_connection_tracker_host_->GetDisplayMediaSuccess( |
| user_media_request->request_id(), stream->id(), audio_track_info, |
| video_track_info); |
| } |
| |
| void PeerConnectionTracker::TrackGetDisplayMediaFailure( |
| UserMediaRequest* user_media_request, |
| const String& error, |
| const String& error_message) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| |
| peer_connection_tracker_host_->GetDisplayMediaFailure( |
| user_media_request->request_id(), error, error_message); |
| } |
| |
| void PeerConnectionTracker::TrackRtcEventLogWrite( |
| RTCPeerConnectionHandler* pc_handler, |
| const 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); |
| } |
| |
| void PeerConnectionTracker::TrackRtcDataChannelLogWrite( |
| RTCPeerConnectionHandler* pc_handler, |
| const Vector<uint8_t>& output) { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| int id = GetLocalIDForHandler(pc_handler); |
| if (id == -1) { |
| return; |
| } |
| |
| peer_connection_tracker_host_->WebRtcDataChannelLogWrite(id, output); |
| } |
| |
| int PeerConnectionTracker::GetNextLocalID() { |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_); |
| return GetNextProcessLocalID(); |
| } |
| |
| 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::List value) { |
| peer_connection_tracker_host_->AddStandardStats(lid, std::move(value)); |
| } |
| |
| } // namespace blink |