• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
stream()35   MediaStreamInterface* stream() { return stream_; }
stream_type()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>
GetTrackIds(const std::vector<rtc::scoped_refptr<T>> & tracks)45   IdSet GetTrackIds(const std::vector<rtc::scoped_refptr<T> >& tracks) {
46     IdSet track_ids;
47     typename std::vector<rtc::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   rtc::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 {
ObserverFindercontent::__anon858bd2e00111::ObserverFinder85   ObserverFinder(MediaStreamTrackMetrics::StreamType stream_type,
86                  MediaStreamInterface* stream)
87       : stream_type(stream_type), stream_(stream) {}
operator ()content::__anon858bd2e00111::ObserverFinder88   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 
MediaStreamTrackMetricsObserver(MediaStreamTrackMetrics::StreamType stream_type,MediaStreamInterface * stream,MediaStreamTrackMetrics * owner)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 
~MediaStreamTrackMetricsObserver()111 MediaStreamTrackMetricsObserver::~MediaStreamTrackMetricsObserver() {
112   stream_->UnregisterObserver(this);
113   SendLifetimeMessages(MediaStreamTrackMetrics::DISCONNECTED);
114 }
115 
SendLifetimeMessages(MediaStreamTrackMetrics::LifetimeEvent event)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 
OnChanged()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 
ReportAddedAndRemovedTracks(const IdSet & new_ids,const IdSet & old_ids,MediaStreamTrackMetrics::TrackType track_type)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 
ReportTracks(const IdSet & ids,MediaStreamTrackMetrics::TrackType track_type,MediaStreamTrackMetrics::LifetimeEvent event)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 
MediaStreamTrackMetrics()195 MediaStreamTrackMetrics::MediaStreamTrackMetrics()
196     : ice_state_(webrtc::PeerConnectionInterface::kIceConnectionNew) {}
197 
~MediaStreamTrackMetrics()198 MediaStreamTrackMetrics::~MediaStreamTrackMetrics() {
199   for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
200        ++it) {
201     (*it)->SendLifetimeMessages(DISCONNECTED);
202   }
203 }
204 
AddStream(StreamType type,MediaStreamInterface * stream)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 
RemoveStream(StreamType type,MediaStreamInterface * stream)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 
IceConnectionChange(PeerConnectionInterface::IceConnectionState new_state)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 }
SendLifeTimeMessageDependingOnIceState(MediaStreamTrackMetricsObserver * observer)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 
SendLifetimeMessage(const std::string & track_id,TrackType track_type,LifetimeEvent event,StreamType stream_type)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 
MakeUniqueIdImpl(uint64 pc_id,const std::string & track_id,StreamType stream_type)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 
MakeUniqueId(const std::string & track_id,StreamType stream_type)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