1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#include "content/renderer/media/peer_connection_tracker.h"
5
6#include "base/strings/utf_string_conversions.h"
7#include "content/common/media/peer_connection_tracker_messages.h"
8#include "content/renderer/media/rtc_media_constraints.h"
9#include "content/renderer/media/rtc_peer_connection_handler.h"
10#include "content/renderer/render_thread_impl.h"
11#include "third_party/WebKit/public/platform/WebMediaStream.h"
12#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
13#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
14#include "third_party/WebKit/public/platform/WebRTCICECandidate.h"
15#include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h"
16#include "third_party/WebKit/public/web/WebDocument.h"
17#include "third_party/WebKit/public/web/WebFrame.h"
18
19using std::string;
20using webrtc::MediaConstraintsInterface;
21using blink::WebRTCPeerConnectionHandlerClient;
22
23namespace content {
24
25static string SerializeServers(
26    const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) {
27  string result = "[";
28  for (size_t i = 0; i < servers.size(); ++i) {
29    result += servers[i].uri;
30    if (i != servers.size() - 1)
31      result += ", ";
32  }
33  result += "]";
34  return result;
35}
36
37static string SerializeMediaConstraints(
38    const RTCMediaConstraints& constraints) {
39  string result;
40  MediaConstraintsInterface::Constraints mandatory = constraints.GetMandatory();
41  if (!mandatory.empty()) {
42    result += "mandatory: {";
43    for (size_t i = 0; i < mandatory.size(); ++i) {
44      result += mandatory[i].key + ":" + mandatory[i].value;
45      if (i != mandatory.size() - 1)
46        result += ", ";
47    }
48    result += "}";
49  }
50  MediaConstraintsInterface::Constraints optional = constraints.GetOptional();
51  if (!optional.empty()) {
52    if (!result.empty())
53      result += ", ";
54    result += "optional: {";
55    for (size_t i = 0; i < optional.size(); ++i) {
56      result += optional[i].key + ":" + optional[i].value;
57      if (i != optional.size() - 1)
58        result += ", ";
59    }
60    result += "}";
61  }
62  return result;
63}
64
65static string SerializeMediaStreamComponent(
66    const blink::WebMediaStreamTrack component) {
67  string id = UTF16ToUTF8(component.source().id());
68  return id;
69}
70
71static string SerializeMediaDescriptor(
72    const blink::WebMediaStream& stream) {
73  string label = UTF16ToUTF8(stream.id());
74  string result = "label: " + label;
75  blink::WebVector<blink::WebMediaStreamTrack> tracks;
76  stream.audioTracks(tracks);
77  if (!tracks.isEmpty()) {
78    result += ", audio: [";
79    for (size_t i = 0; i < tracks.size(); ++i) {
80      result += SerializeMediaStreamComponent(tracks[i]);
81      if (i != tracks.size() - 1)
82        result += ", ";
83    }
84    result += "]";
85  }
86  stream.videoTracks(tracks);
87  if (!tracks.isEmpty()) {
88    result += ", video: [";
89    for (size_t i = 0; i < tracks.size(); ++i) {
90      result += SerializeMediaStreamComponent(tracks[i]);
91      if (i != tracks.size() - 1)
92        result += ", ";
93    }
94    result += "]";
95  }
96  return result;
97}
98
99#define GET_STRING_OF_STATE(state)                \
100  case WebRTCPeerConnectionHandlerClient::state:  \
101    result = #state;                              \
102    break;
103
104static string GetSignalingStateString(
105    WebRTCPeerConnectionHandlerClient::SignalingState state) {
106  string result;
107  switch (state) {
108    GET_STRING_OF_STATE(SignalingStateStable)
109    GET_STRING_OF_STATE(SignalingStateHaveLocalOffer)
110    GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer)
111    GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer)
112    GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer)
113    GET_STRING_OF_STATE(SignalingStateClosed)
114    default:
115      NOTREACHED();
116      break;
117  }
118  return result;
119}
120
121static string GetIceConnectionStateString(
122    WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
123  string result;
124  switch (state) {
125    GET_STRING_OF_STATE(ICEConnectionStateStarting)
126    GET_STRING_OF_STATE(ICEConnectionStateChecking)
127    GET_STRING_OF_STATE(ICEConnectionStateConnected)
128    GET_STRING_OF_STATE(ICEConnectionStateCompleted)
129    GET_STRING_OF_STATE(ICEConnectionStateFailed)
130    GET_STRING_OF_STATE(ICEConnectionStateDisconnected)
131    GET_STRING_OF_STATE(ICEConnectionStateClosed)
132    default:
133      NOTREACHED();
134      break;
135  }
136  return result;
137}
138
139static string GetIceGatheringStateString(
140    WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
141  string result;
142  switch (state) {
143    GET_STRING_OF_STATE(ICEGatheringStateNew)
144    GET_STRING_OF_STATE(ICEGatheringStateGathering)
145    GET_STRING_OF_STATE(ICEGatheringStateComplete)
146    default:
147      NOTREACHED();
148      break;
149  }
150  return result;
151}
152
153// Builds a DictionaryValue from the StatsReport.
154// The caller takes the ownership of the returned value.
155// Note:
156// The format must be consistent with what webrtc_internals.js expects.
157// If you change it here, you must change webrtc_internals.js as well.
158static base::DictionaryValue* GetDictValueStats(
159    const webrtc::StatsReport& report) {
160  if (report.values.empty())
161    return NULL;
162
163  DictionaryValue* dict = new base::DictionaryValue();
164  dict->SetDouble("timestamp", report.timestamp);
165
166  base::ListValue* values = new base::ListValue();
167  dict->Set("values", values);
168
169  for (size_t i = 0; i < report.values.size(); ++i) {
170    values->AppendString(report.values[i].name);
171    values->AppendString(report.values[i].value);
172  }
173  return dict;
174}
175
176// Builds a DictionaryValue from the StatsReport.
177// The caller takes the ownership of the returned value.
178static base::DictionaryValue* GetDictValue(const webrtc::StatsReport& report) {
179  scoped_ptr<base::DictionaryValue> stats, result;
180
181  stats.reset(GetDictValueStats(report));
182  if (!stats)
183    return NULL;
184
185  result.reset(new base::DictionaryValue());
186  // Note:
187  // The format must be consistent with what webrtc_internals.js expects.
188  // If you change it here, you must change webrtc_internals.js as well.
189  result->Set("stats", stats.release());
190  result->SetString("id", report.id);
191  result->SetString("type", report.type);
192
193  return result.release();
194}
195
196class InternalStatsObserver : public webrtc::StatsObserver {
197 public:
198  InternalStatsObserver(int lid)
199      : lid_(lid){}
200
201  virtual void OnComplete(
202      const std::vector<webrtc::StatsReport>& reports) OVERRIDE {
203    base::ListValue list;
204
205    for (size_t i = 0; i < reports.size(); ++i) {
206      base::DictionaryValue* report = GetDictValue(reports[i]);
207      if (report)
208        list.Append(report);
209    }
210
211    if (!list.empty())
212      RenderThreadImpl::current()->Send(
213          new PeerConnectionTrackerHost_AddStats(lid_, list));
214  }
215
216 protected:
217  virtual ~InternalStatsObserver() {}
218
219 private:
220  int lid_;
221};
222
223PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
224}
225
226PeerConnectionTracker::~PeerConnectionTracker() {
227}
228
229bool PeerConnectionTracker::OnControlMessageReceived(
230    const IPC::Message& message) {
231  bool handled = true;
232  IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message)
233    IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats, OnGetAllStats)
234    IPC_MESSAGE_UNHANDLED(handled = false)
235  IPC_END_MESSAGE_MAP()
236  return handled;
237}
238
239void PeerConnectionTracker::OnGetAllStats() {
240  for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
241       it != peer_connection_id_map_.end(); ++it) {
242
243    talk_base::scoped_refptr<InternalStatsObserver> observer(
244        new talk_base::RefCountedObject<InternalStatsObserver>(it->second));
245
246    it->first->GetStats(observer, NULL);
247  }
248}
249
250void PeerConnectionTracker::RegisterPeerConnection(
251    RTCPeerConnectionHandler* pc_handler,
252    const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
253    const RTCMediaConstraints& constraints,
254    const blink::WebFrame* frame) {
255  DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
256  PeerConnectionInfo info;
257
258  info.lid = GetNextLocalID();
259  info.servers = SerializeServers(servers);
260  info.constraints = SerializeMediaConstraints(constraints);
261  info.url = frame->document().url().spec();
262  RenderThreadImpl::current()->Send(
263      new PeerConnectionTrackerHost_AddPeerConnection(info));
264
265  DCHECK(peer_connection_id_map_.find(pc_handler) ==
266         peer_connection_id_map_.end());
267  peer_connection_id_map_[pc_handler] = info.lid;
268}
269
270void PeerConnectionTracker::UnregisterPeerConnection(
271    RTCPeerConnectionHandler* pc_handler) {
272  DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
273
274  std::map<RTCPeerConnectionHandler*, int>::iterator it =
275      peer_connection_id_map_.find(pc_handler);
276
277  if (it == peer_connection_id_map_.end()) {
278    // The PeerConnection might not have been registered if its initilization
279    // failed.
280    return;
281  }
282
283  RenderThreadImpl::current()->Send(
284      new PeerConnectionTrackerHost_RemovePeerConnection(it->second));
285
286  peer_connection_id_map_.erase(it);
287}
288
289void PeerConnectionTracker::TrackCreateOffer(
290    RTCPeerConnectionHandler* pc_handler,
291    const RTCMediaConstraints& constraints) {
292  SendPeerConnectionUpdate(
293      pc_handler, "createOffer",
294      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
295}
296
297void PeerConnectionTracker::TrackCreateAnswer(
298    RTCPeerConnectionHandler* pc_handler,
299    const RTCMediaConstraints& constraints) {
300  SendPeerConnectionUpdate(
301      pc_handler, "createAnswer",
302      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
303}
304
305void PeerConnectionTracker::TrackSetSessionDescription(
306    RTCPeerConnectionHandler* pc_handler,
307    const blink::WebRTCSessionDescription& desc,
308    Source source) {
309  string sdp = UTF16ToUTF8(desc.sdp());
310  string type = UTF16ToUTF8(desc.type());
311
312  string value = "type: " + type + ", sdp: " + sdp;
313  SendPeerConnectionUpdate(
314      pc_handler,
315      source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
316      value);
317}
318
319void PeerConnectionTracker::TrackUpdateIce(
320      RTCPeerConnectionHandler* pc_handler,
321      const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
322      const RTCMediaConstraints& options) {
323  string servers_string = "servers: " + SerializeServers(servers);
324  string constraints =
325      "constraints: {" + SerializeMediaConstraints(options) + "}";
326
327  SendPeerConnectionUpdate(
328      pc_handler, "updateIce", servers_string + ", " + constraints);
329}
330
331void PeerConnectionTracker::TrackAddIceCandidate(
332      RTCPeerConnectionHandler* pc_handler,
333      const blink::WebRTCICECandidate& candidate,
334      Source source) {
335  string value = "mid: " + UTF16ToUTF8(candidate.sdpMid()) + ", " +
336                 "candidate: " + UTF16ToUTF8(candidate.candidate());
337  SendPeerConnectionUpdate(
338      pc_handler,
339      source == SOURCE_LOCAL ? "onIceCandidate" : "addIceCandidate", value);
340}
341
342void PeerConnectionTracker::TrackAddStream(
343    RTCPeerConnectionHandler* pc_handler,
344    const blink::WebMediaStream& stream,
345    Source source){
346  SendPeerConnectionUpdate(
347      pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream",
348      SerializeMediaDescriptor(stream));
349}
350
351void PeerConnectionTracker::TrackRemoveStream(
352    RTCPeerConnectionHandler* pc_handler,
353    const blink::WebMediaStream& stream,
354    Source source){
355  SendPeerConnectionUpdate(
356      pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream",
357      SerializeMediaDescriptor(stream));
358}
359
360void PeerConnectionTracker::TrackCreateDataChannel(
361    RTCPeerConnectionHandler* pc_handler,
362    const webrtc::DataChannelInterface* data_channel,
363    Source source) {
364  string value = "label: " + data_channel->label() +
365                 ", reliable: " + (data_channel->reliable() ? "true" : "false");
366  SendPeerConnectionUpdate(
367      pc_handler,
368      source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel",
369      value);
370}
371
372void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) {
373  SendPeerConnectionUpdate(pc_handler, "stop", std::string());
374}
375
376void PeerConnectionTracker::TrackSignalingStateChange(
377      RTCPeerConnectionHandler* pc_handler,
378      WebRTCPeerConnectionHandlerClient::SignalingState state) {
379  SendPeerConnectionUpdate(
380      pc_handler, "signalingStateChange", GetSignalingStateString(state));
381}
382
383void PeerConnectionTracker::TrackIceConnectionStateChange(
384      RTCPeerConnectionHandler* pc_handler,
385      WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
386  SendPeerConnectionUpdate(
387      pc_handler, "iceConnectionStateChange",
388      GetIceConnectionStateString(state));
389}
390
391void PeerConnectionTracker::TrackIceGatheringStateChange(
392      RTCPeerConnectionHandler* pc_handler,
393      WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
394  SendPeerConnectionUpdate(
395      pc_handler, "iceGatheringStateChange",
396      GetIceGatheringStateString(state));
397}
398
399void PeerConnectionTracker::TrackSessionDescriptionCallback(
400    RTCPeerConnectionHandler* pc_handler, Action action,
401    const string& callback_type, const string& value) {
402  string update_type;
403  switch (action) {
404    case ACTION_SET_LOCAL_DESCRIPTION:
405      update_type = "setLocalDescription";
406      break;
407    case ACTION_SET_REMOTE_DESCRIPTION:
408      update_type = "setRemoteDescription";
409      break;
410    case ACTION_CREATE_OFFER:
411      update_type = "createOffer";
412      break;
413    case ACTION_CREATE_ANSWER:
414      update_type = "createAnswer";
415      break;
416    default:
417      NOTREACHED();
418      break;
419  }
420  update_type += callback_type;
421
422  SendPeerConnectionUpdate(pc_handler, update_type, value);
423}
424
425void PeerConnectionTracker::TrackOnRenegotiationNeeded(
426    RTCPeerConnectionHandler* pc_handler) {
427  SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string());
428}
429
430void PeerConnectionTracker::TrackCreateDTMFSender(
431    RTCPeerConnectionHandler* pc_handler,
432    const blink::WebMediaStreamTrack& track) {
433  SendPeerConnectionUpdate(pc_handler, "createDTMFSender",
434                           UTF16ToUTF8(track.id()));
435}
436
437int PeerConnectionTracker::GetNextLocalID() {
438  return next_lid_++;
439}
440
441void PeerConnectionTracker::SendPeerConnectionUpdate(
442    RTCPeerConnectionHandler* pc_handler,
443    const std::string& type,
444    const std::string& value) {
445  if (peer_connection_id_map_.find(pc_handler) == peer_connection_id_map_.end())
446    return;
447
448  RenderThreadImpl::current()->Send(
449      new PeerConnectionTrackerHost_UpdatePeerConnection(
450          peer_connection_id_map_[pc_handler], type, value));
451}
452
453}  // namespace content
454