File: cross_thread_media_source_attachment.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 (297 lines) | stat: -rw-r--r-- 16,193 bytes parent folder | download | duplicates (10)
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_

#include <memory>

#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "base/types/pass_key.h"
#include "third_party/blink/public/platform/web_time_range.h"
#include "third_party/blink/renderer/core/html/track/audio_track.h"
#include "third_party/blink/renderer/core/html/track/audio_track_list.h"
#include "third_party/blink/renderer/core/html/track/video_track.h"
#include "third_party/blink/renderer/core/html/track/video_track_list.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_attachment_supplement.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"

namespace blink {

// Concrete attachment that supports operation between a media element on the
// main thread and the MSE API on a dedicated worker thread.
class CrossThreadMediaSourceAttachment final
    : public MediaSourceAttachmentSupplement {
 public:
  // For use by Remove{Audio,Video}TracksFromMediaElements' and
  // AddMainThread{Audio,Video}TrackToMediaElements' internal helpers.
  enum class TrackAddRemovalType { kAudio, kVideo };

  // The only intended callers of this constructor are restricted to those able
  // to obtain an AttachmentCreationPasskeyProvider's pass key. This method is
  // expected to only be called in a worker thread context. The raw pointer is
  // then adopted into a scoped_refptr by the caller (e.g.,
  // URLMediaSource::createObjectUrl will lead to
  // MediaSourceRegistryImpl::RegisterURL doing this scoped_refptr adoption;
  // separately, MediaSource::handle() does this adoption immediately.)
  CrossThreadMediaSourceAttachment(MediaSource* media_source,
                                   AttachmentCreationPassKeyProvider::PassKey);

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

  // MediaSourceAttachmentSupplement, called by MSE API on worker thread.
  // These generally require the MSE implementation to issue these calls from
  // the target of a RunExclusively() callback to ensure thread safety: much of
  // the MSE API implementation (such as readyState, and knowing whether or not
  // the underlying WebSourceBuffers and WebMediaSource are still usable, since
  // they access a main-thread-owned demuxer) needs to use RunExclusively().
  // Meanwhile, the main thread element could cause changes to such state (via
  // this attachment) and therefore also require exclusion using the same
  // |attachment_state_lock_|.
  void NotifyDurationChanged(MediaSourceTracer* tracer, double duration) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  base::TimeDelta GetRecentMediaTime(MediaSourceTracer* tracer) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  bool GetElementError(MediaSourceTracer* tracer) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  AudioTrackList* CreateAudioTrackList(MediaSourceTracer* tracer) final;
  VideoTrackList* CreateVideoTrackList(MediaSourceTracer* tracer) final;
  void AddAudioTrackToMediaElement(MediaSourceTracer* tracer,
                                   AudioTrack* track) final;
  void AddVideoTrackToMediaElement(MediaSourceTracer* tracer,
                                   VideoTrack* track) final;
  void RemoveAudioTracksFromMediaElement(MediaSourceTracer* tracer,
                                         Vector<String> audio_ids,
                                         bool enqueue_change_event) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void RemoveVideoTracksFromMediaElement(MediaSourceTracer* tracer,
                                         Vector<String> video_ids,
                                         bool enqueue_change_event) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void AddMainThreadAudioTrackToMediaElement(String id,
                                             String kind,
                                             String label,
                                             String language,
                                             bool enabled) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void AddMainThreadVideoTrackToMediaElement(String id,
                                             String kind,
                                             String label,
                                             String language,
                                             bool selected) final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void OnMediaSourceContextDestroyed() final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

  // Method meant to protect MSE API operations on worker thread MSE API from
  // colliding with concurrently executing operations from the main thread
  // running in this attachment. If |abort_if_not_fully_attached| is true, then
  // conditionally runs |cb| iff the media element is still attached and has not
  // ever issued a Close operation on this attachment, and if those conditions
  // fail, |cb| is not run and this method returns false. If
  // |abort_if_not_fully_attached| is false, then unconditionally runs |cb| and
  // returns true. If run, |cb| is run synchronously while holding the
  // attachment's internal |attachment_state_lock_|. Any return values needed by
  // the caller from |cb| should be passed by pointer, enabling usage of this
  // helper to provide safety while still retaining synchronous worker-thread
  // MSE API operation.
  bool RunExclusively(bool abort_if_not_fully_attached,
                      RunExclusivelyCB cb) final
      LOCKS_EXCLUDED(attachment_state_lock_);

  // See MediaSourceAttachmentSupplement for details. Simply, if this returns
  // true, then SourceBuffer::RemovedFromMediaSource() can safely access the
  // underlying demuxer, so long as the |attachment_state_lock_| is held
  // continuously throughout this call and such accesses.
  bool FullyAttachedOrSameThread(SourceBufferPassKey) const final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

  // MediaSourceAttachment methods called on main thread by media element,
  // except Unregister is called on either main or dedicated worker thread by
  // MediaSourceRegistryImpl.
  void Unregister() final;
  MediaSourceTracer* StartAttachingToMediaElement(HTMLMediaElement*,
                                                  bool* success) final
      LOCKS_EXCLUDED(attachment_state_lock_);
  void CompleteAttachingToMediaElement(MediaSourceTracer* tracer,
                                       std::unique_ptr<WebMediaSource>) final
      LOCKS_EXCLUDED(attachment_state_lock_);

  void Close(MediaSourceTracer* tracer) final
      LOCKS_EXCLUDED(attachment_state_lock_);

  WebTimeRanges BufferedInternal(MediaSourceTracer* tracer) const final
      LOCKS_EXCLUDED(attachment_state_lock_);

  WebTimeRanges SeekableInternal(MediaSourceTracer* tracer) const final
      LOCKS_EXCLUDED(attachment_state_lock_);
  void OnTrackChanged(MediaSourceTracer* tracer, TrackBase*) final
      LOCKS_EXCLUDED(attachment_state_lock_);

  void OnElementTimeUpdate(double time) final
      LOCKS_EXCLUDED(attachment_state_lock_);
  void OnElementError() final LOCKS_EXCLUDED(attachment_state_lock_);

  void OnElementContextDestroyed() final LOCKS_EXCLUDED(attachment_state_lock_);

  void AssertCrossThreadMutexIsAcquiredForDebugging() final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

  void SendUpdatedInfoToMainThreadCache() final
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

 private:
  ~CrossThreadMediaSourceAttachment() override;

  void RemoveTracksFromMediaElementInternal(TrackAddRemovalType track_type,
                                            Vector<String> track_ids,
                                            bool enqueue_change_event)
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void RemoveTracksFromMediaElementOnMainThread(TrackAddRemovalType track_type,
                                                Vector<String> track_ids,
                                                bool enqueue_change_event)
      LOCKS_EXCLUDED(attachment_state_lock_);

  void AddTrackToMediaElementInternal(TrackAddRemovalType track_type,
                                      String id,
                                      String kind,
                                      String label,
                                      String language,
                                      bool enable_or_select)
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);
  void AddTrackToMediaElementOnMainThread(TrackAddRemovalType track_type,
                                          String id,
                                          String kind,
                                          String label,
                                          String language,
                                          bool enable_or_select)
      LOCKS_EXCLUDED(attachment_state_lock_);

