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_