1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
content / browser / media / audio_stream_monitor.h [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_MEDIA_AUDIO_STREAM_MONITOR_H_
#define CONTENT_BROWSER_MEDIA_AUDIO_STREAM_MONITOR_H_
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
class WebContents;
// Keeps track of the audible state of audio output streams and uses it to
// maintain a "was recently audible" binary state for the audio indicators in
// the tab UI. The logic is to: 1) Turn on immediately when sound is audible;
// and 2) Hold on for X amount of time after sound has gone silent, then turn
// off if no longer audible. Said another way, we don't want tab indicators to
// turn on/off repeatedly and annoy the user. AudioStreamMonitor sends UI
// update notifications only when needed, but may be queried at any time.
//
// When monitoring is not available, audibility is approximated with having
// active audio streams.
//
// Each WebContentsImpl owns an AudioStreamMonitor.
class CONTENT_EXPORT AudioStreamMonitor : public WebContentsObserver {
public:
explicit AudioStreamMonitor(WebContents* contents);
AudioStreamMonitor(const AudioStreamMonitor&) = delete;
AudioStreamMonitor& operator=(const AudioStreamMonitor&) = delete;
~AudioStreamMonitor() override;
// Returns true if audio has recently been audible from the tab. This is
// usually called whenever the tab data model is refreshed; but there are
// other use cases as well (e.g., the OOM killer uses this to de-prioritize
// the killing of tabs making sounds).
bool WasRecentlyAudible() const;
// Returns true if the audio is currently audible from the given WebContents.
// The difference from WasRecentlyAudible() is that this method will return
// false as soon as the WebContents stop producing sound.
bool IsCurrentlyAudible() const;
// Called by the WebContentsImpl if |render_process_id| dies; used to clear
// any outstanding poll callbacks.
void RenderProcessGone(int render_process_id);
// Starts or stops monitoring respectively for the stream owned by the
// specified renderer. Safe to call from any thread.
static void StartMonitoringStream(
GlobalRenderFrameHostId render_frame_host_id,
int stream_id);
static void StopMonitoringStream(GlobalRenderFrameHostId render_frame_host_id,
int stream_id);
// Updates the audible state for the given stream. Safe to call from any
// thread.
static void UpdateStreamAudibleState(
GlobalRenderFrameHostId render_frame_host_id,
int stream_id,
bool is_audible);
// WebContentsObserver implementation
void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
// Overloaded to avoid conflict with RenderProcessGone(int).
void PrimaryMainFrameRenderProcessGone(
base::TerminationStatus status) override {}
void set_was_recently_audible_for_testing(bool value) {
indicator_is_on_ = value;
}
void set_is_currently_audible_for_testing(bool value) { is_audible_ = value; }
// Class to help automatically remove audible client.
class CONTENT_EXPORT AudibleClientRegistration {
public:
AudibleClientRegistration(GlobalRenderFrameHostId render_frame_host_id,
AudioStreamMonitor* audio_stream_monitor);
~AudibleClientRegistration();
private:
GlobalRenderFrameHostId render_frame_host_id_;
raw_ptr<AudioStreamMonitor> audio_stream_monitor_;
};
// Registers an audible client, which will be unregistered when the returned
// AudibleClientRegistration is released.
std::unique_ptr<AudibleClientRegistration> RegisterAudibleClient(
GlobalRenderFrameHostId render_frame_host_id);
private:
friend class AudioStreamMonitorTest;
friend class AudibleClientRegistration;
enum {
// Minimum amount of time to hold a tab indicator on after it becomes
// silent.
kHoldOnMilliseconds = 2000
};
struct CONTENT_EXPORT StreamID {
GlobalRenderFrameHostId render_frame_host_id;
int stream_id;
bool operator<(const StreamID& other) const;
bool operator==(const StreamID& other) const;
};
// Starts monitoring the audible state for the given stream.
void StartMonitoringStreamOnUIThread(const StreamID& sid);
// Stops monitoring the audible state for the given stream.
void StopMonitoringStreamOnUIThread(const StreamID& sid);
// Updates the audible state for the given stream.
void UpdateStreamAudibleStateOnUIThread(const StreamID& sid, bool is_audible);
// Compares last known indicator state with what it should be, and triggers UI
// updates through |web_contents_| if needed. When the indicator is turned
// on, |off_timer_| is started to re-invoke this method in the future.
void MaybeToggle();
void UpdateStreams();
// Adds/Removes Audible clients.
void AddAudibleClient(GlobalRenderFrameHostId render_frame_host_id);
void RemoveAudibleClient(GlobalRenderFrameHostId render_frame_host_id);
// The WebContents instance to receive indicator toggle notifications. This
// pointer should be valid for the lifetime of AudioStreamMonitor.
const raw_ptr<WebContents> web_contents_;
// Confirms single-threaded access in debug builds.
base::ThreadChecker thread_checker_;
// The audible state for each stream. Only playing (i.e., not paused)
// streams will have an entry in this map.
base::flat_map<StreamID, bool> streams_;
// Map of non-stream audible clients, e.g. players not using AudioServices.
// size_t is the number of audible clients associated with the
// GlobalRenderFrameHostId. If size_t count reaches 0 there are no
// remaining audible clients for the associated host id.
base::flat_map<GlobalRenderFrameHostId, size_t> audible_clients_;
// Records the last time at which all streams became silent.
base::TimeTicks last_became_silent_time_;
// Set to true if the last call to MaybeToggle() determined the indicator
// should be turned on.
bool indicator_is_on_ = false;
// Whether the WebContents is currently audible.
bool is_audible_ = false;
// Started only when an indicator is toggled on, to turn it off again in the
// future.
base::OneShotTimer off_timer_;
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_AUDIO_STREAM_MONITOR_H_