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 WebKit::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 WebKit::WebMediaStreamTrack component) {
67  string id = UTF16ToUTF8(component.source().id());
68  return id;
69}
70
71static string SerializeMediaDescriptor(
72    const WebKit::WebMediaStream& stream) {
73  string label = UTF16ToUTF8(stream.id());
74  string result = "label: " + label;
75  WebKit::WebVector<WebKit::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  if (!dict)
165    return NULL;
166  dict->SetDouble("timestamp", report.timestamp);
167
168  base::ListValue* values = new base::ListValue();
169  if (!values) {
170    delete dict;
171    return NULL;
172  }
173  dict->Set("values", values);
174
175  for (size_t i = 0; i < report.values.size(); ++i) {
176    values->AppendString(report.values[i].name);
177    values->AppendString(report.values[i].value);
178  }
179  return dict;
180}
181
182// Builds a DictionaryValue from the StatsReport.
183// The caller takes the ownership of the returned value.
184static base::DictionaryValue* GetDictValue(const webrtc::StatsReport& report) {
185  scoped_ptr<base::DictionaryValue> stats, result;
186
187  stats.reset(GetDictValueStats(report));
188  if (!stats)
189    return NULL;
190
191  result.reset(new base::DictionaryValue());
192  if (!result)
193    return NULL;
194
195  // Note:
196  // The format must be consistent with what webrtc_internals.js expects.
197  // If you change it here, you must change webrtc_internals.js as well.
198  if (stats)
199    result->Set("stats", stats.release());
200  result->SetString("id", report.id);
201  result->SetString("type", report.type);
202
203  return result.release();
204}
205
206class InternalStatsObserver : public webrtc::StatsObserver {
207 public:
208  InternalStatsObserver(int lid)
209      : lid_(lid){}
210
211  virtual void OnComplete(
212      const std::vector<webrtc::StatsReport>& reports) OVERRIDE {
213    base::ListValue list;
214
215    for (size_t i = 0; i < reports.size(); ++i) {
216      base::DictionaryValue* report = GetDictValue(reports[i]);
217      if (report)
218        list.Append(report);
219    }
220
221    if (!list.empty())
222      RenderThreadImpl::current()->Send(
223          new PeerConnectionTrackerHost_AddStats(lid_, list));
224  }
225
226 protected:
227  virtual ~InternalStatsObserver() {}
228
229 private:
230  int lid_;
231};
232
233PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
234}
235
236PeerConnectionTracker::~PeerConnectionTracker() {
237}
238
239bool PeerConnectionTracker::OnControlMessageReceived(
240    const IPC::Message& message) {
241  bool handled = true;
242  IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message)
243    IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats, OnGetAllStats)
244    IPC_MESSAGE_UNHANDLED(handled = false)
245  IPC_END_MESSAGE_MAP()
246  return handled;
247}
248
249void PeerConnectionTracker::OnGetAllStats() {
250  for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
251       it != peer_connection_id_map_.end(); ++it) {
252
253    talk_base::scoped_refptr<InternalStatsObserver> observer(
254        new talk_base::RefCountedObject<InternalStatsObserver>(it->second));
255
256    it->first->GetStats(observer, NULL);
257  }
258}
259
260void PeerConnectionTracker::RegisterPeerConnection(
261    RTCPeerConnectionHandler* pc_handler,
262    const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
263    const RTCMediaConstraints& constraints,
264    const WebKit::WebFrame* frame) {
265  DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
266  PeerConnectionInfo info;
267
268  info.lid = GetNextLocalID();
269  info.servers = SerializeServers(servers);
270  info.constraints = SerializeMediaConstraints(constraints);
271  info.url = frame->document().url().spec();
272  RenderThreadImpl::current()->Send(
273      new PeerConnectionTrackerHost_AddPeerConnection(info));
274
275  DCHECK(peer_connection_id_map_.find(pc_handler) ==
276         peer_connection_id_map_.end());
277  peer_connection_id_map_[pc_handler] = info.lid;
278}
279
280void PeerConnectionTracker::UnregisterPeerConnection(
281    RTCPeerConnectionHandler* pc_handler) {
282  DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
283
284  std::map<RTCPeerConnectionHandler*, int>::iterator it =
285      peer_connection_id_map_.find(pc_handler);
286
287  if (it == peer_connection_id_map_.end()) {
288    // The PeerConnection might not have been registered if its initilization
289    // failed.
290    return;
291  }
292
293  RenderThreadImpl::current()->Send(
294      new PeerConnectionTrackerHost_RemovePeerConnection(it->second));
295
296  peer_connection_id_map_.erase(it);
297}
298
299void PeerConnectionTracker::TrackCreateOffer(
300    RTCPeerConnectionHandler* pc_handler,
301    const RTCMediaConstraints& constraints) {
302  SendPeerConnectionUpdate(
303      pc_handler, "createOffer",
304      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
305}
306
307void PeerConnectionTracker::TrackCreateAnswer(
308    RTCPeerConnectionHandler* pc_handler,
309    const RTCMediaConstraints& constraints) {
310  SendPeerConnectionUpdate(
311      pc_handler, "createAnswer",
312      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
313}
314
315void PeerConnectionTracker::TrackSetSessionDescription(
316    RTCPeerConnectionHandler* pc_handler,
317    const WebKit::WebRTCSessionDescription& desc,
318    Source source) {
319  string sdp = UTF16ToUTF8(desc.sdp());
320  string type = UTF16ToUTF8(desc.type());
321
322  string value = "type: " + type + ", sdp: " + sdp;
323  SendPeerConnectionUpdate(
324      pc_handler,
325      source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
326      value);
327}
328
329void PeerConnectionTracker::TrackUpdateIce(
330      RTCPeerConnectionHandler* pc_handler,
331      const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
332      const RTCMediaConstraints& options) {
333  string servers_string = "servers: " + SerializeServers(servers);
334  string constraints =
335      "constraints: {" + SerializeMediaConstraints(options) + "}";
336
337  SendPeerConnectionUpdate(
338      pc_handler, "updateIce", servers_string + ", " + constraints);
339}
340
341void PeerConnectionTracker::TrackAddIceCandidate(
342      RTCPeerConnectionHandler* pc_handler,
343      const WebKit::WebRTCICECandidate& candidate,
344      Source source) {
345  string value = "mid: " + UTF16ToUTF8(candidate.sdpMid()) + ", " +
346                 "candidate: " + UTF16ToUTF8(candidate.candidate());
347  SendPeerConnectionUpdate(
348      pc_handler,
349      source == SOURCE_LOCAL ? "onIceCandidate" : "addIceCandidate", value);
350}
351
352void PeerConnectionTracker::TrackAddStream(
353    RTCPeerConnectionHandler* pc_handler,
354    const WebKit::WebMediaStream& stream,
355    Source source){
356  SendPeerConnectionUpdate(
357      pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream",
358      SerializeMediaDescriptor(stream));
359}
360
361void PeerConnectionTracker::TrackRemoveStream(
362    RTCPeerConnectionHandler* pc_handler,
363    const WebKit::WebMediaStream& stream,
364    Source source){
365  SendPeerConnectionUpdate(
366      pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream",
367      SerializeMediaDescriptor(stream));
368}
369
370void PeerConnectionTracker::TrackCreateDataChannel(
371    RTCPeerConnectionHandler* pc_handler,
372    const webrtc::DataChannelInterface* data_channel,
373    Source source) {
374  string value = "label: " + data_channel->label() +
375                 ", reliable: " + (data_channel->reliable() ? "true" : "false");
376  SendPeerConnectionUpdate(
377      pc_handler,
378      source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel",
379      value);
380}
381
382void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) {
383  SendPeerConnectionUpdate(pc_handler, "stop", std::string());
384}
385
386void PeerConnectionTracker::TrackSignalingStateChange(
387      RTCPeerConnectionHandler* pc_handler,
388      WebRTCPeerConnectionHandlerClient::SignalingState state) {
389  SendPeerConnectionUpdate(
390      pc_handler, "signalingStateChange", GetSignalingStateString(state));
391}
392
393void PeerConnectionTracker::TrackIceConnectionStateChange(
394      RTCPeerConnectionHandler* pc_handler,
395      WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
396  SendPeerConnectionUpdate(
397      pc_handler, "iceConnectionStateChange",
398      GetIceConnectionStateString(state));
399}
400
401void PeerConnectionTracker::TrackIceGatheringStateChange(
402      RTCPeerConnectionHandler* pc_handler,
403      WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
404  SendPeerConnectionUpdate(
405      pc_handler, "iceGatheringStateChange",
406      GetIceGatheringStateString(state));
407}
408
409void PeerConnectionTracker::TrackSessionDescriptionCallback(
410    RTCPeerConnectionHandler* pc_handler, Action action,
411    const string& callback_type, const string& value) {
412  string update_type;
413  switch (action) {
414    case ACTION_SET_LOCAL_DESCRIPTION:
415      update_type = "setLocalDescription";
416      break;
417    case ACTION_SET_REMOTE_DESCRIPTION:
418      update_type = "setRemoteDescription";
419      break;
420    case ACTION_CREATE_OFFER:
421      update_type = "createOffer";
422      break;
423    case ACTION_CREATE_ANSWER:
424      update_type = "createAnswer";
425      break;
426    default:
427      NOTREACHED();
428      break;
429  }
430  update_type += callback_type;
431
432  SendPeerConnectionUpdate(pc_handler, update_type, value);
433}
434
435void PeerConnectionTracker::TrackOnRenegotiationNeeded(
436    RTCPeerConnectionHandler* pc_handler) {
437  SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string());
438}
439
440void PeerConnectionTracker::TrackCreateDTMFSender(
441    RTCPeerConnectionHandler* pc_handler,
442    const WebKit::WebMediaStreamTrack& track) {
443  SendPeerConnectionUpdate(pc_handler, "createDTMFSender",
444                           UTF16ToUTF8(track.id()));
445}
446
447int PeerConnectionTracker::GetNextLocalID() {
448  return next_lid_++;
449}
450
451void PeerConnectionTracker::SendPeerConnectionUpdate(
452    RTCPeerConnectionHandler* pc_handler,
453    const std::string& type,
454    const std::string& value) {
455  if (peer_connection_id_map_.find(pc_handler) == peer_connection_id_map_.end())
456    return;
457
458  RenderThreadImpl::current()->Send(
459      new PeerConnectionTrackerHost_UpdatePeerConnection(
460          peer_connection_id_map_[pc_handler], type, value));
461}
462
463}  // namespace content
464