/work/obj-fuzz/dist/include/MediaStreamGraph.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
4 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #ifndef MOZILLA_MEDIASTREAMGRAPH_H_ |
7 | | #define MOZILLA_MEDIASTREAMGRAPH_H_ |
8 | | |
9 | | #include "AudioStream.h" |
10 | | #include "MainThreadUtils.h" |
11 | | #include "MediaStreamTypes.h" |
12 | | #include "StreamTracks.h" |
13 | | #include "VideoSegment.h" |
14 | | #include "mozilla/LinkedList.h" |
15 | | #include "mozilla/Maybe.h" |
16 | | #include "mozilla/Mutex.h" |
17 | | #include "mozilla/TaskQueue.h" |
18 | | #include "nsAutoPtr.h" |
19 | | #include "nsAutoRef.h" |
20 | | #include "nsIRunnable.h" |
21 | | #include "nsTArray.h" |
22 | | #include <speex/speex_resampler.h> |
23 | | |
24 | | class nsIRunnable; |
25 | | class nsIGlobalObject; |
26 | | class nsPIDOMWindowInner; |
27 | | |
28 | | namespace mozilla { |
29 | | class AsyncLogger; |
30 | | }; |
31 | | |
32 | | extern mozilla::AsyncLogger gMSGTraceLogger; |
33 | | |
34 | | |
35 | | template <> |
36 | | class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState> |
37 | | { |
38 | | public: |
39 | 0 | static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); } |
40 | | }; |
41 | | |
42 | | namespace mozilla { |
43 | | |
44 | | extern LazyLogModule gMediaStreamGraphLog; |
45 | | |
46 | | namespace dom { |
47 | | enum class AudioContextOperation; |
48 | | } |
49 | | |
50 | | namespace media { |
51 | | template<typename V, typename E> class Pledge; |
52 | | } |
53 | | |
54 | | /* |
55 | | * MediaStreamGraph is a framework for synchronized audio/video processing |
56 | | * and playback. It is designed to be used by other browser components such as |
57 | | * HTML media elements, media capture APIs, real-time media streaming APIs, |
58 | | * multitrack media APIs, and advanced audio APIs. |
59 | | * |
60 | | * The MediaStreamGraph uses a dedicated thread to process media --- the media |
61 | | * graph thread. This ensures that we can process media through the graph |
62 | | * without blocking on main-thread activity. The media graph is only modified |
63 | | * on the media graph thread, to ensure graph changes can be processed without |
64 | | * interfering with media processing. All interaction with the media graph |
65 | | * thread is done with message passing. |
66 | | * |
67 | | * APIs that modify the graph or its properties are described as "control APIs". |
68 | | * These APIs are asynchronous; they queue graph changes internally and |
69 | | * those changes are processed all-at-once by the MediaStreamGraph. The |
70 | | * MediaStreamGraph monitors the main thread event loop via nsIAppShell::RunInStableState |
71 | | * to ensure that graph changes from a single event loop task are always |
72 | | * processed all together. Control APIs should only be used on the main thread, |
73 | | * currently; we may be able to relax that later. |
74 | | * |
75 | | * To allow precise synchronization of times in the control API, the |
76 | | * MediaStreamGraph maintains a "media timeline". Control APIs that take or |
77 | | * return times use that timeline. Those times never advance during |
78 | | * an event loop task. This time is returned by MediaStreamGraph::GetCurrentTime(). |
79 | | * |
80 | | * Media decoding, audio processing and media playback use thread-safe APIs to |
81 | | * the media graph to ensure they can continue while the main thread is blocked. |
82 | | * |
83 | | * When the graph is changed, we may need to throw out buffered data and |
84 | | * reprocess it. This is triggered automatically by the MediaStreamGraph. |
85 | | */ |
86 | | |
87 | | class AudioNodeEngine; |
88 | | class AudioNodeExternalInputStream; |
89 | | class AudioNodeStream; |
90 | | class MediaInputPort; |
91 | | class MediaStream; |
92 | | class MediaStreamGraph; |
93 | | class MediaStreamGraphImpl; |
94 | | class ProcessedMediaStream; |
95 | | class SourceMediaStream; |
96 | | |
97 | | class AudioDataListenerInterface { |
98 | | protected: |
99 | | // Protected destructor, to discourage deletion outside of Release(): |
100 | | virtual ~AudioDataListenerInterface() {} |
101 | | |
102 | | public: |
103 | | /* These are for cubeb audio input & output streams: */ |
104 | | /** |
105 | | * Output data to speakers, for use as the "far-end" data for echo |
106 | | * cancellation. This is not guaranteed to be in any particular size |
107 | | * chunks. |
108 | | */ |
109 | | virtual void NotifyOutputData(MediaStreamGraphImpl* aGraph, |
110 | | AudioDataValue* aBuffer, size_t aFrames, |
111 | | TrackRate aRate, uint32_t aChannels) = 0; |
112 | | /** |
113 | | * Input data from a microphone (or other audio source. This is not |
114 | | * guaranteed to be in any particular size chunks. |
115 | | */ |
116 | | virtual void NotifyInputData(MediaStreamGraphImpl* aGraph, |
117 | | const AudioDataValue* aBuffer, size_t aFrames, |
118 | | TrackRate aRate, uint32_t aChannels) = 0; |
119 | | |
120 | | /** |
121 | | * Number of audio input channels. |
122 | | */ |
123 | | virtual uint32_t RequestedInputChannelCount(MediaStreamGraphImpl* aGraph) = 0; |
124 | | |
125 | | /** |
126 | | * Called when the underlying audio device has changed. |
127 | | */ |
128 | | virtual void DeviceChanged(MediaStreamGraphImpl* aGraph) = 0; |
129 | | |
130 | | /** |
131 | | * Called when the underlying audio device is being closed. |
132 | | */ |
133 | | virtual void Disconnect(MediaStreamGraphImpl* aGraph) = 0; |
134 | | }; |
135 | | |
136 | | class AudioDataListener : public AudioDataListenerInterface { |
137 | | protected: |
138 | | // Protected destructor, to discourage deletion outside of Release(): |
139 | 0 | virtual ~AudioDataListener() {} |
140 | | |
141 | | public: |
142 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener) |
143 | | }; |
144 | | |
145 | | /** |
146 | | * This is a base class for main-thread listener callbacks. |
147 | | * This callback is invoked on the main thread when the main-thread-visible |
148 | | * state of a stream has changed. |
149 | | * |
150 | | * These methods are called with the media graph monitor held, so |
151 | | * reentry into general media graph methods is not possible. |
152 | | * You should do something non-blocking and non-reentrant (e.g. dispatch an |
153 | | * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate |
154 | | * would be a good choice. |
155 | | * The listener is allowed to synchronously remove itself from the stream, but |
156 | | * not add or remove any other listeners. |
157 | | */ |
158 | | class MainThreadMediaStreamListener { |
159 | | public: |
160 | | virtual void NotifyMainThreadStreamFinished() = 0; |
161 | | }; |
162 | | |
163 | | /** |
164 | | * Helper struct used to keep track of memory usage by AudioNodes. |
165 | | */ |
166 | | struct AudioNodeSizes |
167 | | { |
168 | 0 | AudioNodeSizes() : mStream(0), mEngine(0), mNodeType() {} |
169 | | size_t mStream; |
170 | | size_t mEngine; |
171 | | const char* mNodeType; |
172 | | }; |
173 | | |
174 | | class AudioNodeEngine; |
175 | | class AudioNodeExternalInputStream; |
176 | | class AudioNodeStream; |
177 | | class AudioSegment; |
178 | | class DirectMediaStreamTrackListener; |
179 | | class MediaInputPort; |
180 | | class MediaStreamGraphImpl; |
181 | | class MediaStreamListener; |
182 | | class MediaStreamTrackListener; |
183 | | class MediaStreamVideoSink; |
184 | | class ProcessedMediaStream; |
185 | | class SourceMediaStream; |
186 | | class TrackUnionStream; |
187 | | |
188 | | /** |
189 | | * Helper struct for binding a track listener to a specific TrackID. |
190 | | */ |
191 | | template<typename Listener> |
192 | | struct TrackBound |
193 | | { |
194 | | RefPtr<Listener> mListener; |
195 | | TrackID mTrackID; |
196 | | }; |
197 | | |
198 | | /** |
199 | | * A stream of synchronized audio and video data. All (not blocked) streams |
200 | | * progress at the same rate --- "real time". Streams cannot seek. The only |
201 | | * operation readers can perform on a stream is to read the next data. |
202 | | * |
203 | | * Consumers of a stream can be reading from it at different offsets, but that |
204 | | * should only happen due to the order in which consumers are being run. |
205 | | * Those offsets must not diverge in the long term, otherwise we would require |
206 | | * unbounded buffering. |
207 | | * |
208 | | * Streams can be in a "blocked" state. While blocked, a stream does not |
209 | | * produce data. A stream can be explicitly blocked via the control API, |
210 | | * or implicitly blocked by whatever's generating it (e.g. an underrun in the |
211 | | * source resource), or implicitly blocked because something consuming it |
212 | | * blocks, or implicitly because it has finished. |
213 | | * |
214 | | * A stream can be in a "finished" state. "Finished" streams are permanently |
215 | | * blocked. |
216 | | * |
217 | | * Transitions into and out of the "blocked" and "finished" states are managed |
218 | | * by the MediaStreamGraph on the media graph thread. |
219 | | * |
220 | | * We buffer media data ahead of the consumers' reading offsets. It is possible |
221 | | * to have buffered data but still be blocked. |
222 | | * |
223 | | * Any stream can have its audio and video playing when requested. The media |
224 | | * stream graph plays audio by constructing audio output streams as necessary. |
225 | | * Video is played by setting video frames into an MediaStreamVideoSink at the right |
226 | | * time. To ensure video plays in sync with audio, make sure that the same |
227 | | * stream is playing both the audio and video. |
228 | | * |
229 | | * The data in a stream is managed by StreamTracks. It consists of a set of |
230 | | * tracks of various types that can start and end over time. |
231 | | * |
232 | | * Streams are explicitly managed. The client creates them via |
233 | | * MediaStreamGraph::CreateInput/ProcessedMediaStream, and releases them by calling |
234 | | * Destroy() when no longer needed (actual destruction will be deferred). |
235 | | * The actual object is owned by the MediaStreamGraph. The basic idea is that |
236 | | * main thread objects will keep Streams alive as long as necessary (using the |
237 | | * cycle collector to clean up whenever needed). |
238 | | * |
239 | | * We make them refcounted only so that stream-related messages with MediaStream* |
240 | | * pointers can be sent to the main thread safely. |
241 | | * |
242 | | * The lifetimes of MediaStreams are controlled from the main thread. |
243 | | * For MediaStreams exposed to the DOM, the lifetime is controlled by the DOM |
244 | | * wrapper; the DOM wrappers own their associated MediaStreams. When a DOM |
245 | | * wrapper is destroyed, it sends a Destroy message for the associated |
246 | | * MediaStream and clears its reference (the last main-thread reference to |
247 | | * the object). When the Destroy message is processed on the graph manager |
248 | | * thread we immediately release the affected objects (disentangling them |
249 | | * from other objects as necessary). |
250 | | * |
251 | | * This could cause problems for media processing if a MediaStream is |
252 | | * destroyed while a downstream MediaStream is still using it. Therefore |
253 | | * the DOM wrappers must keep upstream MediaStreams alive as long as they |
254 | | * could be being used in the media graph. |
255 | | * |
256 | | * At any time, however, a set of MediaStream wrappers could be |
257 | | * collected via cycle collection. Destroy messages will be sent |
258 | | * for those objects in arbitrary order and the MediaStreamGraph has to be able |
259 | | * to handle this. |
260 | | */ |
261 | | |
262 | | // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to |
263 | | // GetTickCount() and conflicts with MediaStream::GetCurrentTime. |
264 | | #ifdef GetCurrentTime |
265 | | #undef GetCurrentTime |
266 | | #endif |
267 | | |
268 | | class MediaStream : public mozilla::LinkedListElement<MediaStream> |
269 | | { |
270 | | public: |
271 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream) |
272 | | |
273 | | explicit MediaStream(); |
274 | | |
275 | | protected: |
276 | | // Protected destructor, to discourage deletion outside of Release(): |
277 | | virtual ~MediaStream(); |
278 | | |
279 | | public: |
280 | | /** |
281 | | * Returns the graph that owns this stream. |
282 | | */ |
283 | | MediaStreamGraphImpl* GraphImpl(); |
284 | | const MediaStreamGraphImpl* GraphImpl() const; |
285 | | MediaStreamGraph* Graph(); |
286 | | /** |
287 | | * Sets the graph that owns this stream. Should only be called once. |
288 | | */ |
289 | | void SetGraphImpl(MediaStreamGraphImpl* aGraph); |
290 | | void SetGraphImpl(MediaStreamGraph* aGraph); |
291 | | |
292 | | /** |
293 | | * Returns sample rate of the graph. |
294 | | */ |
295 | | TrackRate GraphRate() const { return mTracks.GraphRate(); } |
296 | | |
297 | | // Control API. |
298 | | // Since a stream can be played multiple ways, we need to combine independent |
299 | | // volume settings. The aKey parameter is used to keep volume settings |
300 | | // separate. Since the stream is always playing the same contents, only |
301 | | // a single audio output stream is used; the volumes are combined. |
302 | | // Currently only the first enabled audio track is played. |
303 | | // XXX change this so all enabled audio tracks are mixed and played. |
304 | | virtual void AddAudioOutput(void* aKey); |
305 | | virtual void SetAudioOutputVolume(void* aKey, float aVolume); |
306 | | virtual void RemoveAudioOutput(void* aKey); |
307 | | // Since a stream can be played multiple ways, we need to be able to |
308 | | // play to multiple MediaStreamVideoSinks. |
309 | | // Only the first enabled video track is played. |
310 | | virtual void AddVideoOutput(MediaStreamVideoSink* aSink, |
311 | | TrackID aID = TRACK_ANY); |
312 | | virtual void RemoveVideoOutput(MediaStreamVideoSink* aSink, |
313 | | TrackID aID = TRACK_ANY); |
314 | | // Explicitly suspend. Useful for example if a media element is pausing |
315 | | // and we need to stop its stream emitting its buffered data. As soon as the |
316 | | // Suspend message reaches the graph, the stream stops processing. It |
317 | | // ignores its inputs and produces silence/no video until Resumed. Its |
318 | | // current time does not advance. |
319 | | virtual void Suspend(); |
320 | | virtual void Resume(); |
321 | | // Events will be dispatched by calling methods of aListener. |
322 | | virtual void AddListener(MediaStreamListener* aListener); |
323 | | virtual void RemoveListener(MediaStreamListener* aListener); |
324 | | virtual void AddTrackListener(MediaStreamTrackListener* aListener, |
325 | | TrackID aTrackID); |
326 | | virtual void RemoveTrackListener(MediaStreamTrackListener* aListener, |
327 | | TrackID aTrackID); |
328 | | |
329 | | /** |
330 | | * Adds aListener to the source stream of track aTrackID in this stream. |
331 | | * When the MediaStreamGraph processes the added listener, it will traverse |
332 | | * the graph and add it to the track's source stream (remapping the TrackID |
333 | | * along the way). |
334 | | * Note that the listener will be notified on the MediaStreamGraph thread |
335 | | * with whether the installation of it at the source was successful or not. |
336 | | */ |
337 | | virtual void AddDirectTrackListener(DirectMediaStreamTrackListener* aListener, |
338 | | TrackID aTrackID); |
339 | | |
340 | | /** |
341 | | * Removes aListener from the source stream of track aTrackID in this stream. |
342 | | * Note that the listener has already been removed if the link between the |
343 | | * source of track aTrackID and this stream has been broken (and made track |
344 | | * aTrackID end). The caller doesn't have to care about this, removing when |
345 | | * the source cannot be found, or when the listener had already been removed |
346 | | * does nothing. |
347 | | */ |
348 | | virtual void RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener, |
349 | | TrackID aTrackID); |
350 | | |
351 | | // A disabled track has video replaced by black, and audio replaced by |
352 | | // silence. |
353 | | void SetTrackEnabled(TrackID aTrackID, DisabledTrackMode aMode); |
354 | | |
355 | | // Finish event will be notified by calling methods of aListener. It is the |
356 | | // responsibility of the caller to remove aListener before it is destroyed. |
357 | | void AddMainThreadListener(MainThreadMediaStreamListener* aListener); |
358 | | // It's safe to call this even if aListener is not currently a listener; |
359 | | // the call will be ignored. |
360 | | void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener) |
361 | | { |
362 | | MOZ_ASSERT(NS_IsMainThread()); |
363 | | MOZ_ASSERT(aListener); |
364 | | mMainThreadListeners.RemoveElement(aListener); |
365 | | } |
366 | | |
367 | | /** |
368 | | * Ensure a runnable will run on the main thread after running all pending |
369 | | * updates that were sent from the graph thread or will be sent before the |
370 | | * graph thread receives the next graph update. |
371 | | * |
372 | | * If the graph has been shut down or destroyed, then the runnable will be |
373 | | * dispatched to the event queue immediately. If the graph is non-realtime |
374 | | * and has not started, then the runnable will be run |
375 | | * synchronously/immediately. (There are no pending updates in these |
376 | | * situations.) |
377 | | * |
378 | | * Main thread only. |
379 | | */ |
380 | | void RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable); |
381 | | |
382 | | // Signal that the client is done with this MediaStream. It will be deleted |
383 | | // later. Do not mix usage of Destroy() with RegisterUser()/UnregisterUser(). |
384 | | // That will cause the MediaStream to be destroyed twice, which will cause |
385 | | // some assertions to fail. |
386 | | virtual void Destroy(); |
387 | | // Signal that a client is using this MediaStream. Useful to not have to |
388 | | // explicitly manage ownership (responsibility to Destroy()) when there are |
389 | | // multiple clients using a MediaStream. |
390 | | void RegisterUser(); |
391 | | // Signal that a client no longer needs this MediaStream. When the number of |
392 | | // clients using this MediaStream reaches 0, it will be destroyed. |
393 | | void UnregisterUser(); |
394 | | |
395 | | // Returns the main-thread's view of how much data has been processed by |
396 | | // this stream. |
397 | | StreamTime GetCurrentTime() const |
398 | | { |
399 | | NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); |
400 | | return mMainThreadCurrentTime; |
401 | | } |
402 | | // Return the main thread's view of whether this stream has finished. |
403 | | bool IsFinished() const |
404 | | { |
405 | | NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); |
406 | | return mMainThreadFinished; |
407 | | } |
408 | | |
409 | | bool IsDestroyed() const |
410 | | { |
411 | | NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); |
412 | | return mMainThreadDestroyed; |
413 | | } |
414 | | |
415 | | friend class MediaStreamGraphImpl; |
416 | | friend class MediaInputPort; |
417 | | friend class AudioNodeExternalInputStream; |
418 | | |
419 | 0 | virtual SourceMediaStream* AsSourceStream() { return nullptr; } |
420 | 0 | virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; } |
421 | 0 | virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; } |
422 | 0 | virtual TrackUnionStream* AsTrackUnionStream() { return nullptr; } |
423 | | |
424 | | // These Impl methods perform the core functionality of the control methods |
425 | | // above, on the media graph thread. |
426 | | /** |
427 | | * Stop all stream activity and disconnect it from all inputs and outputs. |
428 | | * This must be idempotent. |
429 | | */ |
430 | | virtual void DestroyImpl(); |
431 | 0 | StreamTime GetTracksEnd() const { return mTracks.GetEnd(); } |
432 | | #ifdef DEBUG |
433 | | void DumpTrackInfo() const { return mTracks.DumpTrackInfo(); } |
434 | | #endif |
435 | | void SetAudioOutputVolumeImpl(void* aKey, float aVolume); |
436 | | void AddAudioOutputImpl(void* aKey); |
437 | | // Returns true if this stream has an audio output. |
438 | | bool HasAudioOutput() const |
439 | 0 | { |
440 | 0 | return !mAudioOutputs.IsEmpty(); |
441 | 0 | } |
442 | | void RemoveAudioOutputImpl(void* aKey); |
443 | | void AddVideoOutputImpl(already_AddRefed<MediaStreamVideoSink> aSink, |
444 | | TrackID aID); |
445 | | void RemoveVideoOutputImpl(MediaStreamVideoSink* aSink, TrackID aID); |
446 | | void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener); |
447 | | void RemoveListenerImpl(MediaStreamListener* aListener); |
448 | | |
449 | | /** |
450 | | * Removes all direct listeners and signals to them that they have been |
451 | | * uninstalled. |
452 | | */ |
453 | 0 | virtual void RemoveAllDirectListenersImpl() {} |
454 | | void RemoveAllListenersImpl(); |
455 | | virtual void AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener, |
456 | | TrackID aTrackID); |
457 | | virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener, |
458 | | TrackID aTrackID); |
459 | | virtual void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener, |
460 | | TrackID aTrackID); |
461 | | virtual void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener, |
462 | | TrackID aTrackID); |
463 | | virtual void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode); |
464 | | DisabledTrackMode GetDisabledTrackMode(TrackID aTrackID); |
465 | | |
466 | | void AddConsumer(MediaInputPort* aPort) |
467 | 0 | { |
468 | 0 | mConsumers.AppendElement(aPort); |
469 | 0 | } |
470 | | void RemoveConsumer(MediaInputPort* aPort) |
471 | 0 | { |
472 | 0 | mConsumers.RemoveElement(aPort); |
473 | 0 | } |
474 | | uint32_t ConsumerCount() const |
475 | 0 | { |
476 | 0 | return mConsumers.Length(); |
477 | 0 | } |
478 | 0 | StreamTracks& GetStreamTracks() { return mTracks; } |
479 | 0 | GraphTime GetStreamTracksStartTime() const { return mTracksStartTime; } |
480 | | |
481 | | double StreamTimeToSeconds(StreamTime aTime) const |
482 | | { |
483 | | NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time"); |
484 | | return static_cast<double>(aTime)/mTracks.GraphRate(); |
485 | | } |
486 | | int64_t StreamTimeToMicroseconds(StreamTime aTime) const |
487 | 0 | { |
488 | 0 | NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time"); |
489 | 0 | return (aTime*1000000) / mTracks.GraphRate(); |
490 | 0 | } |
491 | | StreamTime SecondsToNearestStreamTime(double aSeconds) const |
492 | | { |
493 | | NS_ASSERTION(0 <= aSeconds && aSeconds <= TRACK_TICKS_MAX/TRACK_RATE_MAX, |
494 | | "Bad seconds"); |
495 | | return mTracks.GraphRate() * aSeconds + 0.5; |
496 | | } |
497 | | StreamTime MicrosecondsToStreamTimeRoundDown(int64_t aMicroseconds) const |
498 | 0 | { |
499 | 0 | return (aMicroseconds*mTracks.GraphRate())/1000000; |
500 | 0 | } |
501 | | |
502 | | TrackTicks TimeToTicksRoundUp(TrackRate aRate, StreamTime aTime) const |
503 | | { |
504 | | return RateConvertTicksRoundUp(aRate, mTracks.GraphRate(), aTime); |
505 | | } |
506 | | StreamTime TicksToTimeRoundDown(TrackRate aRate, TrackTicks aTicks) const |
507 | 0 | { |
508 | 0 | return RateConvertTicksRoundDown(mTracks.GraphRate(), aRate, aTicks); |
509 | 0 | } |
510 | | /** |
511 | | * Convert graph time to stream time. aTime must be <= mStateComputedTime |
512 | | * to ensure we know exactly how much time this stream will be blocked during |
513 | | * the interval. |
514 | | */ |
515 | | StreamTime GraphTimeToStreamTimeWithBlocking(GraphTime aTime) const; |
516 | | /** |
517 | | * Convert graph time to stream time. This assumes there is no blocking time |
518 | | * to take account of, which is always true except between a stream |
519 | | * having its blocking time calculated in UpdateGraph and its blocking time |
520 | | * taken account of in UpdateCurrentTimeForStreams. |
521 | | */ |
522 | | StreamTime GraphTimeToStreamTime(GraphTime aTime) const; |
523 | | /** |
524 | | * Convert stream time to graph time. This assumes there is no blocking time |
525 | | * to take account of, which is always true except between a stream |
526 | | * having its blocking time calculated in UpdateGraph and its blocking time |
527 | | * taken account of in UpdateCurrentTimeForStreams. |
528 | | */ |
529 | | GraphTime StreamTimeToGraphTime(StreamTime aTime) const; |
530 | | |
531 | 0 | bool IsFinishedOnGraphThread() const { return mFinished; } |
532 | | virtual void FinishOnGraphThread(); |
533 | | |
534 | 0 | bool HasCurrentData() const { return mHasCurrentData; } |
535 | | |
536 | | /** |
537 | | * Find track by track id. |
538 | | */ |
539 | | StreamTracks::Track* FindTrack(TrackID aID) const; |
540 | | |
541 | | StreamTracks::Track* EnsureTrack(TrackID aTrack); |
542 | | |
543 | | virtual void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment = nullptr); |
544 | | |
545 | | // Return true if the main thread needs to observe updates from this stream. |
546 | | virtual bool MainThreadNeedsUpdates() const |
547 | 0 | { |
548 | 0 | return true; |
549 | 0 | } |
550 | | |
551 | | virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; |
552 | | virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
553 | | |
554 | 0 | bool IsSuspended() const { return mSuspendedCount > 0; } |
555 | | void IncrementSuspendCount(); |
556 | | void DecrementSuspendCount(); |
557 | | |
558 | | protected: |
559 | | // |AdvanceTimeVaryingValuesToCurrentTime| will be override in SourceMediaStream. |
560 | | virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, |
561 | | GraphTime aBlockedTime) |
562 | 0 | { |
563 | 0 | mTracksStartTime += aBlockedTime; |
564 | 0 | mTracks.ForgetUpTo(aCurrentTime - mTracksStartTime); |
565 | 0 | } |
566 | | |
567 | | void NotifyMainThreadListeners() |
568 | 0 | { |
569 | 0 | NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); |
570 | 0 |
|
571 | 0 | for (int32_t i = mMainThreadListeners.Length() - 1; i >= 0; --i) { |
572 | 0 | mMainThreadListeners[i]->NotifyMainThreadStreamFinished(); |
573 | 0 | } |
574 | 0 | mMainThreadListeners.Clear(); |
575 | 0 | } |
576 | | |
577 | | bool ShouldNotifyStreamFinished() |
578 | 0 | { |
579 | 0 | NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); |
580 | 0 | if (!mMainThreadFinished || mFinishedNotificationSent) { |
581 | 0 | return false; |
582 | 0 | } |
583 | 0 | |
584 | 0 | mFinishedNotificationSent = true; |
585 | 0 | return true; |
586 | 0 | } |
587 | | |
588 | | // This state is all initialized on the main thread but |
589 | | // otherwise modified only on the media graph thread. |
590 | | |
591 | | // Buffered data. The start of the buffer corresponds to mTracksStartTime. |
592 | | // Conceptually the buffer contains everything this stream has ever played, |
593 | | // but we forget some prefix of the buffered data to bound the space usage. |
594 | | StreamTracks mTracks; |
595 | | // The time when the buffered data could be considered to have started playing. |
596 | | // This increases over time to account for time the stream was blocked before |
597 | | // mCurrentTime. |
598 | | GraphTime mTracksStartTime; |
599 | | |
600 | | // Client-set volume of this stream |
601 | | struct AudioOutput { |
602 | 0 | explicit AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {} |
603 | | void* mKey; |
604 | | float mVolume; |
605 | | }; |
606 | | nsTArray<AudioOutput> mAudioOutputs; |
607 | | nsTArray<TrackBound<MediaStreamVideoSink>> mVideoOutputs; |
608 | | // We record the last played video frame to avoid playing the frame again |
609 | | // with a different frame id. |
610 | | VideoFrame mLastPlayedVideoFrame; |
611 | | nsTArray<RefPtr<MediaStreamListener> > mListeners; |
612 | | nsTArray<TrackBound<MediaStreamTrackListener>> mTrackListeners; |
613 | | nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners; |
614 | | // List of disabled TrackIDs and their associated disabled mode. |
615 | | // They can either by disabled by frames being replaced by black, or by |
616 | | // retaining the previous frame. |
617 | | nsTArray<DisabledTrack> mDisabledTracks; |
618 | | |
619 | | // GraphTime at which this stream starts blocking. |
620 | | // This is only valid up to mStateComputedTime. The stream is considered to |
621 | | // have not been blocked before mCurrentTime (its mTracksStartTime is increased |
622 | | // as necessary to account for that time instead). |
623 | | GraphTime mStartBlocking; |
624 | | |
625 | | // MediaInputPorts to which this is connected |
626 | | nsTArray<MediaInputPort*> mConsumers; |
627 | | |
628 | | // Where audio output is going. There is one AudioOutputStream per |
629 | | // audio track. |
630 | | struct AudioOutputStream |
631 | | { |
632 | | // When we started audio playback for this track. |
633 | | // Add mStream->GetPosition() to find the current audio playback position. |
634 | | GraphTime mAudioPlaybackStartTime; |
635 | | // Amount of time that we've wanted to play silence because of the stream |
636 | | // blocking. |
637 | | MediaTime mBlockedAudioTime; |
638 | | // Last tick written to the audio output. |
639 | | StreamTime mLastTickWritten; |
640 | | TrackID mTrackID; |
641 | | }; |
642 | | nsTArray<AudioOutputStream> mAudioOutputStreams; |
643 | | |
644 | | /** |
645 | | * Number of outstanding suspend operations on this stream. Stream is |
646 | | * suspended when this is > 0. |
647 | | */ |
648 | | int32_t mSuspendedCount; |
649 | | |
650 | | /** |
651 | | * When true, this means the stream will be finished once all |
652 | | * buffered data has been consumed. |
653 | | * Only accessed on the graph thread |
654 | | */ |
655 | | bool mFinished; |
656 | | /** |
657 | | * When true, mFinished is true and we've played all the data in this stream |
658 | | * and fired NotifyFinished notifications. |
659 | | */ |
660 | | bool mNotifiedFinished; |
661 | | /** |
662 | | * When true, the last NotifyBlockingChanged delivered to the listeners |
663 | | * indicated that the stream is blocked. |
664 | | */ |
665 | | bool mNotifiedBlocked; |
666 | | /** |
667 | | * True if some data can be present by this stream if/when it's unblocked. |
668 | | * Set by the stream itself on the MediaStreamGraph thread. Only changes |
669 | | * from false to true once a stream has data, since we won't |
670 | | * unblock it until there's more data. |
671 | | */ |
672 | | bool mHasCurrentData; |
673 | | /** |
674 | | * True if mHasCurrentData is true and we've notified listeners. |
675 | | */ |
676 | | bool mNotifiedHasCurrentData; |
677 | | |
678 | | // Main-thread views of state |
679 | | StreamTime mMainThreadCurrentTime; |
680 | | bool mMainThreadFinished; |
681 | | bool mFinishedNotificationSent; |
682 | | bool mMainThreadDestroyed; |
683 | | int mNrOfMainThreadUsers; |
684 | | |
685 | | // Our media stream graph. null if destroyed on the graph thread. |
686 | | MediaStreamGraphImpl* mGraph; |
687 | | }; |
688 | | |
689 | | /** |
690 | | * This is a stream into which a decoder can write audio and video. |
691 | | * |
692 | | * Audio and video can be written on any thread, but you probably want to |
693 | | * always write from the same thread to avoid unexpected interleavings. |
694 | | */ |
695 | | class SourceMediaStream : public MediaStream |
696 | | { |
697 | | public: |
698 | | explicit SourceMediaStream(); |
699 | | |
700 | 0 | SourceMediaStream* AsSourceStream() override { return this; } |
701 | | |
702 | | // Main thread only |
703 | | |
704 | | /** |
705 | | * Enable or disable pulling. When pulling is enabled, NotifyPull |
706 | | * gets called on MediaStreamListeners for this stream during the |
707 | | * MediaStreamGraph control loop. Pulling is initially disabled. |
708 | | * Due to unavoidable race conditions, after a call to SetPullEnabled(false) |
709 | | * it is still possible for a NotifyPull to occur. |
710 | | */ |
711 | | void SetPullEnabled(bool aEnabled); |
712 | | |
713 | | // Users of audio inputs go through the stream so it can track when the |
714 | | // last stream referencing an input goes away, so it can close the cubeb |
715 | | // input. Also note: callable on any thread (though it bounces through |
716 | | // MainThread to set the command if needed). |
717 | | nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID, |
718 | | AudioDataListener* aListener); |
719 | | // Note: also implied when Destroy() happens |
720 | | void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID, |
721 | | AudioDataListener* aListener); |
722 | | |
723 | | // MediaStreamGraph thread only |
724 | | void DestroyImpl() override; |
725 | | |
726 | | // Call these on any thread. |
727 | | /** |
728 | | * Call all MediaStreamListeners to request new data via the NotifyPull API |
729 | | * (if enabled). |
730 | | * aDesiredUpToTime (in): end time of new data requested. |
731 | | * |
732 | | * Returns true if new data is about to be added. |
733 | | */ |
734 | | bool PullNewData(StreamTime aDesiredUpToTime); |
735 | | |
736 | | /** |
737 | | * Extract any state updates pending in the stream, and apply them. |
738 | | */ |
739 | | void ExtractPendingInput(); |
740 | | |
741 | | /** |
742 | | * These add/remove DirectListeners, which allow bypassing the graph and any |
743 | | * synchronization delays for e.g. PeerConnection, which wants the data ASAP |
744 | | * and lets the far-end handle sync and playout timing. |
745 | | */ |
746 | | void NotifyListenersEventImpl(MediaStreamGraphEvent aEvent); |
747 | | void NotifyListenersEvent(MediaStreamGraphEvent aEvent); |
748 | | |
749 | | enum { |
750 | | ADDTRACK_QUEUED = 0x01 // Queue track add until FinishAddTracks() |
751 | | }; |
752 | | /** |
753 | | * Add a new track to the stream starting at the given base time (which |
754 | | * must be greater than or equal to the last time passed to |
755 | | * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should |
756 | | * contain data starting after aStart. |
757 | | */ |
758 | | void AddTrack(TrackID aID, StreamTime aStart, MediaSegment* aSegment, |
759 | | uint32_t aFlags = 0) |
760 | | { |
761 | | AddTrackInternal(aID, GraphRate(), aStart, aSegment, aFlags); |
762 | | } |
763 | | |
764 | | /** |
765 | | * Like AddTrack, but resamples audio from aRate to the graph rate. |
766 | | */ |
767 | | void AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart, |
768 | | AudioSegment* aSegment, uint32_t aFlags = 0); |
769 | | |
770 | | /** |
771 | | * Call after a series of AddTrack or AddAudioTrack calls to implement |
772 | | * any pending track adds. |
773 | | */ |
774 | | void FinishAddTracks(); |
775 | | |
776 | | /** |
777 | | * Append media data to a track. Ownership of aSegment remains with the caller, |
778 | | * but aSegment is emptied. |
779 | | * Returns false if the data was not appended because no such track exists |
780 | | * or the stream was already finished. |
781 | | */ |
782 | | virtual bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr); |
783 | | /** |
784 | | * Get the stream time of the end of the data that has been appended so far. |
785 | | * Can be called from any thread but won't be useful if it can race with |
786 | | * an AppendToTrack call, so should probably just be called from the thread |
787 | | * that also calls AppendToTrack. |
788 | | */ |
789 | | StreamTime GetEndOfAppendedData(TrackID aID); |
790 | | /** |
791 | | * Indicate that a track has ended. Do not do any more API calls |
792 | | * affecting this track. |
793 | | * Ignored if the track does not exist. |
794 | | */ |
795 | | void EndTrack(TrackID aID); |
796 | | /** |
797 | | * Indicate that no tracks will be added starting before time aKnownTime. |
798 | | * aKnownTime must be >= its value at the last call to AdvanceKnownTracksTime. |
799 | | */ |
800 | | void AdvanceKnownTracksTime(StreamTime aKnownTime); |
801 | | void AdvanceKnownTracksTimeWithLockHeld(StreamTime aKnownTime); |
802 | | /** |
803 | | * Indicate that this stream should enter the "finished" state. All tracks |
804 | | * must have been ended via EndTrack. The finish time of the stream is |
805 | | * when all tracks have ended. |
806 | | */ |
807 | | void FinishPendingWithLockHeld(); |
808 | | void FinishPending() |
809 | 0 | { |
810 | 0 | MutexAutoLock lock(mMutex); |
811 | 0 | FinishPendingWithLockHeld(); |
812 | 0 | } |
813 | | |
814 | | // Overriding allows us to hold the mMutex lock while changing the track enable status |
815 | | void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) override; |
816 | | |
817 | | // Overriding allows us to ensure mMutex is locked while changing the track enable status |
818 | | void |
819 | | ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, |
820 | 0 | MediaSegment* aRawSegment = nullptr) override { |
821 | 0 | mMutex.AssertCurrentThreadOwns(); |
822 | 0 | MediaStream::ApplyTrackDisabling(aTrackID, aSegment, aRawSegment); |
823 | 0 | } |
824 | | |
825 | | void RemoveAllDirectListenersImpl() override; |
826 | | |
827 | | /** |
828 | | * End all tracks and Finish() this stream. Used to voluntarily revoke access |
829 | | * to a LocalMediaStream. |
830 | | */ |
831 | | void EndAllTrackAndFinish(); |
832 | | |
833 | | void RegisterForAudioMixing(); |
834 | | |
835 | | /** |
836 | | * Returns true if this SourceMediaStream contains at least one audio track |
837 | | * that is in pending state. |
838 | | * This is thread safe, and takes the SourceMediaStream mutex. |
839 | | */ |
840 | | bool HasPendingAudioTrack(); |
841 | | |
842 | | TimeStamp GetStreamTracksStrartTimeStamp() |
843 | 0 | { |
844 | 0 | MutexAutoLock lock(mMutex); |
845 | 0 | return mStreamTracksStartTimeStamp; |
846 | 0 | } |
847 | | |
848 | | // XXX need a Reset API |
849 | | |
850 | | friend class MediaStreamGraphImpl; |
851 | | |
852 | | protected: |
853 | | enum TrackCommands : uint32_t; |
854 | | |
855 | | virtual ~SourceMediaStream(); |
856 | | |
857 | | /** |
858 | | * Data for each track that hasn't ended. |
859 | | */ |
860 | | struct TrackData { |
861 | | TrackID mID; |
862 | | // Sample rate of the input data. |
863 | | TrackRate mInputRate; |
864 | | // Resampler if the rate of the input track does not match the |
865 | | // MediaStreamGraph's. |
866 | | nsAutoRef<SpeexResamplerState> mResampler; |
867 | | int mResamplerChannelCount; |
868 | | StreamTime mStart; |
869 | | // End-time of data already flushed to the track (excluding mData) |
870 | | StreamTime mEndOfFlushedData; |
871 | | // Each time the track updates are flushed to the media graph thread, |
872 | | // the segment buffer is emptied. |
873 | | nsAutoPtr<MediaSegment> mData; |
874 | | // Each time the track updates are flushed to the media graph thread, |
875 | | // this is cleared. |
876 | | uint32_t mCommands; |
877 | | }; |
878 | | |
879 | | bool NeedsMixing(); |
880 | | |
881 | | void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment); |
882 | | |
883 | | void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener, |
884 | | TrackID aTrackID) override; |
885 | | void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener, |
886 | | TrackID aTrackID) override; |
887 | | |
888 | | void AddTrackInternal(TrackID aID, TrackRate aRate, |
889 | | StreamTime aStart, MediaSegment* aSegment, |
890 | | uint32_t aFlags); |
891 | | |
892 | | TrackData* FindDataForTrack(TrackID aID) |
893 | 0 | { |
894 | 0 | mMutex.AssertCurrentThreadOwns(); |
895 | 0 | for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) { |
896 | 0 | if (mUpdateTracks[i].mID == aID) { |
897 | 0 | return &mUpdateTracks[i]; |
898 | 0 | } |
899 | 0 | } |
900 | 0 | return nullptr; |
901 | 0 | } |
902 | | |
903 | | /** |
904 | | * Notify direct consumers of new data to one of the stream tracks. |
905 | | * The data doesn't have to be resampled (though it may be). This is called |
906 | | * from AppendToTrack on the thread providing the data, and will call |
907 | | * the Listeners on this thread. |
908 | | */ |
909 | | void NotifyDirectConsumers(TrackData *aTrack, |
910 | | MediaSegment *aSegment); |
911 | | |
912 | | virtual void |
913 | | AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, |
914 | | GraphTime aBlockedTime) override; |
915 | | void SetStreamTracksStartTimeStamp(const TimeStamp& aTimeStamp) |
916 | 0 | { |
917 | 0 | MutexAutoLock lock(mMutex); |
918 | 0 | mStreamTracksStartTimeStamp = aTimeStamp; |
919 | 0 | } |
920 | | |
921 | | // Only accessed on the MSG thread. Used so to ask the MSGImpl to usecount |
922 | | // users of a specific input. |
923 | | // XXX Should really be a CubebUtils::AudioDeviceID, but they aren't |
924 | | // copyable (opaque pointers) |
925 | | RefPtr<AudioDataListener> mInputListener; |
926 | | |
927 | | // This must be acquired *before* MediaStreamGraphImpl's lock, if they are |
928 | | // held together. |
929 | | Mutex mMutex; |
930 | | // protected by mMutex |
931 | | StreamTime mUpdateKnownTracksTime; |
932 | | // This time stamp will be updated in adding and blocked SourceMediaStream, |
933 | | // |AddStreamGraphThread| and |AdvanceTimeVaryingValuesToCurrentTime| in |
934 | | // particularly. |
935 | | TimeStamp mStreamTracksStartTimeStamp; |
936 | | nsTArray<TrackData> mUpdateTracks; |
937 | | nsTArray<TrackData> mPendingTracks; |
938 | | nsTArray<TrackBound<DirectMediaStreamTrackListener>> mDirectTrackListeners; |
939 | | bool mPullEnabled; |
940 | | bool mFinishPending; |
941 | | bool mNeedsMixing; |
942 | | }; |
943 | | |
944 | | /** |
945 | | * The blocking mode decides how a track should be blocked in a MediaInputPort. |
946 | | */ |
947 | | enum class BlockingMode |
948 | | { |
949 | | /** |
950 | | * BlockingMode CREATION blocks the source track from being created |
951 | | * in the destination. It'll end if it already exists. |
952 | | */ |
953 | | CREATION, |
954 | | /** |
955 | | * BlockingMode END_EXISTING allows a track to be created in the destination |
956 | | * but will end it before any data has been passed through. |
957 | | */ |
958 | | END_EXISTING, |
959 | | }; |
960 | | |
961 | | /** |
962 | | * Represents a connection between a ProcessedMediaStream and one of its |
963 | | * input streams. |
964 | | * We make these refcounted so that stream-related messages with MediaInputPort* |
965 | | * pointers can be sent to the main thread safely. |
966 | | * |
967 | | * A port can be locked to a specific track in the source stream, in which case |
968 | | * only this track will be forwarded to the destination stream. TRACK_ANY |
969 | | * can used to signal that all tracks shall be forwarded. |
970 | | * |
971 | | * When a port is locked to a specific track in the source stream, it may also |
972 | | * indicate a TrackID to map this source track to in the destination stream |
973 | | * by setting aDestTrack to an explicit ID. When we do this, we must know |
974 | | * that this TrackID in the destination stream is available. We assert during |
975 | | * processing that the ID is available and that there are no generic input |
976 | | * ports already attached to the destination stream. |
977 | | * Note that this is currently only handled by TrackUnionStreams. |
978 | | * |
979 | | * When a port's source or destination stream dies, the stream's DestroyImpl |
980 | | * calls MediaInputPort::Disconnect to disconnect the port from |
981 | | * the source and destination streams. |
982 | | * |
983 | | * The lifetimes of MediaInputPort are controlled from the main thread. |
984 | | * The media graph adds a reference to the port. When a MediaInputPort is no |
985 | | * longer needed, main-thread code sends a Destroy message for the port and |
986 | | * clears its reference (the last main-thread reference to the object). When |
987 | | * the Destroy message is processed on the graph manager thread we disconnect |
988 | | * the port and drop the graph's reference, destroying the object. |
989 | | */ |
990 | | class MediaInputPort final |
991 | | { |
992 | | private: |
993 | | // Do not call this constructor directly. Instead call aDest->AllocateInputPort. |
994 | | MediaInputPort(MediaStream* aSource, |
995 | | TrackID& aSourceTrack, |
996 | | ProcessedMediaStream* aDest, |
997 | | TrackID& aDestTrack, |
998 | | uint16_t aInputNumber, |
999 | | uint16_t aOutputNumber) |
1000 | | : mSource(aSource) |
1001 | | , mSourceTrack(aSourceTrack) |
1002 | | , mDest(aDest) |
1003 | | , mDestTrack(aDestTrack) |
1004 | | , mInputNumber(aInputNumber) |
1005 | | , mOutputNumber(aOutputNumber) |
1006 | | , mGraph(nullptr) |
1007 | 0 | { |
1008 | 0 | MOZ_COUNT_CTOR(MediaInputPort); |
1009 | 0 | } |
1010 | | |
1011 | | // Private destructor, to discourage deletion outside of Release(): |
1012 | | ~MediaInputPort() |
1013 | | { |
1014 | | MOZ_COUNT_DTOR(MediaInputPort); |
1015 | | } |
1016 | | |
1017 | | public: |
1018 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort) |
1019 | | |
1020 | | // Called on graph manager thread |
1021 | | // Do not call these from outside MediaStreamGraph.cpp! |
1022 | | void Init(); |
1023 | | // Called during message processing to trigger removal of this stream. |
1024 | | void Disconnect(); |
1025 | | |
1026 | | // Control API |
1027 | | /** |
1028 | | * Disconnects and destroys the port. The caller must not reference this |
1029 | | * object again. |
1030 | | */ |
1031 | | void Destroy(); |
1032 | | |
1033 | | // Any thread |
1034 | | MediaStream* GetSource() const { return mSource; } |
1035 | | TrackID GetSourceTrackId() const { return mSourceTrack; } |
1036 | | ProcessedMediaStream* GetDestination() const { return mDest; } |
1037 | | TrackID GetDestinationTrackId() const { return mDestTrack; } |
1038 | | |
1039 | | /** |
1040 | | * Block aTrackId in the source stream from being passed through the port. |
1041 | | * Consumers will interpret this track as ended. |
1042 | | * Returns a pledge that resolves on the main thread after the track block has |
1043 | | * been applied by the MSG. |
1044 | | */ |
1045 | | already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId, |
1046 | | BlockingMode aBlockingMode); |
1047 | | private: |
1048 | | void BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode); |
1049 | | |
1050 | | public: |
1051 | | // Returns true if aTrackId has not been blocked for any reason and this port |
1052 | | // has not been locked to another track. |
1053 | | bool PassTrackThrough(TrackID aTrackId) const |
1054 | 0 | { |
1055 | 0 | bool blocked = false; |
1056 | 0 | for (auto pair : mBlockedTracks) { |
1057 | 0 | if (pair.first() == aTrackId && |
1058 | 0 | (pair.second() == BlockingMode::CREATION || |
1059 | 0 | pair.second() == BlockingMode::END_EXISTING)) { |
1060 | 0 | blocked = true; |
1061 | 0 | break; |
1062 | 0 | } |
1063 | 0 | } |
1064 | 0 | return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId); |
1065 | 0 | } |
1066 | | |
1067 | | // Returns true if aTrackId has not been blocked for track creation and this |
1068 | | // port has not been locked to another track. |
1069 | | bool AllowCreationOf(TrackID aTrackId) const |
1070 | 0 | { |
1071 | 0 | bool blocked = false; |
1072 | 0 | for (auto pair : mBlockedTracks) { |
1073 | 0 | if (pair.first() == aTrackId && |
1074 | 0 | pair.second() == BlockingMode::CREATION) { |
1075 | 0 | blocked = true; |
1076 | 0 | break; |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId); |
1080 | 0 | } |
1081 | | |
1082 | | uint16_t InputNumber() const { return mInputNumber; } |
1083 | | uint16_t OutputNumber() const { return mOutputNumber; } |
1084 | | |
1085 | | // Call on graph manager thread |
1086 | | struct InputInterval { |
1087 | | GraphTime mStart; |
1088 | | GraphTime mEnd; |
1089 | | bool mInputIsBlocked; |
1090 | | }; |
1091 | | // Find the next time interval starting at or after aTime during which |
1092 | | // mDest is not blocked and mSource's blocking status does not change. |
1093 | | InputInterval GetNextInputInterval(GraphTime aTime) const; |
1094 | | |
1095 | | /** |
1096 | | * Returns the graph that owns this port. |
1097 | | */ |
1098 | | MediaStreamGraphImpl* GraphImpl(); |
1099 | | MediaStreamGraph* Graph(); |
1100 | | |
1101 | | /** |
1102 | | * Sets the graph that owns this stream. Should only be called once. |
1103 | | */ |
1104 | | void SetGraphImpl(MediaStreamGraphImpl* aGraph); |
1105 | | |
1106 | | /** |
1107 | | * Notify the port that the source MediaStream has been suspended. |
1108 | | */ |
1109 | | void Suspended(); |
1110 | | |
1111 | | /** |
1112 | | * Notify the port that the source MediaStream has been resumed. |
1113 | | */ |
1114 | | void Resumed(); |
1115 | | |
1116 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
1117 | | { |
1118 | | size_t amount = 0; |
1119 | | |
1120 | | // Not owned: |
1121 | | // - mSource |
1122 | | // - mDest |
1123 | | // - mGraph |
1124 | | return amount; |
1125 | | } |
1126 | | |
1127 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
1128 | | { |
1129 | | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
1130 | | } |
1131 | | |
1132 | | private: |
1133 | | friend class MediaStreamGraphImpl; |
1134 | | friend class MediaStream; |
1135 | | friend class ProcessedMediaStream; |
1136 | | // Never modified after Init() |
1137 | | MediaStream* mSource; |
1138 | | TrackID mSourceTrack; |
1139 | | ProcessedMediaStream* mDest; |
1140 | | TrackID mDestTrack; |
1141 | | // The input and output numbers are optional, and are currently only used by |
1142 | | // Web Audio. |
1143 | | const uint16_t mInputNumber; |
1144 | | const uint16_t mOutputNumber; |
1145 | | |
1146 | | typedef Pair<TrackID, BlockingMode> BlockedTrack; |
1147 | | nsTArray<BlockedTrack> mBlockedTracks; |
1148 | | |
1149 | | // Our media stream graph |
1150 | | MediaStreamGraphImpl* mGraph; |
1151 | | }; |
1152 | | |
1153 | | /** |
1154 | | * This stream processes zero or more input streams in parallel to produce |
1155 | | * its output. The details of how the output is produced are handled by |
1156 | | * subclasses overriding the ProcessInput method. |
1157 | | */ |
1158 | | class ProcessedMediaStream : public MediaStream |
1159 | | { |
1160 | | public: |
1161 | | explicit ProcessedMediaStream() |
1162 | | : MediaStream() |
1163 | | , mAutofinish(false) |
1164 | | , mCycleMarker(0) |
1165 | 0 | {} |
1166 | | |
1167 | | // Control API. |
1168 | | /** |
1169 | | * Allocates a new input port attached to source aStream. |
1170 | | * This stream can be removed by calling MediaInputPort::Remove(). |
1171 | | * |
1172 | | * The input port is tied to aTrackID in the source stream. |
1173 | | * aTrackID can be set to TRACK_ANY to automatically forward all tracks from |
1174 | | * aStream. |
1175 | | * |
1176 | | * If aTrackID is an explicit ID, aDestTrackID can also be made explicit |
1177 | | * to ensure that the track is assigned this ID in the destination stream. |
1178 | | * To avoid intermittent TrackID collisions the destination stream may not |
1179 | | * have any existing generic input ports (with TRACK_ANY source track) when |
1180 | | * you allocate an input port with a destination TrackID. |
1181 | | * |
1182 | | * To end a track in the destination stream forwarded with TRACK_ANY, |
1183 | | * it can be blocked in the input port through MediaInputPort::BlockTrackId(). |
1184 | | * |
1185 | | * Tracks in aBlockedTracks will be blocked in the input port initially. This |
1186 | | * ensures that they don't get created by the MSG-thread before we can |
1187 | | * BlockTrackId() on the main thread. |
1188 | | */ |
1189 | | already_AddRefed<MediaInputPort> |
1190 | | AllocateInputPort(MediaStream* aStream, |
1191 | | TrackID aTrackID = TRACK_ANY, |
1192 | | TrackID aDestTrackID = TRACK_ANY, |
1193 | | uint16_t aInputNumber = 0, |
1194 | | uint16_t aOutputNumber = 0, |
1195 | | nsTArray<TrackID>* aBlockedTracks = nullptr); |
1196 | | /** |
1197 | | * Queue a message to force this stream into the finished state. |
1198 | | */ |
1199 | | void QueueFinish(); |
1200 | | /** |
1201 | | * Queue a message to set the autofinish flag on this stream (defaults to |
1202 | | * false). When this flag is set, and all input streams are in the finished |
1203 | | * state (including if there are no input streams), this stream automatically |
1204 | | * enters the finished state. |
1205 | | */ |
1206 | | void QueueSetAutofinish(bool aAutofinish); |
1207 | | |
1208 | 0 | ProcessedMediaStream* AsProcessedStream() override { return this; } |
1209 | | |
1210 | | friend class MediaStreamGraphImpl; |
1211 | | |
1212 | | // Do not call these from outside MediaStreamGraph.cpp! |
1213 | | virtual void AddInput(MediaInputPort* aPort); |
1214 | | virtual void RemoveInput(MediaInputPort* aPort) |
1215 | 0 | { |
1216 | 0 | mInputs.RemoveElement(aPort) || mSuspendedInputs.RemoveElement(aPort); |
1217 | 0 | } |
1218 | | bool HasInputPort(MediaInputPort* aPort) const |
1219 | 0 | { |
1220 | 0 | return mInputs.Contains(aPort) || mSuspendedInputs.Contains(aPort); |
1221 | 0 | } |
1222 | | uint32_t InputPortCount() const |
1223 | 0 | { |
1224 | 0 | return mInputs.Length() + mSuspendedInputs.Length(); |
1225 | 0 | } |
1226 | | void InputSuspended(MediaInputPort* aPort); |
1227 | | void InputResumed(MediaInputPort* aPort); |
1228 | 0 | virtual MediaStream* GetInputStreamFor(TrackID aTrackID) { return nullptr; } |
1229 | 0 | virtual TrackID GetInputTrackIDFor(TrackID aTrackID) { return TRACK_NONE; } |
1230 | | void DestroyImpl() override; |
1231 | | /** |
1232 | | * This gets called after we've computed the blocking states for all |
1233 | | * streams (mBlocked is up to date up to mStateComputedTime). |
1234 | | * Also, we've produced output for all streams up to this one. If this stream |
1235 | | * is not in a cycle, then all its source streams have produced data. |
1236 | | * Generate output from aFrom to aTo. |
1237 | | * This will be called on streams that have finished. Most stream types should |
1238 | | * just return immediately if IsFinishedOnGraphThread(), but some may wish to |
1239 | | * update internal state (see AudioNodeStream). |
1240 | | * ProcessInput is allowed to call FinishOnGraphThread only if ALLOW_FINISH |
1241 | | * is in aFlags. (This flag will be set when aTo >= mStateComputedTime, i.e. |
1242 | | * when we've producing the last block of data we need to produce.) Otherwise |
1243 | | * we can get into a situation where we've determined the stream should not |
1244 | | * block before mStateComputedTime, but the stream finishes before |
1245 | | * mStateComputedTime, violating the invariant that finished streams are blocked. |
1246 | | */ |
1247 | | enum { |
1248 | | ALLOW_FINISH = 0x01 |
1249 | | }; |
1250 | | virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0; |
1251 | 0 | void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; } |
1252 | | |
1253 | | // Only valid after MediaStreamGraphImpl::UpdateStreamOrder() has run. |
1254 | | // A DelayNode is considered to break a cycle and so this will not return |
1255 | | // true for echo loops, only for muted cycles. |
1256 | 0 | bool InMutedCycle() const { return mCycleMarker; } |
1257 | | |
1258 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override |
1259 | 0 | { |
1260 | 0 | size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf); |
1261 | 0 | // Not owned: |
1262 | 0 | // - mInputs elements |
1263 | 0 | // - mSuspendedInputs elements |
1264 | 0 | amount += mInputs.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1265 | 0 | amount += mSuspendedInputs.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1266 | 0 | return amount; |
1267 | 0 | } |
1268 | | |
1269 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override |
1270 | 0 | { |
1271 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
1272 | 0 | } |
1273 | | |
1274 | | protected: |
1275 | | // This state is all accessed only on the media graph thread. |
1276 | | |
1277 | | // The list of all inputs that are not currently suspended. |
1278 | | nsTArray<MediaInputPort*> mInputs; |
1279 | | // The list of all inputs that are currently suspended. |
1280 | | nsTArray<MediaInputPort*> mSuspendedInputs; |
1281 | | bool mAutofinish; |
1282 | | // After UpdateStreamOrder(), mCycleMarker is either 0 or 1 to indicate |
1283 | | // whether this stream is in a muted cycle. During ordering it can contain |
1284 | | // other marker values - see MediaStreamGraphImpl::UpdateStreamOrder(). |
1285 | | uint32_t mCycleMarker; |
1286 | | }; |
1287 | | |
1288 | | /** |
1289 | | * There is a single MediaStreamGraph per window. |
1290 | | * Additionaly, each OfflineAudioContext object creates its own MediaStreamGraph |
1291 | | * object too.. |
1292 | | */ |
1293 | | class MediaStreamGraph |
1294 | | { |
1295 | | public: |
1296 | | |
1297 | | // We ensure that the graph current time advances in multiples of |
1298 | | // IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that |
1299 | | // never blocks and has a track with the ideal audio rate will produce audio |
1300 | | // in multiples of the block size. |
1301 | | |
1302 | | // Initializing an graph that outputs audio can be quite long on some |
1303 | | // platforms. Code that want to output audio at some point can express the |
1304 | | // fact that they will need an audio stream at some point by passing |
1305 | | // AUDIO_THREAD_DRIVER when getting an instance of MediaStreamGraph, so that |
1306 | | // the graph starts with the right driver. |
1307 | | enum GraphDriverType { |
1308 | | AUDIO_THREAD_DRIVER, |
1309 | | SYSTEM_THREAD_DRIVER, |
1310 | | OFFLINE_THREAD_DRIVER |
1311 | | }; |
1312 | | static const uint32_t AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT = 20*1000; |
1313 | | static const TrackRate REQUEST_DEFAULT_SAMPLE_RATE = 0; |
1314 | | |
1315 | | // Main thread only |
1316 | | static MediaStreamGraph* GetInstanceIfExists(nsPIDOMWindowInner* aWindow, |
1317 | | TrackRate aSampleRate); |
1318 | | static MediaStreamGraph* GetInstance(GraphDriverType aGraphDriverRequested, |
1319 | | nsPIDOMWindowInner* aWindow, |
1320 | | TrackRate aSampleRate); |
1321 | | static MediaStreamGraph* CreateNonRealtimeInstance( |
1322 | | TrackRate aSampleRate, |
1323 | | nsPIDOMWindowInner* aWindowId); |
1324 | | |
1325 | | // Return the correct main thread for this graph. This always returns |
1326 | | // something that is valid. Thread safe. |
1327 | | AbstractThread* AbstractMainThread(); |
1328 | | |
1329 | | // Idempotent |
1330 | | static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph); |
1331 | | |
1332 | | virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID, |
1333 | | AudioDataListener* aListener) = 0; |
1334 | | virtual void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID, |
1335 | | AudioDataListener* aListener) = 0; |
1336 | | // Control API. |
1337 | | /** |
1338 | | * Create a stream that a media decoder (or some other source of |
1339 | | * media data, such as a camera) can write to. |
1340 | | */ |
1341 | | SourceMediaStream* CreateSourceStream(); |
1342 | | /** |
1343 | | * Create a stream that will form the union of the tracks of its input |
1344 | | * streams. |
1345 | | * A TrackUnionStream contains all the tracks of all its input streams. |
1346 | | * Adding a new input stream makes that stream's tracks immediately appear as new |
1347 | | * tracks starting at the time the input stream was added. |
1348 | | * Removing an input stream makes the output tracks corresponding to the |
1349 | | * removed tracks immediately end. |
1350 | | * For each added track, the track ID of the output track is the track ID |
1351 | | * of the input track or one plus the maximum ID of all previously added |
1352 | | * tracks, whichever is greater. |
1353 | | * TODO at some point we will probably need to add API to select |
1354 | | * particular tracks of each input stream. |
1355 | | */ |
1356 | | ProcessedMediaStream* CreateTrackUnionStream(); |
1357 | | /** |
1358 | | * Create a stream that will mix all its audio input. |
1359 | | */ |
1360 | | ProcessedMediaStream* CreateAudioCaptureStream(TrackID aTrackId); |
1361 | | |
1362 | | /** |
1363 | | * Add a new stream to the graph. Main thread. |
1364 | | */ |
1365 | | void AddStream(MediaStream* aStream); |
1366 | | |
1367 | | /* From the main thread, ask the MSG to send back an event when the graph |
1368 | | * thread is running, and audio is being processed. */ |
1369 | | void NotifyWhenGraphStarted(AudioNodeStream* aNodeStream); |
1370 | | /* From the main thread, suspend, resume or close an AudioContext. |
1371 | | * aStreams are the streams of all the AudioNodes of the AudioContext that |
1372 | | * need to be suspended or resumed. This can be empty if this is a second |
1373 | | * consecutive suspend call and all the nodes are already suspended. |
1374 | | * |
1375 | | * This can possibly pause the graph thread, releasing system resources, if |
1376 | | * all streams have been suspended/closed. |
1377 | | * |
1378 | | * When the operation is complete, aPromise is resolved. |
1379 | | */ |
1380 | | void ApplyAudioContextOperation(MediaStream* aDestinationStream, |
1381 | | const nsTArray<MediaStream*>& aStreams, |
1382 | | dom::AudioContextOperation aState, |
1383 | | void* aPromise); |
1384 | | |
1385 | | bool IsNonRealtime() const; |
1386 | | /** |
1387 | | * Start processing non-realtime for a specific number of ticks. |
1388 | | */ |
1389 | | void StartNonRealtimeProcessing(uint32_t aTicksToProcess); |
1390 | | |
1391 | | /** |
1392 | | * Media graph thread only. |
1393 | | * Dispatches a runnable that will run on the main thread after all |
1394 | | * main-thread stream state has been next updated. |
1395 | | * |
1396 | | * Should only be called during MediaStreamListener callbacks or during |
1397 | | * ProcessedMediaStream::ProcessInput(). |
1398 | | */ |
1399 | | virtual void DispatchToMainThreadAfterStreamStateUpdate( |
1400 | | already_AddRefed<nsIRunnable> aRunnable); |
1401 | | |
1402 | | /** |
1403 | | * Returns graph sample rate in Hz. |
1404 | | */ |
1405 | | TrackRate GraphRate() const { return mSampleRate; } |
1406 | | |
1407 | | void RegisterCaptureStreamForWindow(uint64_t aWindowId, |
1408 | | ProcessedMediaStream* aCaptureStream); |
1409 | | void UnregisterCaptureStreamForWindow(uint64_t aWindowId); |
1410 | | already_AddRefed<MediaInputPort> ConnectToCaptureStream( |
1411 | | uint64_t aWindowId, MediaStream* aMediaStream); |
1412 | | |
1413 | | void AssertOnGraphThreadOrNotRunning() const |
1414 | 0 | { |
1415 | 0 | MOZ_ASSERT(OnGraphThreadOrNotRunning()); |
1416 | 0 | } |
1417 | | |
1418 | | protected: |
1419 | | explicit MediaStreamGraph(TrackRate aSampleRate) |
1420 | | : mSampleRate(aSampleRate) |
1421 | 0 | { |
1422 | 0 | MOZ_COUNT_CTOR(MediaStreamGraph); |
1423 | 0 | } |
1424 | | virtual ~MediaStreamGraph() |
1425 | 0 | { |
1426 | 0 | MOZ_COUNT_DTOR(MediaStreamGraph); |
1427 | 0 | } |
1428 | | |
1429 | | // Intended only for assertions, either on graph thread or not running (in |
1430 | | // which case we must be on the main thread). |
1431 | | bool OnGraphThreadOrNotRunning() const; |
1432 | | bool OnGraphThread() const; |
1433 | | |
1434 | | // Media graph thread only |
1435 | | nsTArray<nsCOMPtr<nsIRunnable> > mPendingUpdateRunnables; |
1436 | | |
1437 | | /** |
1438 | | * Sample rate at which this graph runs. For real time graphs, this is |
1439 | | * the rate of the audio mixer. For offline graphs, this is the rate specified |
1440 | | * at construction. |
1441 | | */ |
1442 | | TrackRate mSampleRate; |
1443 | | }; |
1444 | | |
1445 | | } // namespace mozilla |
1446 | | |
1447 | | #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */ |