Replace FINAL and OVERRIDE with their C++11 counterparts in content/renderer
[chromium-blink-merge.git] / content / renderer / media / webrtc / media_stream_track_metrics.cc
blobf13d192241cb5150139afc5b4501192c8159ab2e
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.
5 #include "content/renderer/media/webrtc/media_stream_track_metrics.h"
7 #include <inttypes.h>
8 #include <set>
9 #include <string>
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"
16 using webrtc::AudioTrackVector;
17 using webrtc::MediaStreamInterface;
18 using webrtc::MediaStreamTrackInterface;
19 using webrtc::PeerConnectionInterface;
20 using webrtc::VideoTrackVector;
22 namespace content {
24 class MediaStreamTrackMetricsObserver : public webrtc::ObserverInterface {
25 public:
26 MediaStreamTrackMetricsObserver(
27 MediaStreamTrackMetrics::StreamType stream_type,
28 MediaStreamInterface* stream,
29 MediaStreamTrackMetrics* owner);
30 virtual ~MediaStreamTrackMetricsObserver();
32 // Sends begin/end messages for all tracks currently tracked.
33 void SendLifetimeMessages(MediaStreamTrackMetrics::LifetimeEvent event);
35 MediaStreamInterface* stream() { return stream_; }
36 MediaStreamTrackMetrics::StreamType stream_type() { return stream_type_; }
38 private:
39 typedef std::set<std::string> IdSet;
41 // webrtc::ObserverInterface implementation.
42 virtual void OnChanged() override;
44 template <class T>
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());
52 return track_ids;
55 void ReportAddedAndRemovedTracks(
56 const IdSet& new_ids,
57 const IdSet& old_ids,
58 MediaStreamTrackMetrics::TrackType track_type);
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);
66 // False until start/end of lifetime messages have been sent.
67 bool has_reported_start_;
68 bool has_reported_end_;
70 // IDs of audio and video tracks in the stream being observed.
71 IdSet audio_track_ids_;
72 IdSet video_track_ids_;
74 MediaStreamTrackMetrics::StreamType stream_type_;
75 rtc::scoped_refptr<MediaStreamInterface> stream_;
77 // Non-owning.
78 MediaStreamTrackMetrics* owner_;
81 namespace {
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();
92 MediaStreamTrackMetrics::StreamType stream_type;
93 MediaStreamInterface* stream_;
96 } // namespace
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);
111 MediaStreamTrackMetricsObserver::~MediaStreamTrackMetricsObserver() {
112 stream_->UnregisterObserver(this);
113 SendLifetimeMessages(MediaStreamTrackMetrics::DISCONNECTED);
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);
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;
136 ReportTracks(audio_track_ids_, MediaStreamTrackMetrics::AUDIO_TRACK, event);
137 ReportTracks(video_track_ids_, MediaStreamTrackMetrics::VIDEO_TRACK, event);
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;
149 void MediaStreamTrackMetricsObserver::OnChanged() {
150 AudioTrackVector all_audio_tracks = stream_->GetAudioTracks();
151 IdSet all_audio_track_ids = GetTrackIds(all_audio_tracks);
153 VideoTrackVector all_video_tracks = stream_->GetVideoTracks();
154 IdSet all_video_track_ids = GetTrackIds(all_video_tracks);
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);
167 // We always update our sets of tracks.
168 audio_track_ids_ = all_audio_track_ids;
169 video_track_ids_ = all_video_track_ids;
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_);
178 IdSet added_tracks = base::STLSetDifference<IdSet>(new_ids, old_ids);
179 IdSet removed_tracks = base::STLSetDifference<IdSet>(old_ids, new_ids);
181 ReportTracks(added_tracks, track_type, MediaStreamTrackMetrics::CONNECTED);
182 ReportTracks(
183 removed_tracks, track_type, MediaStreamTrackMetrics::DISCONNECTED);
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_);
195 MediaStreamTrackMetrics::MediaStreamTrackMetrics()
196 : ice_state_(webrtc::PeerConnectionInterface::kIceConnectionNew) {}
198 MediaStreamTrackMetrics::~MediaStreamTrackMetrics() {
199 for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
200 ++it) {
201 (*it)->SendLifetimeMessages(DISCONNECTED);
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);
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;
225 observers_.erase(it);
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);
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;
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;
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;
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)));
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.
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);
314 base::MD5Context ctx;
315 base::MD5Init(&ctx);
316 base::MD5Update(&ctx, unique_id_string);
317 base::MD5Digest digest;
318 base::MD5Final(&digest, &ctx);
320 COMPILE_ASSERT(sizeof(digest.a) > sizeof(uint64), NeedBiggerDigest);
321 return *reinterpret_cast<uint64*>(digest.a);
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);
332 } // namespace content