普通文本  |  472行  |  15.12 KB

// 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/utf_string_conversions.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 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 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(component.source().id());
  return id;
}

static string SerializeMediaDescriptor(
    const blink::WebMediaStream& stream) {
  string label = base::UTF16ToUTF8(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;
}

#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 webrtc::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 (size_t i = 0; i < report.values.size(); ++i) {
    values->AppendString(report.values[i].name);
    values->AppendString(report.values[i].value);
  }
  return dict;
}

// Builds a DictionaryValue from the StatsReport.
// The caller takes the ownership of the returned value.
static base::DictionaryValue* GetDictValue(const webrtc::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);
  result->SetString("type", report.type);

  return result.release();
}

class InternalStatsObserver : public webrtc::StatsObserver {
 public:
  InternalStatsObserver(int lid)
      : lid_(lid){}

  virtual void OnComplete(
      const std::vector<webrtc::StatsReport>& reports) OVERRIDE {
    base::ListValue list;

    for (size_t i = 0; i < reports.size(); ++i) {
      base::DictionaryValue* report = GetDictValue(reports[i]);
      if (report)
        list.Append(report);
    }

    if (!list.empty())
      RenderThreadImpl::current()->Send(
          new PeerConnectionTrackerHost_AddStats(lid_, list));
  }

 protected:
  virtual ~InternalStatsObserver() {}

 private:
  int lid_;
};

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_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void PeerConnectionTracker::OnGetAllStats() {
  for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
       it != peer_connection_id_map_.end(); ++it) {

    talk_base::scoped_refptr<InternalStatsObserver> observer(
        new talk_base::RefCountedObject<InternalStatsObserver>(it->second));

    it->first->GetStats(
        observer,
        NULL,
        webrtc::PeerConnectionInterface::kStatsOutputLevelDebug);
  }
}

void PeerConnectionTracker::RegisterPeerConnection(
    RTCPeerConnectionHandler* pc_handler,
    const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
    const RTCMediaConstraints& constraints,
    const blink::WebFrame* frame) {
  DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
  PeerConnectionInfo info;

  info.lid = GetNextLocalID();
  info.servers = SerializeServers(servers);
  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) {
  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) {
  SendPeerConnectionUpdate(
      pc_handler, "createOffer",
      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
}

void PeerConnectionTracker::TrackCreateAnswer(
    RTCPeerConnectionHandler* pc_handler,
    const RTCMediaConstraints& constraints) {
  SendPeerConnectionUpdate(
      pc_handler, "createAnswer",
      "constraints: {" + SerializeMediaConstraints(constraints) + "}");
}

void PeerConnectionTracker::TrackSetSessionDescription(
    RTCPeerConnectionHandler* pc_handler,
    const blink::WebRTCSessionDescription& desc,
    Source source) {
  string sdp = base::UTF16ToUTF8(desc.sdp());
  string type = base::UTF16ToUTF8(desc.type());

  string value = "type: " + type + ", sdp: " + sdp;
  SendPeerConnectionUpdate(
      pc_handler,
      source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
      value);
}

void PeerConnectionTracker::TrackUpdateIce(
      RTCPeerConnectionHandler* pc_handler,
      const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers,
      const RTCMediaConstraints& options) {
  string servers_string = "servers: " + SerializeServers(servers);
  string constraints =
      "constraints: {" + SerializeMediaConstraints(options) + "}";

  SendPeerConnectionUpdate(
      pc_handler, "updateIce", servers_string + ", " + constraints);
}

void PeerConnectionTracker::TrackAddIceCandidate(
      RTCPeerConnectionHandler* pc_handler,
      const blink::WebRTCICECandidate& candidate,
      Source source) {
  string value = "mid: " + base::UTF16ToUTF8(candidate.sdpMid()) + ", " +
                 "candidate: " + base::UTF16ToUTF8(candidate.candidate());
  SendPeerConnectionUpdate(
      pc_handler,
      source == SOURCE_LOCAL ? "onIceCandidate" : "addIceCandidate", value);
}

void PeerConnectionTracker::TrackAddStream(
    RTCPeerConnectionHandler* pc_handler,
    const blink::WebMediaStream& stream,
    Source source){
  SendPeerConnectionUpdate(
      pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream",
      SerializeMediaDescriptor(stream));
}

void PeerConnectionTracker::TrackRemoveStream(
    RTCPeerConnectionHandler* pc_handler,
    const blink::WebMediaStream& stream,
    Source source){
  SendPeerConnectionUpdate(
      pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream",
      SerializeMediaDescriptor(stream));
}

void PeerConnectionTracker::TrackCreateDataChannel(
    RTCPeerConnectionHandler* pc_handler,
    const webrtc::DataChannelInterface* data_channel,
    Source source) {
  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) {
  SendPeerConnectionUpdate(pc_handler, "stop", std::string());
}

void PeerConnectionTracker::TrackSignalingStateChange(
      RTCPeerConnectionHandler* pc_handler,
      WebRTCPeerConnectionHandlerClient::SignalingState state) {
  SendPeerConnectionUpdate(
      pc_handler, "signalingStateChange", GetSignalingStateString(state));
}

void PeerConnectionTracker::TrackIceConnectionStateChange(
      RTCPeerConnectionHandler* pc_handler,
      WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
  SendPeerConnectionUpdate(
      pc_handler, "iceConnectionStateChange",
      GetIceConnectionStateString(state));
}

void PeerConnectionTracker::TrackIceGatheringStateChange(
      RTCPeerConnectionHandler* pc_handler,
      WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
  SendPeerConnectionUpdate(
      pc_handler, "iceGatheringStateChange",
      GetIceGatheringStateString(state));
}

void PeerConnectionTracker::TrackSessionDescriptionCallback(
    RTCPeerConnectionHandler* pc_handler, Action action,
    const string& callback_type, const string& value) {
  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) {
  SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string());
}

void PeerConnectionTracker::TrackCreateDTMFSender(
    RTCPeerConnectionHandler* pc_handler,
    const blink::WebMediaStreamTrack& track) {
  SendPeerConnectionUpdate(pc_handler, "createDTMFSender",
                           base::UTF16ToUTF8(track.id()));
}

void PeerConnectionTracker::TrackGetUserMedia(
    const blink::WebUserMediaRequest& user_media_request) {
  RTCMediaConstraints audio_constraints(user_media_request.audioConstraints());
  RTCMediaConstraints video_constraints(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() {
  return next_lid_++;
}

void PeerConnectionTracker::SendPeerConnectionUpdate(
    RTCPeerConnectionHandler* pc_handler,
    const std::string& type,
    const std::string& value) {
  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