  void CompleteAttachingToMediaElementOnWorkerThread(
      std::unique_ptr<WebMediaSource> web_media_source)
      LOCKS_EXCLUDED(attachment_state_lock_);

  void CloseOnWorkerThread() LOCKS_EXCLUDED(attachment_state_lock_);

  void UpdateWorkerThreadTimeCache(base::TimeDelta time)
      LOCKS_EXCLUDED(attachment_state_lock_);
  void HandleElementErrorOnWorkerThread()
      LOCKS_EXCLUDED(attachment_state_lock_);

  void SendUpdatedInfoToMainThreadCacheInternal(bool has_new_duration,
                                                double new_duration)
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

  void UpdateMainThreadInfoCache(WebTimeRanges new_buffered,
                                 WebTimeRanges new_seekable,
                                 bool has_new_duration,
                                 double new_duration)
      LOCKS_EXCLUDED(attachment_state_lock_);

  // In this cross-thread implementation, this helper is used to verify
  // assumption of "liveness" of the attachment while the caller holds
  // |attachment_state_lock_| for common operations.
  void VerifyCalledWhileContextsAliveForDebugging() const
      EXCLUSIVE_LOCKS_REQUIRED(attachment_state_lock_);

  mutable base::Lock attachment_state_lock_;

  // Cache of the registered worker-thread MediaSource. Retains strong reference
  // on all Oilpan heaps, from construction of this object until Unregister() is
  // called. This lets the main thread successfully attach (modulo normal
  // reasons why StartAttaching..() can fail) to the worker-thread MediaSource
  // even if there were no other strong references other than this one on the
  // worker-thread Oilpan heap to the MediaSource.
  CrossThreadPersistent<MediaSource> registered_media_source_
      GUARDED_BY(attachment_state_lock_);

