Roll src/third_party/WebKit eac3800:0237a66 (svn 202606:202607)
[chromium-blink-merge.git] / content / renderer / media / webrtc / media_stream_track_metrics.cc
blob0ec4444582b1e07b59ce5d465bfb7a6187b69cf0
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 "base/thread_task_runner_handle.h"
13 #include "content/common/media/media_stream_track_metrics_host_messages.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h"
17 using webrtc::AudioTrackVector;
18 using webrtc::MediaStreamInterface;
19 using webrtc::MediaStreamTrackInterface;
20 using webrtc::PeerConnectionInterface;
21 using webrtc::VideoTrackVector;
23 namespace content {
24 namespace {
25 typedef std::set<std::string> IdSet;
27 template <class T>
28 IdSet GetTrackIds(const std::vector<rtc::scoped_refptr<T>>& tracks) {
29 IdSet track_ids;
30 for (const auto& track : tracks)
31 track_ids.insert(track->id());
32 return track_ids;
35 // TODO(tommi): Consolidate this and TrackObserver since these implementations
36 // are fundamentally achieving the same thing (aside from specific logic inside
37 // the OnChanged callbacks).
38 class MediaStreamObserver
39 : public base::RefCountedThreadSafe<MediaStreamObserver>,
40 public webrtc::ObserverInterface {
41 public:
42 typedef base::Callback<
43 void(const IdSet& audio_track_ids, const IdSet& video_track_ids)>
44 OnChangedCallback;
46 MediaStreamObserver(
47 const OnChangedCallback& callback,
48 const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
49 webrtc::MediaStreamInterface* stream)
50 : main_thread_(main_thread), stream_(stream), callback_(callback) {
51 signaling_thread_.DetachFromThread();
52 stream_->RegisterObserver(this);
55 const scoped_refptr<webrtc::MediaStreamInterface>& stream() const {
56 DCHECK(main_thread_->BelongsToCurrentThread());
57 return stream_;
60 void Unregister() {
61 DCHECK(main_thread_->BelongsToCurrentThread());
62 callback_.Reset();
63 stream_->UnregisterObserver(this);
64 stream_ = nullptr;
67 private:
68 friend class base::RefCountedThreadSafe<MediaStreamObserver>;
69 ~MediaStreamObserver() override {
70 DCHECK(!stream_.get()) << "must have been unregistered before deleting";
73 // webrtc::ObserverInterface implementation.
74 void OnChanged() override {
75 DCHECK(signaling_thread_.CalledOnValidThread());
76 main_thread_->PostTask(FROM_HERE,
77 base::Bind(&MediaStreamObserver::OnChangedOnMainThread, this,
78 GetTrackIds(stream_->GetAudioTracks()),
79 GetTrackIds(stream_->GetVideoTracks())));
82 void OnChangedOnMainThread(const IdSet& audio_track_ids,
83 const IdSet& video_track_ids) {
84 DCHECK(main_thread_->BelongsToCurrentThread());
85 if (!callback_.is_null())
86 callback_.Run(audio_track_ids, video_track_ids);
89 const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
90 scoped_refptr<webrtc::MediaStreamInterface> stream_;
91 OnChangedCallback callback_; // Only touched on the main thread.
92 base::ThreadChecker signaling_thread_;
95 } // namespace
97 class MediaStreamTrackMetricsObserver {
98 public:
99 MediaStreamTrackMetricsObserver(
100 MediaStreamTrackMetrics::StreamType stream_type,
101 MediaStreamInterface* stream,
102 MediaStreamTrackMetrics* owner);
103 ~MediaStreamTrackMetricsObserver();
105 // Sends begin/end messages for all tracks currently tracked.
106 void SendLifetimeMessages(MediaStreamTrackMetrics::LifetimeEvent event);
108 MediaStreamInterface* stream() {
109 DCHECK(thread_checker_.CalledOnValidThread());
110 return observer_->stream().get();
113 MediaStreamTrackMetrics::StreamType stream_type() {
114 DCHECK(thread_checker_.CalledOnValidThread());
115 return stream_type_;
118 private:
119 void OnChanged(const IdSet& audio_track_ids, const IdSet& video_track_ids);
121 void ReportAddedAndRemovedTracks(
122 const IdSet& new_ids,
123 const IdSet& old_ids,
124 MediaStreamTrackMetrics::TrackType track_type);
126 // Sends a lifetime message for the given tracks. OK to call with an
127 // empty |ids|, in which case the method has no side effects.
128 void ReportTracks(const IdSet& ids,
129 MediaStreamTrackMetrics::TrackType track_type,
130 MediaStreamTrackMetrics::LifetimeEvent event);
132 // False until start/end of lifetime messages have been sent.
133 bool has_reported_start_;
134 bool has_reported_end_;
136 // IDs of audio and video tracks in the stream being observed.
137 IdSet audio_track_ids_;
138 IdSet video_track_ids_;
140 MediaStreamTrackMetrics::StreamType stream_type_;
141 scoped_refptr<MediaStreamObserver> observer_;
143 // Non-owning.
144 MediaStreamTrackMetrics* owner_;
145 base::ThreadChecker thread_checker_;
148 namespace {
150 // Used with std::find_if.
151 struct ObserverFinder {
152 ObserverFinder(MediaStreamTrackMetrics::StreamType stream_type,
153 MediaStreamInterface* stream)
154 : stream_type(stream_type), stream_(stream) {}
155 bool operator()(MediaStreamTrackMetricsObserver* observer) {
156 return stream_ == observer->stream() &&
157 stream_type == observer->stream_type();
159 MediaStreamTrackMetrics::StreamType stream_type;
160 MediaStreamInterface* stream_;
163 } // namespace
165 MediaStreamTrackMetricsObserver::MediaStreamTrackMetricsObserver(
166 MediaStreamTrackMetrics::StreamType stream_type,
167 MediaStreamInterface* stream,
168 MediaStreamTrackMetrics* owner)
169 : has_reported_start_(false),
170 has_reported_end_(false),
171 audio_track_ids_(GetTrackIds(stream->GetAudioTracks())),
172 video_track_ids_(GetTrackIds(stream->GetVideoTracks())),
173 stream_type_(stream_type),
174 observer_(new MediaStreamObserver(
175 base::Bind(&MediaStreamTrackMetricsObserver::OnChanged,
176 base::Unretained(this)),
177 base::ThreadTaskRunnerHandle::Get(),
178 stream)),
179 owner_(owner) {
182 MediaStreamTrackMetricsObserver::~MediaStreamTrackMetricsObserver() {
183 DCHECK(thread_checker_.CalledOnValidThread());
184 observer_->Unregister();
185 SendLifetimeMessages(MediaStreamTrackMetrics::DISCONNECTED);
188 void MediaStreamTrackMetricsObserver::SendLifetimeMessages(
189 MediaStreamTrackMetrics::LifetimeEvent event) {
190 DCHECK(thread_checker_.CalledOnValidThread());
191 if (event == MediaStreamTrackMetrics::CONNECTED) {
192 // Both ICE CONNECTED and COMPLETED can trigger the first
193 // start-of-life event, so we only report the first.
194 if (has_reported_start_)
195 return;
196 DCHECK(!has_reported_start_ && !has_reported_end_);
197 has_reported_start_ = true;
198 } else {
199 DCHECK(event == MediaStreamTrackMetrics::DISCONNECTED);
201 // We only report the first end-of-life event, since there are
202 // several cases where end-of-life can be reached. We also don't
203 // report end unless we've reported start.
204 if (has_reported_end_ || !has_reported_start_)
205 return;
206 has_reported_end_ = true;
209 ReportTracks(audio_track_ids_, MediaStreamTrackMetrics::AUDIO_TRACK, event);
210 ReportTracks(video_track_ids_, MediaStreamTrackMetrics::VIDEO_TRACK, event);
212 if (event == MediaStreamTrackMetrics::DISCONNECTED) {
213 // After disconnection, we can get reconnected, so we need to
214 // forget that we've sent lifetime events, while retaining all
215 // other state.
216 DCHECK(has_reported_start_ && has_reported_end_);
217 has_reported_start_ = false;
218 has_reported_end_ = false;
222 void MediaStreamTrackMetricsObserver::OnChanged(
223 const IdSet& audio_track_ids, const IdSet& video_track_ids) {
224 DCHECK(thread_checker_.CalledOnValidThread());
226 // We only report changes after our initial report, and never after
227 // our last report.
228 if (has_reported_start_ && !has_reported_end_) {
229 ReportAddedAndRemovedTracks(audio_track_ids,
230 audio_track_ids_,
231 MediaStreamTrackMetrics::AUDIO_TRACK);
232 ReportAddedAndRemovedTracks(video_track_ids,
233 video_track_ids_,
234 MediaStreamTrackMetrics::VIDEO_TRACK);
237 // We always update our sets of tracks.
238 audio_track_ids_ = audio_track_ids;
239 video_track_ids_ = video_track_ids;
242 void MediaStreamTrackMetricsObserver::ReportAddedAndRemovedTracks(
243 const IdSet& new_ids,
244 const IdSet& old_ids,
245 MediaStreamTrackMetrics::TrackType track_type) {
246 DCHECK(thread_checker_.CalledOnValidThread());
247 DCHECK(has_reported_start_ && !has_reported_end_);
249 IdSet added_tracks = base::STLSetDifference<IdSet>(new_ids, old_ids);
250 IdSet removed_tracks = base::STLSetDifference<IdSet>(old_ids, new_ids);
252 ReportTracks(added_tracks, track_type, MediaStreamTrackMetrics::CONNECTED);
253 ReportTracks(
254 removed_tracks, track_type, MediaStreamTrackMetrics::DISCONNECTED);
257 void MediaStreamTrackMetricsObserver::ReportTracks(
258 const IdSet& ids,
259 MediaStreamTrackMetrics::TrackType track_type,
260 MediaStreamTrackMetrics::LifetimeEvent event) {
261 DCHECK(thread_checker_.CalledOnValidThread());
262 for (IdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
263 owner_->SendLifetimeMessage(*it, track_type, event, stream_type_);
267 MediaStreamTrackMetrics::MediaStreamTrackMetrics()
268 : ice_state_(webrtc::PeerConnectionInterface::kIceConnectionNew) {}
270 MediaStreamTrackMetrics::~MediaStreamTrackMetrics() {
271 for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
272 ++it) {
273 (*it)->SendLifetimeMessages(DISCONNECTED);
277 void MediaStreamTrackMetrics::AddStream(StreamType type,
278 MediaStreamInterface* stream) {
279 DCHECK(CalledOnValidThread());
280 MediaStreamTrackMetricsObserver* observer =
281 new MediaStreamTrackMetricsObserver(type, stream, this);
282 observers_.insert(observers_.end(), observer);
283 SendLifeTimeMessageDependingOnIceState(observer);
286 void MediaStreamTrackMetrics::RemoveStream(StreamType type,
287 MediaStreamInterface* stream) {
288 DCHECK(CalledOnValidThread());
289 ObserverVector::iterator it = std::find_if(
290 observers_.begin(), observers_.end(), ObserverFinder(type, stream));
291 if (it == observers_.end()) {
292 // Since external apps could call removeStream with a stream they
293 // never added, this can happen without it being an error.
294 return;
297 observers_.erase(it);
300 void MediaStreamTrackMetrics::IceConnectionChange(
301 PeerConnectionInterface::IceConnectionState new_state) {
302 DCHECK(CalledOnValidThread());
303 ice_state_ = new_state;
304 for (ObserverVector::iterator it = observers_.begin(); it != observers_.end();
305 ++it) {
306 SendLifeTimeMessageDependingOnIceState(*it);
309 void MediaStreamTrackMetrics::SendLifeTimeMessageDependingOnIceState(
310 MediaStreamTrackMetricsObserver* observer) {
311 // There is a state transition diagram for these states at
312 // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCIceConnectionState
313 switch (ice_state_) {
314 case PeerConnectionInterface::kIceConnectionConnected:
315 case PeerConnectionInterface::kIceConnectionCompleted:
316 observer->SendLifetimeMessages(CONNECTED);
317 break;
319 case PeerConnectionInterface::kIceConnectionFailed:
320 // We don't really need to handle FAILED (it is only supposed
321 // to be preceded by CHECKING so we wouldn't yet have sent a
322 // lifetime message) but we might as well use belt and
323 // suspenders and handle it the same as the other "end call"
324 // states. It will be ignored anyway if the call is not
325 // already connected.
326 case PeerConnectionInterface::kIceConnectionNew:
327 // It's a bit weird to count NEW as an end-lifetime event, but
328 // it's possible to transition directly from a connected state
329 // (CONNECTED or COMPLETED) to NEW, which can then be followed
330 // by a new connection. The observer will ignore the end
331 // lifetime event if it was not preceded by a begin-lifetime
332 // event.
333 case PeerConnectionInterface::kIceConnectionDisconnected:
334 case PeerConnectionInterface::kIceConnectionClosed:
335 observer->SendLifetimeMessages(DISCONNECTED);
336 break;
338 default:
339 // We ignore the remaining state (CHECKING) as it is never
340 // involved in a transition from connected to disconnected or
341 // vice versa.
342 break;
346 void MediaStreamTrackMetrics::SendLifetimeMessage(const std::string& track_id,
347 TrackType track_type,
348 LifetimeEvent event,
349 StreamType stream_type) {
350 RenderThreadImpl* render_thread = RenderThreadImpl::current();
351 // |render_thread| can be NULL in certain cases when running as part
352 // |of a unit test.
353 if (render_thread) {
354 if (event == CONNECTED) {
355 RenderThreadImpl::current()->Send(
356 new MediaStreamTrackMetricsHost_AddTrack(
357 MakeUniqueId(track_id, stream_type),
358 track_type == AUDIO_TRACK,
359 stream_type == RECEIVED_STREAM));
360 } else {
361 DCHECK_EQ(DISCONNECTED, event);
362 RenderThreadImpl::current()->Send(
363 new MediaStreamTrackMetricsHost_RemoveTrack(
364 MakeUniqueId(track_id, stream_type)));
369 uint64 MediaStreamTrackMetrics::MakeUniqueIdImpl(uint64 pc_id,
370 const std::string& track_id,
371 StreamType stream_type) {
372 // We use a hash over the |track| pointer and the PeerConnection ID,
373 // plus a boolean flag indicating whether the track is remote (since
374 // you might conceivably have a remote track added back as a sent
375 // track) as the unique ID.
377 // We don't need a cryptographically secure hash (which MD5 should
378 // no longer be considered), just one with virtually zero chance of
379 // collisions when faced with non-malicious data.
380 std::string unique_id_string =
381 base::StringPrintf("%" PRIu64 " %s %d",
382 pc_id,
383 track_id.c_str(),
384 stream_type == RECEIVED_STREAM ? 1 : 0);
386 base::MD5Context ctx;
387 base::MD5Init(&ctx);
388 base::MD5Update(&ctx, unique_id_string);
389 base::MD5Digest digest;
390 base::MD5Final(&digest, &ctx);
392 static_assert(sizeof(digest.a) > sizeof(uint64), "need a bigger digest");
393 return *reinterpret_cast<uint64*>(digest.a);
396 uint64 MediaStreamTrackMetrics::MakeUniqueId(const std::string& track_id,
397 StreamType stream_type) {
398 return MakeUniqueIdImpl(
399 reinterpret_cast<uint64>(reinterpret_cast<void*>(this)),
400 track_id,
401 stream_type);
404 } // namespace content