Home | History | Annotate | Download | only in webrtc
      1 // Copyright 2014 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 
      5 #include "content/renderer/media/webrtc/media_stream_track_metrics.h"
      6 
      7 #include <inttypes.h>
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/md5.h"
     12 #include "content/common/media/media_stream_track_metrics_host_messages.h"
     13 #include "content/renderer/render_thread_impl.h"
     14 #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h"
     15 
     16 using webrtc::AudioTrackVector;
     17 using webrtc::MediaStreamInterface;
     18 using webrtc::MediaStreamTrackInterface;
     19 using webrtc::PeerConnectionInterface;
     20 using webrtc::VideoTrackVector;
     21 
     22 namespace content {
     23 
     24 class MediaStreamTrackMetricsObserver : public webrtc::ObserverInterface {
     25  public:
     26   MediaStreamTrackMetricsObserver(
     27       MediaStreamTrackMetrics::StreamType stream_type,
     28       MediaStreamInterface* stream,
     29       MediaStreamTrackMetrics* owner);
     30   virtual ~MediaStreamTrackMetricsObserver();
     31 
     32   // Sends begin/end messages for all tracks currently tracked.
     33   void SendLifetimeMessages(MediaStreamTrackMetrics::LifetimeEvent event);
     34 
     35   MediaStreamInterface* stream() { return stream_; }
     36   MediaStreamTrackMetrics::StreamType stream_type() { return stream_type_; }
     37 
     38  private:
     39   typedef std::set<std::string> IdSet;
     40 
     41   // webrtc::ObserverInterface implementation.
     42   virtual void OnChanged() OVERRIDE;
     43 
     44   template <class T>
     45   IdSet GetTrackIds(const std::vector<talk_base::scoped_refptr<T> >& tracks) {
     46     IdSet track_ids;
     47     typename std::vector<talk_base::scoped_refptr<T> >::const_iterator it =
     48         tracks.begin();
     49     for (; it != tracks.end(); ++it) {
     50       track_ids.insert((*it)->id());
     51     }
     52     return track_ids;
     53   }
     54 
     55   void ReportAddedAndRemovedTracks(
     56       const IdSet& new_ids,
     57       const IdSet& old_ids,
     58       MediaStreamTrackMetrics::TrackType track_type);
     59 
     60   // Sends a lifetime message for the given tracks. OK to call with an
     61   // empty |ids|, in which case the method has no side effects.
     62   void ReportTracks(const IdSet& ids,
     63                     MediaStreamTrackMetrics::TrackType track_type,
     64                     MediaStreamTrackMetrics::LifetimeEvent event);
     65 
     66   // False until start/end of lifetime messages have been sent.
     67   bool has_reported_start_;
     68   bool has_reported_end_;
     69 
     70   // IDs of audio and video tracks in the stream being observed.
     71   IdSet audio_track_ids_;
     72   IdSet video_track_ids_;
     73 
     74   MediaStreamTrackMetrics::StreamType stream_type_;
     75   talk_base::scoped_refptr<MediaStreamInterface> stream_;
     76 
     77   // Non-owning.
     78   MediaStreamTrackMetrics* owner_;
     79 };
     80 
     81 namespace {
     82 
     83 // Used with std::find_if.
     84 struct ObserverFinder {
     85   ObserverFinder(MediaStreamTrackMetrics::StreamType stream_type,
     86                  MediaStreamInterface* stream)
     87       : stream_type(stream_type), stream_(stream) {}
     88   bool operator()(MediaStreamTrackMetricsObserver* observer) {
     89     return stream_ == observer->stream() &&
     90            stream_type == observer->stream_type();
     91   }
     92   MediaStreamTrackMetrics::StreamType stream_type;
     93   MediaStreamInterface* stream_;
     94 };
     95 
     96 }  // namespace
     97 
     98 MediaStreamTrackMetricsObserver::MediaStreamTrackMetricsObserver(
     99     MediaStreamTrackMetrics::StreamType stream_type,
    100     MediaStreamInterface* stream,
    101     MediaStreamTrackMetrics* owner)
    102     : has_reported_start_(false),
    103       has_reported_end_(false),
    104       stream_type_(stream_type),
    105       stream_(stream),
    106       owner_(owner) {
    107   OnChanged();  // To populate initial tracks.
    108   stream_->RegisterObserver(this);
    109 }
    110 
    111 MediaStreamTrackMetricsObserver::~MediaStreamTrackMetricsObserver() {
    112   stream_->UnregisterObserver(this);
    113   SendLifetimeMessages(MediaStreamTrackMetrics::DISCONNECTED);
    114 }
    115 
    116 void MediaStreamTrackMetricsObserver::SendLifetimeMessages(
    117     MediaStreamTrackMetrics::LifetimeEvent event) {
    118   if (event == MediaStreamTrackMetrics::CONNECTED) {
    119     // Both ICE CONNECTED and COMPLETED can trigger the first
    120     // start-of-life event, so we only report the first.
    121     if (has_reported_start_)
    122       return;
    123     DCHECK(!has_reported_start_ && !has_reported_end_);
    124     has_reported_start_ = true;
    125   } else {
    126     DCHECK(event == MediaStreamTrackMetrics::DISCONNECTED);
    127 
    128     // We only report the first end-of-life event, since there are
    129     // several cases where end-of-life can be reached. We also don't
    130     // report end unless we've reported start.
    131     if (has_reported_end_ || !has_reported_start_)
    132       return;
    133     has_reported_end_ = true;
    134   }
    135 
    136   ReportTracks(audio_track_ids_, MediaStreamTrackMetrics::AUDIO_TRACK, event);
    137   ReportTracks(video_track_ids_, MediaStreamTrackMetrics::VIDEO_TRACK, event);
    138 
    139   if (event == MediaStreamTrackMetrics::DISCONNECTED) {
    140     // After disconnection, we can get reconnected, so we need to
    141     // forget that we've sent lifetime events, while retaining all
    142     // other state.
    143     DCHECK(has_reported_start_ && has_reported_end_);
    144     has_reported_start_ = false;
    145     has_reported_end_ = false;
    146   }
    147 }
    148 
    149 void MediaStreamTrackMetricsObserver::OnChanged() {
    150   AudioTrackVector all_audio_tracks = stream_->GetAudioTracks();
    151   IdSet all_audio_track_ids = GetTrackIds(all_audio_tracks);
    152 
    153   VideoTrackVector all_video_tracks = stream_->GetVideoTracks();
    154   IdSet all_video_track_ids = GetTrackIds(all_video_tracks);
    155 
    156   // We only report changes after our initial report, and never after
    157   // our last report.
    158   if (has_reported_start_ && !has_reported_end_) {
    159     ReportAddedAndRemovedTracks(all_audio_track_ids,
    160                                 audio_track_ids_,
    161                                 MediaStreamTrackMetrics::AUDIO_TRACK);
    162     ReportAddedAndRemovedTracks(all_video_track_ids,
    163                                 video_track_ids_,
    164                                 MediaStreamTrackMetrics::VIDEO_TRACK);
    165   }
    166 
    167   // We always update our sets of tracks.
    168   audio_track_ids_ = all_audio_track_ids;
    169   video_track_ids_ = all_video_track_ids;
    170 }
    171 
    172 void MediaStreamTrackMetricsObserver::ReportAddedAndRemovedTracks(
    173     const IdSet& new_ids,
    174     const IdSet& old_ids,
    175     MediaStreamTrackMetrics::TrackType track_type) {
    176   DCHECK(has_reported_start_ && !has_reported_end_);
    177 
    178   IdSet added_tracks = base::STLSetDifference<IdSet>(new_ids, old_ids);
    179   IdSet removed_tracks = base::STLSetDifference<IdSet>(old_ids, new_ids);
    180 
    181   ReportTracks(added_tracks, track_type, MediaStreamTrackMetrics::CONNECTED);
    182   ReportTracks(
    183       removed_tracks, track_type, MediaStreamTrackMetrics::DISCONNECTED);
    184 }
    185 
    186 void MediaStreamTrackMetricsObserver::ReportTracks(
    187     const IdSet& ids,
    188     MediaStreamTrackMetrics::TrackType track_type,
    189     MediaStreamTrackMetrics::LifetimeEvent event) {
    190   for (IdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
    191     owner_->SendLifetimeMessage(*it, track_type, event, stream_type_);
    192   }
    193 }
    194 
    195 MediaStreamTrackMetrics::MediaStreamTrackMetrics()
    196     : ice_state_(webrtc::PeerConnectionInterface::kIceConnectionNew) {}
    197 
    198 MediaStreamTrackMetrics::~MediaStreamTrackMetrics() {
    199   for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
    200        ++it) {
    201     (*it)->SendLifetimeMessages(DISCONNECTED);
    202   }
    203 }
    204 
    205 void MediaStreamTrackMetrics::AddStream(StreamType type,
    206                                         MediaStreamInterface* stream) {
    207   DCHECK(CalledOnValidThread());
    208   MediaStreamTrackMetricsObserver* observer =
    209       new MediaStreamTrackMetricsObserver(type, stream, this);
    210   observers_.insert(observers_.end(), observer);
    211   SendLifeTimeMessageDependingOnIceState(observer);
    212 }
    213 
    214 void MediaStreamTrackMetrics::RemoveStream(StreamType type,
    215                                            MediaStreamInterface* stream) {
    216   DCHECK(CalledOnValidThread());
    217   ObserverVector::iterator it = std::find_if(
    218       observers_.begin(), observers_.end(), ObserverFinder(type, stream));
    219   if (it == observers_.end()) {
    220     // Since external apps could call removeStream with a stream they
    221     // never added, this can happen without it being an error.
    222     return;
    223   }
    224 
    225   observers_.erase(it);
    226 }
    227 
    228 void MediaStreamTrackMetrics::IceConnectionChange(
    229     PeerConnectionInterface::IceConnectionState new_state) {
    230   DCHECK(CalledOnValidThread());
    231   ice_state_ = new_state;
    232   for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
    233        ++it) {
    234     SendLifeTimeMessageDependingOnIceState(*it);
    235   }
    236 }
    237 void MediaStreamTrackMetrics::SendLifeTimeMessageDependingOnIceState(
    238     MediaStreamTrackMetricsObserver* observer) {
    239   // There is a state transition diagram for these states at
    240   // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCIceConnectionState
    241   switch (ice_state_) {
    242     case PeerConnectionInterface::kIceConnectionConnected:
    243     case PeerConnectionInterface::kIceConnectionCompleted:
    244       observer->SendLifetimeMessages(CONNECTED);
    245       break;
    246 
    247     case PeerConnectionInterface::kIceConnectionFailed:
    248       // We don't really need to handle FAILED (it is only supposed
    249       // to be preceded by CHECKING so we wouldn't yet have sent a
    250       // lifetime message) but we might as well use belt and
    251       // suspenders and handle it the same as the other "end call"
    252       // states. It will be ignored anyway if the call is not
    253       // already connected.
    254     case PeerConnectionInterface::kIceConnectionNew:
    255       // It's a bit weird to count NEW as an end-lifetime event, but
    256       // it's possible to transition directly from a connected state
    257       // (CONNECTED or COMPLETED) to NEW, which can then be followed
    258       // by a new connection. The observer will ignore the end
    259       // lifetime event if it was not preceded by a begin-lifetime
    260       // event.
    261     case PeerConnectionInterface::kIceConnectionDisconnected:
    262     case PeerConnectionInterface::kIceConnectionClosed:
    263       observer->SendLifetimeMessages(DISCONNECTED);
    264       break;
    265 
    266     default:
    267       // We ignore the remaining state (CHECKING) as it is never
    268       // involved in a transition from connected to disconnected or
    269       // vice versa.
    270       break;
    271   }
    272 }
    273 
    274 void MediaStreamTrackMetrics::SendLifetimeMessage(const std::string& track_id,
    275                                                   TrackType track_type,
    276                                                   LifetimeEvent event,
    277                                                   StreamType stream_type) {
    278   RenderThreadImpl* render_thread = RenderThreadImpl::current();
    279   // |render_thread| can be NULL in certain cases when running as part
    280   // |of a unit test.
    281   if (render_thread) {
    282     if (event == CONNECTED) {
    283       RenderThreadImpl::current()->Send(
    284           new MediaStreamTrackMetricsHost_AddTrack(
    285               MakeUniqueId(track_id, stream_type),
    286               track_type == AUDIO_TRACK,
    287               stream_type == RECEIVED_STREAM));
    288     } else {
    289       DCHECK_EQ(DISCONNECTED, event);
    290       RenderThreadImpl::current()->Send(
    291           new MediaStreamTrackMetricsHost_RemoveTrack(
    292               MakeUniqueId(track_id, stream_type)));
    293     }
    294   }
    295 }
    296 
    297 uint64 MediaStreamTrackMetrics::MakeUniqueIdImpl(uint64 pc_id,
    298                                                  const std::string& track_id,
    299                                                  StreamType stream_type) {
    300   // We use a hash over the |track| pointer and the PeerConnection ID,
    301   // plus a boolean flag indicating whether the track is remote (since
    302   // you might conceivably have a remote track added back as a sent
    303   // track) as the unique ID.
    304   //
    305   // We don't need a cryptographically secure hash (which MD5 should
    306   // no longer be considered), just one with virtually zero chance of
    307   // collisions when faced with non-malicious data.
    308   std::string unique_id_string =
    309       base::StringPrintf("%" PRIu64 " %s %d",
    310                          pc_id,
    311                          track_id.c_str(),
    312                          stream_type == RECEIVED_STREAM ? 1 : 0);
    313 
    314   base::MD5Context ctx;
    315   base::MD5Init(&ctx);
    316   base::MD5Update(&ctx, unique_id_string);
    317   base::MD5Digest digest;
    318   base::MD5Final(&digest, &ctx);
    319 
    320   COMPILE_ASSERT(sizeof(digest.a) > sizeof(uint64), NeedBiggerDigest);
    321   return *reinterpret_cast<uint64*>(digest.a);
    322 }
    323 
    324 uint64 MediaStreamTrackMetrics::MakeUniqueId(const std::string& track_id,
    325                                              StreamType stream_type) {
    326   return MakeUniqueIdImpl(
    327       reinterpret_cast<uint64>(reinterpret_cast<void*>(this)),
    328       track_id,
    329       stream_type);
    330 }
    331 
    332 }  // namespace content
    333