blob: 753706b0dffdf65d2de9755d0ab646521c888382 [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 "content/renderer/media/peer_connection_tracker.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "content/common/media/peer_connection_tracker_messages.h"
#include "content/renderer/media/rtc_media_constraints.h"
#include "content/renderer/media/rtc_peer_connection_handler.h"
#include "content/renderer/render_thread_impl.h"
#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
#include "third_party/WebKit/public/platform/WebMediaStream.h"
#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
#include "third_party/WebKit/public/platform/WebRTCICECandidate.h"
#include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebUserMediaRequest.h"
using std::string;
using webrtc::MediaConstraintsInterface;
using webrtc::StatsReport;
using webrtc::StatsReports;
using blink::WebRTCPeerConnectionHandlerClient;
namespace content {
static string SerializeServers(
const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) {
string result = "[";
for (size_t i = 0; i < servers.size(); ++i) {
result += servers[i].uri;
if (i != servers.size() - 1)
result += ", ";
}
result += "]";
return result;
}
static RTCMediaConstraints GetNativeMediaConstraints(
const blink::WebMediaConstraints& constraints) {
RTCMediaConstraints native_constraints;
if (constraints.isNull())
return native_constraints;
blink::WebVector<blink::WebMediaConstraint> mandatory;
constraints.getMandatoryConstraints(mandatory);
for (size_t i = 0; i < mandatory.size(); ++i) {
native_constraints.AddMandatory(
mandatory[i].m_name.utf8(), mandatory[i].m_value.utf8(), false);
}
blink::WebVector<blink::WebMediaConstraint> optional;
constraints.getOptionalConstraints(optional);
for (size_t i = 0; i < optional.size(); ++i) {
native_constraints.AddOptional(
optional[i].m_name.utf8(), optional[i].m_value.utf8(), false);
}
return native_constraints;
}
static string SerializeMediaConstraints(
const RTCMediaConstraints& constraints) {
string result;
MediaConstraintsInterface::Constraints mandatory = constraints.GetMandatory();
if (!mandatory.empty()) {
result += "mandatory: {";
for (size_t i = 0; i < mandatory.size(); ++i) {
result += mandatory[i].key + ":" + mandatory[i].value;
if (i != mandatory.size() - 1)
result += ", ";
}
result += "}";
}
MediaConstraintsInterface::Constraints optional = constraints.GetOptional();
if (!optional.empty()) {
if (!result.empty())
result += ", ";
result += "optional: {";
for (size_t i = 0; i < optional.size(); ++i) {
result += optional[i].key + ":" + optional[i].value;
if (i != optional.size() - 1)
result += ", ";
}
result += "}";
}
return result;
}
static string SerializeMediaStreamComponent(
const blink::WebMediaStreamTrack component) {
string id = base::UTF16ToUTF8(base::StringPiece16(component.source().id()));
return id;
}
static string SerializeMediaDescriptor(
const blink::WebMediaStream& stream) {
string label = base::UTF16ToUTF8(base::StringPiece16(stream.id()));
string result = "label: " + label;
blink::WebVector<blink::WebMediaStreamTrack> tracks;
stream.audioTracks(tracks);
if (!tracks.isEmpty()) {
result += ", audio: [";
for (size_t i = 0; i < tracks.size(); ++i) {
result += SerializeMediaStreamComponent(tracks[i]);
if (i != tracks.size() - 1)
result += ", ";
}
result += "]";
}
stream.videoTracks(tracks);
if (!tracks.isEmpty()) {
result += ", video: [";
for (size_t i = 0; i < tracks.size(); ++i) {
result += SerializeMediaStreamComponent(tracks[i]);
if (i != tracks.size() - 1)
result += ", ";
}
result += "]";
}
return result;
}
static std::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;
}
static std::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;
}
static std::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;
}
#define GET_STRING_OF_STATE(state) \
case WebRTCPeerConnectionHandlerClient::state: \
result = #state; \
break;
static string GetSignalingStateString(
WebRTCPeerConnectionHandlerClient::SignalingState state) {
string result;
switch (state) {
GET_STRING_OF_STATE(SignalingStateStable)
GET_STRING_OF_STATE(SignalingStateHaveLocalOffer)
GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer)
GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer)
GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer)
GET_STRING_OF_STATE(SignalingStateClosed)
default:
NOTREACHED();
break;
}
return result;
}
static string GetIceConnectionStateString(
WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
string result;
switch (state) {
GET_STRING_OF_STATE(ICEConnectionStateStarting)
GET_STRING_OF_STATE(ICEConnectionStateChecking)
GET_STRING_OF_STATE(ICEConnectionStateConnected)
GET_STRING_OF_STATE(ICEConnectionStateCompleted)
GET_STRING_OF_STATE(ICEConnectionStateFailed)
GET_STRING_OF_STATE(ICEConnectionStateDisconnected)
GET_STRING_OF_STATE(ICEConnectionStateClosed)
default:
NOTREACHED();
break;
}
return result;
}
static string GetIceGatheringStateString(
WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
string result;
switch (state) {
GET_STRING_OF_STATE(ICEGatheringStateNew)
GET_STRING_OF_STATE(ICEGatheringStateGathering)
GET_STRING_OF_STATE(ICEGatheringStateComplete)
default:
NOTREACHED();
break;
}
return result;
}
// Builds a DictionaryValue from the StatsReport.
// The caller takes the ownership of the returned value.
// 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.
static base::DictionaryValue* GetDictValueStats(const StatsReport& report) {
if (report.values().empty())
return NULL;
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetDouble("timestamp", report.timestamp());
base::ListValue* values = new base::ListValue();
dict->Set("values", values);
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 isn't supported, so use string.
case StatsReport::Value::kId:
default:
values->AppendString(value->ToString());
break;
}
}
return dict;
}
// Builds a DictionaryValue from the StatsReport.
// The caller takes the ownership of the returned value.
static base::DictionaryValue* GetDictValue(const StatsReport& report) {
scoped_ptr<base::DictionaryValue> stats, result;
stats.reset(GetDictValueStats(report));
if (!stats)
return NULL;
result.reset(new base::DictionaryValue());
// 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.
result->Set("stats", stats.release());
result->SetString("id", report.id()->ToString());
result->SetString("type", report.TypeToString());
return result.release();
}
class InternalStatsObserver : public webrtc::StatsObserver {
public:
InternalStatsObserver(int lid)
: lid_(lid), main_thread_(base::ThreadTaskRunnerHandle::Get()) {}
void OnComplete(const StatsReports& reports) override {
scoped_ptr<base::ListValue> list(new base::ListValue());
for (const auto* r : reports) {
base::DictionaryValue* report = GetDictValue(*r);
if (report)
list->Append(report);
}
if (!list->empty()) {
main_thread_->PostTask(FROM_HERE,
base::Bind(&InternalStatsObserver::OnCompleteImpl,
base::Passed(&list), lid_));
}
}
protected:
~InternalStatsObserver() 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(scoped_ptr<base::ListValue> list, int lid) {
DCHECK(!list->empty());
RenderThreadImpl::current()->Send(
new PeerConnectionTrackerHost_AddStats(lid, *list.get()));
}
const int lid_;
const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
};
PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
}
PeerConnectionTracker::~PeerConnectionTracker() {
}
bool PeerConnectionTracker::OnControlMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message)
IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats, OnGetAllStats)
IPC_MESSAGE_HANDLER(PeerConnectionTracker_OnSuspend, OnSuspend)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void PeerConnectionTracker::OnGetAllStats() {
DCHECK(main_thread_.CalledOnValidThread());
const std::string empty_track_id;
for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
it != peer_connection_id_map_.end(); ++it) {
rtc::scoped_refptr<InternalStatsObserver> observer(
new rtc::RefCountedObject<InternalStatsObserver>(it->second));
// The last type parameter is ignored when the track id is empty.
it->first->GetStats(
observer,
webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
empty_track_id, blink::WebMediaStreamSource::TypeAudio);
}
}
void PeerConnectionTracker::OnSuspend() {
DCHECK(main_thread_.CalledOnValidThread());
for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
it != peer_connection_id_map_.end(); ++it) {
it->first->CloseClientPeerConnection();
}
}
void PeerConnectionTracker::RegisterPeerConnection(
RTCPeerConnectionHandler* pc_handler,
const webrtc::PeerConnectionInterface::RTCConfiguration& config,
const RTCMediaConstraints& constraints,
const blink::WebFrame* frame) {
DCHECK(main_thread_.CalledOnValidThread());
DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
PeerConnectionInfo info;
info.lid = GetNextLocalID();
info.rtc_configuration =
"{ servers: " + SerializeServers(config.servers) + ", " +
"iceTransportType: " + SerializeIceTransportType(config.type) + ", " +
"bundlePolicy: " + SerializeBundlePolicy(config.bundle_policy) + ", " +
"rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config.rtcp_mux_policy) + " }";
info.constraints = SerializeMediaConstraints(constraints);
info.url = frame->document().url().spec();
RenderThreadImpl::current()->Send(
new PeerConnectionTrackerHost_AddPeerConnection(info));
DCHECK(peer_connection_id_map_.find(pc_handler) ==
peer_connection_id_map_.end());
peer_connection_id_map_[pc_handler] = info.lid;
}
void PeerConnectionTracker::UnregisterPeerConnection(
RTCPeerConnectionHandler* pc_handler) {
DCHECK(main_thread_.CalledOnValidThread());
DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
std::map<RTCPeerConnectionHandler*, int>::iterator it =
peer_connection_id_map_.find(pc_handler);
if (it == peer_connection_id_map_.end()) {
// The PeerConnection might not have been registered if its initilization
// failed.
return;
}
RenderThreadImpl::current()->Send(
new PeerConnectionTrackerHost_RemovePeerConnection(it->second));
peer_connection_id_map_.erase(it);
}
void PeerConnectionTracker::TrackCreateOffer(
RTCPeerConnectionHandler* pc_handler,
const RTCMediaConstraints& constraints) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, "createOffer",
"constraints: {" + SerializeMediaConstraints(constraints) + "}");
}
void PeerConnectionTracker::TrackCreateAnswer(
RTCPeerConnectionHandler* pc_handler,
const RTCMediaConstraints& constraints) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, "createAnswer",
"constraints: {" + SerializeMediaConstraints(constraints) + "}");
}
void PeerConnectionTracker::TrackSetSessionDescription(
RTCPeerConnectionHandler* pc_handler,
const std::string& sdp, const std::string& type, Source source) {
DCHECK(main_thread_.CalledOnValidThread());
string value = "type: " + type + ", sdp: " + sdp;
SendPeerConnectionUpdate(
pc_handler,
source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
value);
}
void PeerConnectionTracker::TrackUpdateIce(
RTCPeerConnectionHandler* pc_handler,
const webrtc::PeerConnectionInterface::RTCConfiguration& config,
const RTCMediaConstraints& options) {
DCHECK(main_thread_.CalledOnValidThread());
string servers_string = "servers: " + SerializeServers(config.servers);
string transport_type =
"iceTransportType: " + SerializeIceTransportType(config.type);
string bundle_policy =
"bundlePolicy: " + SerializeBundlePolicy(config.bundle_policy);
string rtcp_mux_policy =
"rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config.rtcp_mux_policy);
string constraints =
"constraints: {" + SerializeMediaConstraints(options) + "}";
SendPeerConnectionUpdate(
pc_handler,
"updateIce",
servers_string + ", " + transport_type + ", " +
bundle_policy + ", " + rtcp_mux_policy + ", " +
constraints);
}
void PeerConnectionTracker::TrackAddIceCandidate(
RTCPeerConnectionHandler* pc_handler,
const blink::WebRTCICECandidate& candidate,
Source source,
bool succeeded) {
DCHECK(main_thread_.CalledOnValidThread());
string value =
"sdpMid: " +
base::UTF16ToUTF8(base::StringPiece16(candidate.sdpMid())) + ", " +
"sdpMLineIndex: " + base::IntToString(candidate.sdpMLineIndex()) +
", " + "candidate: " +
base::UTF16ToUTF8(base::StringPiece16(candidate.candidate()));
// OnIceCandidate always succeeds as it's a callback from the browser.
DCHECK(source != SOURCE_LOCAL || succeeded);
string event =
(source == SOURCE_LOCAL) ? "onIceCandidate"
: (succeeded ? "addIceCandidate"
: "addIceCandidateFailed");
SendPeerConnectionUpdate(pc_handler, event, value);
}
void PeerConnectionTracker::TrackAddStream(
RTCPeerConnectionHandler* pc_handler,
const blink::WebMediaStream& stream,
Source source) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream",
SerializeMediaDescriptor(stream));
}
void PeerConnectionTracker::TrackRemoveStream(
RTCPeerConnectionHandler* pc_handler,
const blink::WebMediaStream& stream,
Source source){
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream",
SerializeMediaDescriptor(stream));
}
void PeerConnectionTracker::TrackCreateDataChannel(
RTCPeerConnectionHandler* pc_handler,
const webrtc::DataChannelInterface* data_channel,
Source source) {
DCHECK(main_thread_.CalledOnValidThread());
string value = "label: " + data_channel->label() +
", reliable: " + (data_channel->reliable() ? "true" : "false");
SendPeerConnectionUpdate(
pc_handler,
source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel",
value);
}
void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(pc_handler, "stop", std::string());
}
void PeerConnectionTracker::TrackSignalingStateChange(
RTCPeerConnectionHandler* pc_handler,
WebRTCPeerConnectionHandlerClient::SignalingState state) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, "signalingStateChange", GetSignalingStateString(state));
}
void PeerConnectionTracker::TrackIceConnectionStateChange(
RTCPeerConnectionHandler* pc_handler,
WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, "iceConnectionStateChange",
GetIceConnectionStateString(state));
}
void PeerConnectionTracker::TrackIceGatheringStateChange(
RTCPeerConnectionHandler* pc_handler,
WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(
pc_handler, "iceGatheringStateChange",
GetIceGatheringStateString(state));
}
void PeerConnectionTracker::TrackSessionDescriptionCallback(
RTCPeerConnectionHandler* pc_handler, Action action,
const string& callback_type, const string& value) {
DCHECK(main_thread_.CalledOnValidThread());
string update_type;
switch (action) {
case ACTION_SET_LOCAL_DESCRIPTION:
update_type = "setLocalDescription";
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 += callback_type;
SendPeerConnectionUpdate(pc_handler, update_type, value);
}
void PeerConnectionTracker::TrackOnRenegotiationNeeded(
RTCPeerConnectionHandler* pc_handler) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string());
}
void PeerConnectionTracker::TrackCreateDTMFSender(
RTCPeerConnectionHandler* pc_handler,
const blink::WebMediaStreamTrack& track) {
DCHECK(main_thread_.CalledOnValidThread());
SendPeerConnectionUpdate(pc_handler, "createDTMFSender",
base::UTF16ToUTF8(base::StringPiece16(track.id())));
}
void PeerConnectionTracker::TrackGetUserMedia(
const blink::WebUserMediaRequest& user_media_request) {
DCHECK(main_thread_.CalledOnValidThread());
RTCMediaConstraints audio_constraints(
GetNativeMediaConstraints(user_media_request.audioConstraints()));
RTCMediaConstraints video_constraints(
GetNativeMediaConstraints(user_media_request.videoConstraints()));
RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia(
user_media_request.securityOrigin().toString().utf8(),
user_media_request.audio(),
user_media_request.video(),
SerializeMediaConstraints(audio_constraints),
SerializeMediaConstraints(video_constraints)));
}
int PeerConnectionTracker::GetNextLocalID() {
DCHECK(main_thread_.CalledOnValidThread());
return next_lid_++;
}
void PeerConnectionTracker::SendPeerConnectionUpdate(
RTCPeerConnectionHandler* pc_handler,
const std::string& type,
const std::string& value) {
DCHECK(main_thread_.CalledOnValidThread());
if (peer_connection_id_map_.find(pc_handler) == peer_connection_id_map_.end())
return;
RenderThreadImpl::current()->Send(
new PeerConnectionTrackerHost_UpdatePeerConnection(
peer_connection_id_map_[pc_handler], type, value));
}
} // namespace content