File: media_engagement_contents_observer.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (201 lines) | stat: -rw-r--r-- 7,706 bytes parent folder | download | duplicates (6)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_MEDIA_MEDIA_ENGAGEMENT_CONTENTS_OBSERVER_H_
#define CHROME_BROWSER_MEDIA_MEDIA_ENGAGEMENT_CONTENTS_OBSERVER_H_

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/web_contents_observer.h"

namespace base {
class Clock;
}  // namespace base

namespace gfx {
class Size;
}  // namespace gfx

class MediaEngagementContentsObserverTest;
class MediaEngagementService;
class MediaEngagementSession;

class MediaEngagementContentsObserver : public content::WebContentsObserver {
 public:
  MediaEngagementContentsObserver(const MediaEngagementContentsObserver&) =
      delete;
  MediaEngagementContentsObserver& operator=(
      const MediaEngagementContentsObserver&) = delete;

  ~MediaEngagementContentsObserver() override;

  // WebContentsObserver implementation.
  void WebContentsDestroyed() override;
  void DidFinishNavigation(
      content::NavigationHandle* navigation_handle) override;
  void ReadyToCommitNavigation(
      content::NavigationHandle* navigation_handle) override;
  void MediaStartedPlaying(
      const MediaPlayerInfo& media_player_info,
      const content::MediaPlayerId& media_player_id) override;
  void MediaStoppedPlaying(
      const MediaPlayerInfo& media_player_info,
      const content::MediaPlayerId& media_player_id,
      WebContentsObserver::MediaStoppedReason reason) override;
  void DidUpdateAudioMutingState(bool muted) override;
  void MediaMutedStatusChanged(const content::MediaPlayerId& id,
                               bool muted) override;
  void MediaResized(const gfx::Size& size,
                    const content::MediaPlayerId& id) override;
  void MediaDestroyed(const content::MediaPlayerId& id) override;
  void AudioContextPlaybackStarted(
      const AudioContextId& audio_context_id) override;
  void AudioContextPlaybackStopped(
      const AudioContextId& audio_context_id) override;

  static const gfx::Size kSignificantSize;

 private:
  FRIEND_TEST_ALL_PREFIXES(MediaEngagementContentsObserverTest,
                           RecordInsignificantReason);
  FRIEND_TEST_ALL_PREFIXES(MediaEngagementContentsObserverTest,
                           RecordInsignificantReason_NotAdded_AfterFirstTime);
  // Only MediaEngagementService can create a MediaEngagementContentsObserver.
  friend MediaEngagementService;
  friend MediaEngagementContentsObserverTest;
  friend class MediaEngagementBrowserTest;

  MediaEngagementContentsObserver(content::WebContents* web_contents,
                                  MediaEngagementService* service);

  // This is the maximum playback time for media to be considered 'short'.
  static const base::TimeDelta kMaxShortPlaybackTime;

  void OnSignificantMediaPlaybackTimeForPlayer(
      const content::MediaPlayerId& id);
  void OnSignificantMediaPlaybackTimeForPage();
  void OnSignificantAudioContextPlaybackTimeForPage();

  void UpdatePlayerTimer(const content::MediaPlayerId&);
  void UpdatePageTimer();
  void UpdateAudioContextTimer();

  bool AreConditionsMet() const;
  bool AreAudioContextConditionsMet() const;

  void SetTaskRunnerForTest(scoped_refptr<base::SequencedTaskRunner>);

  // |this| is owned by |service_|.
  raw_ptr<MediaEngagementService> service_;

  // Timer that will fire when the playback time reaches the minimum for
  // significant media playback.
  base::OneShotTimer playback_timer_;

  // Set of active players that can produce a significant playback. In other
  // words, whether this set is empty can be used to know if there is a
  // significant playback.
  std::set<content::MediaPlayerId> significant_players_;

  // Timer that will fire when the playback time of any audio context reaches
  // the minimum for significant media playback.
  base::OneShotTimer audio_context_timer_;

  // Set of active audio contexts that can produce a significant playback.
  std::set<AudioContextId> audio_context_players_;

  // Measures playback time for a player.
  class PlaybackTimer {
   public:
    explicit PlaybackTimer(base::Clock*);

    PlaybackTimer(const PlaybackTimer&) = delete;
    PlaybackTimer& operator=(const PlaybackTimer&) = delete;

    ~PlaybackTimer();

    void Start();
    void Stop();
    bool IsRunning() const;
    base::TimeDelta Elapsed() const;
    void Reset();

   private:
    // The clock is owned by |service_| which already owns |this|.
    raw_ptr<base::Clock> clock_;

    std::optional<base::Time> start_time_;
    base::TimeDelta recorded_time_;
  };

  // A structure containing all the information we have about a player's state.
  struct PlayerState {
    explicit PlayerState(base::Clock*);

    PlayerState(const PlayerState&) = delete;
    PlayerState& operator=(const PlayerState&) = delete;

    PlayerState(PlayerState&&);

    ~PlayerState();

    std::optional<bool> muted;
    std::optional<bool> playing;           // Currently playing.
    std::optional<bool> significant_size;  // The video track has at least
                                           // a certain frame size.
    std::optional<bool> has_audio;         // The media has an audio track.
    std::optional<bool> has_video;         // The media has a video track.

    bool reached_end_of_stream = false;
    std::unique_ptr<PlaybackTimer> playback_timer;
  };
  std::map<content::MediaPlayerId, PlayerState> player_states_;
  PlayerState& GetPlayerState(const content::MediaPlayerId& id);
  void ClearPlayerStates();

  // Inserts/removes players from significant_players_ based on whether
  // they are considered significant by GetInsignificantPlayerReason.
  void MaybeInsertRemoveSignificantPlayer(const content::MediaPlayerId& id);

  // Returns whether the player with |id| is considered significant.
  bool IsSignificantPlayer(const content::MediaPlayerId& id);

  // Returns whether we have recieved all the state information about a
  // player in order to be able to make a decision about it.
  bool IsPlayerStateComplete(const PlayerState& state);

  static const base::TimeDelta kSignificantMediaPlaybackTime;

  // Clears out players that are ignored because they are too short and register
  // the result as significant/audible players with the `session_`.
  void RegisterAudiblePlayersWithSession();

  // Returns the opener of the current WebContents. Null if there is none.
  content::WebContents* GetOpener() const;

  // Find the appropriate media engagement session if any or create a new one to
  // be used. Will return nullptr if no session should be used.
  scoped_refptr<MediaEngagementSession> GetOrCreateSession(
      content::NavigationHandle* navigation_handle,
      content::WebContents* opener) const;

  // Stores the ids of the players that were audible. The boolean will be true
  // if the player was significant.
  using AudiblePlayerRow = std::pair<bool, std::unique_ptr<base::OneShotTimer>>;
  std::map<content::MediaPlayerId, AudiblePlayerRow> audible_players_;

  // The task runner to use when creating timers. It is used only for testing.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  // The MediaEngagementSession used by this MediaEngagementContentsObserver. It
  // may be shared by other instances if they are part of the same session. It
  // willl be null if it is not part of a session.
  scoped_refptr<MediaEngagementSession> session_;
};

#endif  // CHROME_BROWSER_MEDIA_MEDIA_ENGAGEMENT_CONTENTS_OBSERVER_H_