  // Task runner for posting cross-context information from the main thread to
  // the dedicated worker worker thread that owns |registered_media_source_|
  // (and |attached_media_source_| if currently attached). Rather than using
  // kMediaElementEvent task-type for this, we use kPostedMessage to have
  // similar scheduling priority as if the app instead postMessage'd the
  // information across context while maintaining similar causality. This is
  // used for servicing main thread element operations that require operation in
  // the MSE thread context. This is only valid until
  // |media_source_context_destroyed_| becomes true.
  scoped_refptr<base::SingleThreadTaskRunner> worker_runner_
      GUARDED_BY(attachment_state_lock_);

  // Task runner for posting cross-context information from the worker thread to
  // the main thread that owns |attached_element_| when currently attached.
  // Rather than using kMediaElementEvent task-type for this, we use
  // kPostedMessage to have similar scheduling priority as if the app instead
  // postMessage'd the information across context while maintaining similar
  // causality. This is used for servicing worker thread operations that require
  // operation in the main thread context. This is only valid until
  // |media_element_context_destroyed_| becomes true.
  scoped_refptr<base::SingleThreadTaskRunner> main_runner_
      GUARDED_BY(attachment_state_lock_);

  // In addition to serving as targets for cross-thread communication during a
  // live attachment, these two members function to keep Oilpan GC from
  // collecting either side of the cross-thread attached HTMLME+MSE object group
  // until explicit detachment. Unlike same-thread attachment's usage of Member
  // tracing to detect idle unused attached groups, cross-thread idle detection
  // is not available due to Oilpan's lack of CrossThreadMember.
  CrossThreadPersistent<MediaSource> attached_media_source_
      GUARDED_BY(attachment_state_lock_);
  CrossThreadPersistent<HTMLMediaElement> attached_element_
      GUARDED_BY(attachment_state_lock_);

  bool media_source_context_destroyed_ GUARDED_BY(attachment_state_lock_);
  bool media_element_context_destroyed_ GUARDED_BY(attachment_state_lock_);

  // Updated on worker thread as eventual result of kPostMessage-ing the time
  // received in OnElementTimeUpdate() on the main thread. Read on worker thread
  // synchronous to servicing GetRecentMediaTime().
  // See MediaSourceAttachment::OnElementTimeUpdate() interface comments for
  // more detail.
  base::TimeDelta recent_element_time_ GUARDED_BY(attachment_state_lock_);

  // Updated on worker thread as eventual result of kPostMessage-ing the
  // notification of element error received in OnElementError() on the main
  // thread. Read on worker thread synchronous to servicing GetElementError().
  // See MediaSourceAttachment::OnElementError() interface comments for more
  // detail.
  bool element_has_error_ GUARDED_BY(attachment_state_lock_);

  // TODO(https://crbug.com/878133): Handle supporting attachment-start success
  // even if RevokeMediaSourceObjectURLOnAttach is *not* enabled, and this
  // attachment instance (== object URL) is used sequentially for multiple
  // attachment lifetimes. Solution could be to ship that feature always-on soon
  // (making this scenario unsupported also in same-thread), or instead use a
  // counter here and in any cross-thread attachment task posting to ensure that
  // the cross-thread posted task is meant for the current attachment lifetime.
  // For now, this flag is used to prevent sequential usage of this attachment
  // instance to avoid this problem. (MSE-in-Workers is experimental, and
  // RevokeMediaSourceObjectURLOnAttach is on-by-default.)
  bool have_ever_attached_ GUARDED_BY(attachment_state_lock_);

  // TODO(https://crbug.com/878133): Similarly to |have_ever_attached_|, this
  // member becomes true once this attachment receives a Close() call. Some
  // operations, such as preventing usage of the underlying demuxer, or
  // nullifying cross-thread track notifications, need to know immediately in
  // the worker context once the asynchronous Close() call has ever occurred. If
  // support for sequential multiple attachment lifetimes is needed (for
  // instance, if MSE-in-Workers support is needed when
  // RevokeMediaSourceObjectURLOnAttach is *not* enabled), then a counter-based
  // solution may be required instead of this flag.
  bool have_ever_started_closing_ GUARDED_BY(attachment_state_lock_);

  WebTimeRanges cached_buffered_;
  WebTimeRanges cached_seekable_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_