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"
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
;
24 class MediaStreamTrackMetricsObserver
: public webrtc::ObserverInterface
{
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_
; }
39 typedef std::set
<std::string
> IdSet
;
41 // webrtc::ObserverInterface implementation.
42 virtual void OnChanged() override
;
45 IdSet
GetTrackIds(const std::vector
<rtc::scoped_refptr
<T
> >& tracks
) {
47 typename
std::vector
<rtc::scoped_refptr
<T
> >::const_iterator it
=
49 for (; it
!= tracks
.end(); ++it
) {
50 track_ids
.insert((*it
)->id());
55 void ReportAddedAndRemovedTracks(
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_
;
78 MediaStreamTrackMetrics
* owner_
;
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_
;
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
),
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_
)
123 DCHECK(!has_reported_start_
&& !has_reported_end_
);
124 has_reported_start_
= true;
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_
)
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
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
158 if (has_reported_start_
&& !has_reported_end_
) {
159 ReportAddedAndRemovedTracks(all_audio_track_ids
,
161 MediaStreamTrackMetrics::AUDIO_TRACK
);
162 ReportAddedAndRemovedTracks(all_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
);
183 removed_tracks
, track_type
, MediaStreamTrackMetrics::DISCONNECTED
);
186 void MediaStreamTrackMetricsObserver::ReportTracks(
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();
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.
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();
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
);
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
261 case PeerConnectionInterface::kIceConnectionDisconnected
:
262 case PeerConnectionInterface::kIceConnectionClosed
:
263 observer
->SendLifetimeMessages(DISCONNECTED
);
267 // We ignore the remaining state (CHECKING) as it is never
268 // involved in a transition from connected to disconnected or
274 void MediaStreamTrackMetrics::SendLifetimeMessage(const std::string
& track_id
,
275 TrackType track_type
,
277 StreamType stream_type
) {
278 RenderThreadImpl
* render_thread
= RenderThreadImpl::current();
279 // |render_thread| can be NULL in certain cases when running as part
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
));
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",
312 stream_type
== RECEIVED_STREAM
? 1 : 0);
314 base::MD5Context 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)),
332 } // namespace content