Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 #ifndef MOZILLA_MEDIASTREAMGRAPHIMPL_H_
7 #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_
9 #include "MediaStreamGraph.h"
11 #include "mozilla/Monitor.h"
12 #include "mozilla/TimeStamp.h"
13 #include "nsIMemoryReporter.h"
14 #include "nsIThread.h"
15 #include "nsIRunnable.h"
16 #include "Latency.h"
17 #include "mozilla/WeakPtr.h"
19 namespace mozilla {
21 template <typename T>
22 class LinkedList;
24 class AudioMixer;
26 /**
27 * Assume we can run an iteration of the MediaStreamGraph loop in this much time
28 * or less.
29 * We try to run the control loop at this rate.
30 */
31 static const int MEDIA_GRAPH_TARGET_PERIOD_MS = 10;
33 /**
34 * Assume that we might miss our scheduled wakeup of the MediaStreamGraph by
35 * this much.
36 */
37 static const int SCHEDULE_SAFETY_MARGIN_MS = 10;
39 /**
40 * Try have this much audio buffered in streams and queued to the hardware.
41 * The maximum delay to the end of the next control loop
42 * is 2*MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS.
43 * There is no point in buffering more audio than this in a stream at any
44 * given time (until we add processing).
45 * This is not optimal yet.
46 */
47 static const int AUDIO_TARGET_MS = 2*MEDIA_GRAPH_TARGET_PERIOD_MS +
48 SCHEDULE_SAFETY_MARGIN_MS;
50 /**
51 * Try have this much video buffered. Video frames are set
52 * near the end of the iteration of the control loop. The maximum delay
53 * to the setting of the next video frame is 2*MEDIA_GRAPH_TARGET_PERIOD_MS +
54 * SCHEDULE_SAFETY_MARGIN_MS. This is not optimal yet.
55 */
56 static const int VIDEO_TARGET_MS = 2*MEDIA_GRAPH_TARGET_PERIOD_MS +
57 SCHEDULE_SAFETY_MARGIN_MS;
59 /**
60 * A per-stream update message passed from the media graph thread to the
61 * main thread.
62 */
63 struct StreamUpdate {
64 int64_t mGraphUpdateIndex;
65 nsRefPtr<MediaStream> mStream;
66 StreamTime mNextMainThreadCurrentTime;
67 bool mNextMainThreadFinished;
68 };
70 /**
71 * This represents a message passed from the main thread to the graph thread.
72 * A ControlMessage always has a weak reference a particular affected stream.
73 */
74 class ControlMessage {
75 public:
76 explicit ControlMessage(MediaStream* aStream) : mStream(aStream)
77 {
78 MOZ_COUNT_CTOR(ControlMessage);
79 }
80 // All these run on the graph thread
81 virtual ~ControlMessage()
82 {
83 MOZ_COUNT_DTOR(ControlMessage);
84 }
85 // Do the action of this message on the MediaStreamGraph thread. Any actions
86 // affecting graph processing should take effect at mStateComputedTime.
87 // All stream data for times < mStateComputedTime has already been
88 // computed.
89 virtual void Run() = 0;
90 // When we're shutting down the application, most messages are ignored but
91 // some cleanup messages should still be processed (on the main thread).
92 // This must not add new control messages to the graph.
93 virtual void RunDuringShutdown() {}
94 MediaStream* GetStream() { return mStream; }
96 protected:
97 // We do not hold a reference to mStream. The graph will be holding
98 // a reference to the stream until the Destroy message is processed. The
99 // last message referencing a stream is the Destroy message for that stream.
100 MediaStream* mStream;
101 };
103 /**
104 * The implementation of a media stream graph. This class is private to this
105 * file. It's not in the anonymous namespace because MediaStream needs to
106 * be able to friend it.
107 *
108 * Currently we have one global instance per process, and one per each
109 * OfflineAudioContext object.
110 */
111 class MediaStreamGraphImpl : public MediaStreamGraph,
112 public nsIMemoryReporter {
113 public:
114 NS_DECL_ISUPPORTS
115 NS_DECL_NSIMEMORYREPORTER
117 /**
118 * Set aRealtime to true in order to create a MediaStreamGraph which provides
119 * support for real-time audio and video. Set it to false in order to create
120 * a non-realtime instance which just churns through its inputs and produces
121 * output. Those objects currently only support audio, and are used to
122 * implement OfflineAudioContext. They do not support MediaStream inputs.
123 */
124 explicit MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate);
126 /**
127 * Unregisters memory reporting and deletes this instance. This should be
128 * called instead of calling the destructor directly.
129 */
130 void Destroy();
132 // Main thread only.
133 /**
134 * This runs every time we need to sync state from the media graph thread
135 * to the main thread while the main thread is not in the middle
136 * of a script. It runs during a "stable state" (per HTML5) or during
137 * an event posted to the main thread.
138 */
139 void RunInStableState();
140 /**
141 * Ensure a runnable to run RunInStableState is posted to the appshell to
142 * run at the next stable state (per HTML5).
143 * See EnsureStableStateEventPosted.
144 */
145 void EnsureRunInStableState();
146 /**
147 * Called to apply a StreamUpdate to its stream.
148 */
149 void ApplyStreamUpdate(StreamUpdate* aUpdate);
150 /**
151 * Append a ControlMessage to the message queue. This queue is drained
152 * during RunInStableState; the messages will run on the graph thread.
153 */
154 void AppendMessage(ControlMessage* aMessage);
155 /**
156 * Make this MediaStreamGraph enter forced-shutdown state. This state
157 * will be noticed by the media graph thread, which will shut down all streams
158 * and other state controlled by the media graph thread.
159 * This is called during application shutdown.
160 */
161 void ForceShutDown();
162 /**
163 * Shutdown() this MediaStreamGraph's threads and return when they've shut down.
164 */
165 void ShutdownThreads();
167 /**
168 * Called before the thread runs.
169 */
170 void Init();
171 // The following methods run on the graph thread (or possibly the main thread if
172 // mLifecycleState > LIFECYCLE_RUNNING)
173 /**
174 * Runs main control loop on the graph thread. Normally a single invocation
175 * of this runs for the entire lifetime of the graph thread.
176 */
177 void RunThread();
178 /**
179 * Call this to indicate that another iteration of the control loop is
180 * required on its regular schedule. The monitor must not be held.
181 */
182 void EnsureNextIteration();
183 /**
184 * As above, but with the monitor already held.
185 */
186 void EnsureNextIterationLocked(MonitorAutoLock& aLock);
187 /**
188 * Call this to indicate that another iteration of the control loop is
189 * required immediately. The monitor must already be held.
190 */
191 void EnsureImmediateWakeUpLocked(MonitorAutoLock& aLock);
192 /**
193 * Ensure there is an event posted to the main thread to run RunInStableState.
194 * mMonitor must be held.
195 * See EnsureRunInStableState
196 */
197 void EnsureStableStateEventPosted();
198 /**
199 * Generate messages to the main thread to update it for all state changes.
200 * mMonitor must be held.
201 */
202 void PrepareUpdatesToMainThreadState(bool aFinalUpdate);
203 /**
204 * Returns false if there is any stream that has finished but not yet finished
205 * playing out.
206 */
207 bool AllFinishedStreamsNotified();
208 /**
209 * If we are rendering in non-realtime mode, we don't want to send messages to
210 * the main thread at each iteration for performance reasons. We instead
211 * notify the main thread at the same rate
212 */
213 bool ShouldUpdateMainThread();
214 // The following methods are the various stages of RunThread processing.
215 /**
216 * Compute a new current time for the graph and advance all on-graph-thread
217 * state to the new current time.
218 */
219 void UpdateCurrentTime();
220 /**
221 * Update the consumption state of aStream to reflect whether its data
222 * is needed or not.
223 */
224 void UpdateConsumptionState(SourceMediaStream* aStream);
225 /**
226 * Extract any state updates pending in aStream, and apply them.
227 */
228 void ExtractPendingInput(SourceMediaStream* aStream,
229 GraphTime aDesiredUpToTime,
230 bool* aEnsureNextIteration);
231 /**
232 * Update "have enough data" flags in aStream.
233 */
234 void UpdateBufferSufficiencyState(SourceMediaStream* aStream);
235 /*
236 * If aStream hasn't already been ordered, push it onto aStack and order
237 * its children.
238 */
239 void UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
240 already_AddRefed<MediaStream> aStream);
241 /**
242 * Mark aStream and all its inputs (recursively) as consumed.
243 */
244 static void MarkConsumed(MediaStream* aStream);
245 /**
246 * Sort mStreams so that every stream not in a cycle is after any streams
247 * it depends on, and every stream in a cycle is marked as being in a cycle.
248 * Also sets mIsConsumed on every stream.
249 */
250 void UpdateStreamOrder();
251 /**
252 * Compute the blocking states of streams from mStateComputedTime
253 * until the desired future time aEndBlockingDecisions.
254 * Updates mStateComputedTime and sets MediaStream::mBlocked
255 * for all streams.
256 */
257 void RecomputeBlocking(GraphTime aEndBlockingDecisions);
258 // The following methods are used to help RecomputeBlocking.
259 /**
260 * If aStream isn't already in aStreams, add it and recursively call
261 * AddBlockingRelatedStreamsToSet on all the streams whose blocking
262 * status could depend on or affect the state of aStream.
263 */
264 void AddBlockingRelatedStreamsToSet(nsTArray<MediaStream*>* aStreams,
265 MediaStream* aStream);
266 /**
267 * Mark a stream blocked at time aTime. If this results in decisions that need
268 * to be revisited at some point in the future, *aEnd will be reduced to the
269 * first time in the future to recompute those decisions.
270 */
271 void MarkStreamBlocking(MediaStream* aStream);
272 /**
273 * Recompute blocking for the streams in aStreams for the interval starting at aTime.
274 * If this results in decisions that need to be revisited at some point
275 * in the future, *aEnd will be reduced to the first time in the future to
276 * recompute those decisions.
277 */
278 void RecomputeBlockingAt(const nsTArray<MediaStream*>& aStreams,
279 GraphTime aTime, GraphTime aEndBlockingDecisions,
280 GraphTime* aEnd);
281 /**
282 * Produce data for all streams >= aStreamIndex for the given time interval.
283 * Advances block by block, each iteration producing data for all streams
284 * for a single block.
285 * This is called whenever we have an AudioNodeStream in the graph.
286 */
287 void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
288 TrackRate aSampleRate,
289 GraphTime aFrom,
290 GraphTime aTo);
291 /**
292 * Returns true if aStream will underrun at aTime for its own playback.
293 * aEndBlockingDecisions is when we plan to stop making blocking decisions.
294 * *aEnd will be reduced to the first time in the future to recompute these
295 * decisions.
296 */
297 bool WillUnderrun(MediaStream* aStream, GraphTime aTime,
298 GraphTime aEndBlockingDecisions, GraphTime* aEnd);
299 /**
300 * Given a graph time aTime, convert it to a stream time taking into
301 * account the time during which aStream is scheduled to be blocked.
302 */
303 StreamTime GraphTimeToStreamTime(MediaStream* aStream, GraphTime aTime);
304 /**
305 * Given a graph time aTime, convert it to a stream time taking into
306 * account the time during which aStream is scheduled to be blocked, and
307 * when we don't know whether it's blocked or not, we assume it's not blocked.
308 */
309 StreamTime GraphTimeToStreamTimeOptimistic(MediaStream* aStream, GraphTime aTime);
310 enum {
311 INCLUDE_TRAILING_BLOCKED_INTERVAL = 0x01
312 };
313 /**
314 * Given a stream time aTime, convert it to a graph time taking into
315 * account the time during which aStream is scheduled to be blocked.
316 * aTime must be <= mStateComputedTime since blocking decisions
317 * are only known up to that point.
318 * If aTime is exactly at the start of a blocked interval, then the blocked
319 * interval is included in the time returned if and only if
320 * aFlags includes INCLUDE_TRAILING_BLOCKED_INTERVAL.
321 */
322 GraphTime StreamTimeToGraphTime(MediaStream* aStream, StreamTime aTime,
323 uint32_t aFlags = 0);
324 /**
325 * Get the current audio position of the stream's audio output.
326 */
327 GraphTime GetAudioPosition(MediaStream* aStream);
328 /**
329 * Call NotifyHaveCurrentData on aStream's listeners.
330 */
331 void NotifyHasCurrentData(MediaStream* aStream);
332 /**
333 * If aStream needs an audio stream but doesn't have one, create it.
334 * If aStream doesn't need an audio stream but has one, destroy it.
335 */
336 void CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
337 MediaStream* aStream);
338 /**
339 * Queue audio (mix of stream audio and silence for blocked intervals)
340 * to the audio output stream. Returns the number of frames played.
341 */
342 TrackTicks PlayAudio(MediaStream* aStream, GraphTime aFrom, GraphTime aTo);
343 /**
344 * Set the correct current video frame for stream aStream.
345 */
346 void PlayVideo(MediaStream* aStream);
347 /**
348 * No more data will be forthcoming for aStream. The stream will end
349 * at the current buffer end point. The StreamBuffer's tracks must be
350 * explicitly set to finished by the caller.
351 */
352 void FinishStream(MediaStream* aStream);
353 /**
354 * Compute how much stream data we would like to buffer for aStream.
355 */
356 StreamTime GetDesiredBufferEnd(MediaStream* aStream);
357 /**
358 * Returns true when there are no active streams.
359 */
360 bool IsEmpty() { return mStreams.IsEmpty() && mPortCount == 0; }
362 // For use by control messages, on graph thread only.
363 /**
364 * Identify which graph update index we are currently processing.
365 */
366 int64_t GetProcessingGraphUpdateIndex() { return mProcessingGraphUpdateIndex; }
367 /**
368 * Add aStream to the graph and initializes its graph-specific state.
369 */
370 void AddStream(MediaStream* aStream);
371 /**
372 * Remove aStream from the graph. Ensures that pending messages about the
373 * stream back to the main thread are flushed.
374 */
375 void RemoveStream(MediaStream* aStream);
376 /**
377 * Remove aPort from the graph and release it.
378 */
379 void DestroyPort(MediaInputPort* aPort);
380 /**
381 * Mark the media stream order as dirty.
382 */
383 void SetStreamOrderDirty()
384 {
385 mStreamOrderDirty = true;
386 }
387 /**
388 * Pause all AudioStreams being written to by MediaStreams
389 */
390 void PauseAllAudioOutputs();
391 /**
392 * Resume all AudioStreams being written to by MediaStreams
393 */
394 void ResumeAllAudioOutputs();
396 TrackRate AudioSampleRate() { return mSampleRate; }
398 // Data members
400 /**
401 * Media graph thread.
402 * Readonly after initialization on the main thread.
403 */
404 nsCOMPtr<nsIThread> mThread;
406 // The following state is managed on the graph thread only, unless
407 // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread
408 // is not running and this state can be used from the main thread.
410 nsTArray<nsRefPtr<MediaStream> > mStreams;
411 /**
412 * mOldStreams is used as temporary storage for streams when computing the
413 * order in which we compute them.
414 */
415 nsTArray<nsRefPtr<MediaStream> > mOldStreams;
416 /**
417 * The current graph time for the current iteration of the RunThread control
418 * loop.
419 */
420 GraphTime mCurrentTime;
421 /**
422 * Blocking decisions and all stream contents have been computed up to this
423 * time. The next batch of updates from the main thread will be processed
424 * at this time. Always >= mCurrentTime.
425 */
426 GraphTime mStateComputedTime;
427 /**
428 * This is only used for logging.
429 */
430 TimeStamp mInitialTimeStamp;
431 /**
432 * The real timestamp of the latest run of UpdateCurrentTime.
433 */
434 TimeStamp mCurrentTimeStamp;
435 /**
436 * Date of the last time we updated the main thread with the graph state.
437 */
438 TimeStamp mLastMainThreadUpdate;
439 /**
440 * Which update batch we are currently processing.
441 */
442 int64_t mProcessingGraphUpdateIndex;
443 /**
444 * Number of active MediaInputPorts
445 */
446 int32_t mPortCount;
448 // mMonitor guards the data below.
449 // MediaStreamGraph normally does its work without holding mMonitor, so it is
450 // not safe to just grab mMonitor from some thread and start monkeying with
451 // the graph. Instead, communicate with the graph thread using provided
452 // mechanisms such as the ControlMessage queue.
453 Monitor mMonitor;
455 // Data guarded by mMonitor (must always be accessed with mMonitor held,
456 // regardless of the value of mLifecycleState.
458 /**
459 * State to copy to main thread
460 */
461 nsTArray<StreamUpdate> mStreamUpdates;
462 /**
463 * Runnables to run after the next update to main thread state.
464 */
465 nsTArray<nsCOMPtr<nsIRunnable> > mUpdateRunnables;
466 struct MessageBlock {
467 int64_t mGraphUpdateIndex;
468 nsTArray<nsAutoPtr<ControlMessage> > mMessages;
469 };
470 /**
471 * A list of batches of messages to process. Each batch is processed
472 * as an atomic unit.
473 */
474 nsTArray<MessageBlock> mMessageQueue;
475 /**
476 * This enum specifies where this graph is in its lifecycle. This is used
477 * to control shutdown.
478 * Shutdown is tricky because it can happen in two different ways:
479 * 1) Shutdown due to inactivity. RunThread() detects that it has no
480 * pending messages and no streams, and exits. The next RunInStableState()
481 * checks if there are new pending messages from the main thread (true only
482 * if new stream creation raced with shutdown); if there are, it revives
483 * RunThread(), otherwise it commits to shutting down the graph. New stream
484 * creation after this point will create a new graph. An async event is
485 * dispatched to Shutdown() the graph's threads and then delete the graph
486 * object.
487 * 2) Forced shutdown at application shutdown, or completion of a
488 * non-realtime graph. A flag is set, RunThread() detects the flag and
489 * exits, the next RunInStableState() detects the flag, and dispatches the
490 * async event to Shutdown() the graph's threads. However the graph object
491 * is not deleted. New messages for the graph are processed synchronously on
492 * the main thread if necessary. When the last stream is destroyed, the
493 * graph object is deleted.
494 */
495 enum LifecycleState {
496 // The graph thread hasn't started yet.
497 LIFECYCLE_THREAD_NOT_STARTED,
498 // RunThread() is running normally.
499 LIFECYCLE_RUNNING,
500 // In the following states, the graph thread is not running so
501 // all "graph thread only" state in this class can be used safely
502 // on the main thread.
503 // RunThread() has exited and we're waiting for the next
504 // RunInStableState(), at which point we can clean up the main-thread
505 // side of the graph.
506 LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP,
507 // RunInStableState() posted a ShutdownRunnable, and we're waiting for it
508 // to shut down the graph thread(s).
509 LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN,
510 // Graph threads have shut down but we're waiting for remaining streams
511 // to be destroyed. Only happens during application shutdown and on
512 // completed non-realtime graphs, since normally we'd only shut down a
513 // realtime graph when it has no streams.
514 LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
515 };
516 LifecycleState mLifecycleState;
517 /**
518 * This enum specifies the wait state of the graph thread.
519 */
520 enum WaitState {
521 // RunThread() is running normally
522 WAITSTATE_RUNNING,
523 // RunThread() is paused waiting for its next iteration, which will
524 // happen soon
525 WAITSTATE_WAITING_FOR_NEXT_ITERATION,
526 // RunThread() is paused indefinitely waiting for something to change
527 WAITSTATE_WAITING_INDEFINITELY,
528 // Something has signaled RunThread() to wake up immediately,
529 // but it hasn't done so yet
530 WAITSTATE_WAKING_UP
531 };
532 WaitState mWaitState;
533 /**
534 * The graph should stop processing at or after this time.
535 */
536 GraphTime mEndTime;
538 /**
539 * Sample rate at which this graph runs. For real time graphs, this is
540 * the rate of the audio mixer. For offline graphs, this is the rate specified
541 * at construction.
542 */
543 TrackRate mSampleRate;
544 /**
545 * True when another iteration of the control loop is required.
546 */
547 bool mNeedAnotherIteration;
548 /**
549 * True when we need to do a forced shutdown during application shutdown.
550 */
551 bool mForceShutDown;
552 /**
553 * True when we have posted an event to the main thread to run
554 * RunInStableState() and the event hasn't run yet.
555 */
556 bool mPostedRunInStableStateEvent;
558 // Main thread only
560 /**
561 * Messages posted by the current event loop task. These are forwarded to
562 * the media graph thread during RunInStableState. We can't forward them
563 * immediately because we want all messages between stable states to be
564 * processed as an atomic batch.
565 */
566 nsTArray<nsAutoPtr<ControlMessage> > mCurrentTaskMessageQueue;
567 /**
568 * True when RunInStableState has determined that mLifecycleState is >
569 * LIFECYCLE_RUNNING. Since only the main thread can reset mLifecycleState to
570 * LIFECYCLE_RUNNING, this can be relied on to not change unexpectedly.
571 */
572 bool mDetectedNotRunning;
573 /**
574 * True when a stable state runner has been posted to the appshell to run
575 * RunInStableState at the next stable state.
576 */
577 bool mPostedRunInStableState;
578 /**
579 * True when processing real-time audio/video. False when processing non-realtime
580 * audio.
581 */
582 bool mRealtime;
583 /**
584 * True when a non-realtime MediaStreamGraph has started to process input. This
585 * value is only accessed on the main thread.
586 */
587 bool mNonRealtimeProcessing;
588 /**
589 * True when a change has happened which requires us to recompute the stream
590 * blocking order.
591 */
592 bool mStreamOrderDirty;
593 /**
594 * Hold a ref to the Latency logger
595 */
596 nsRefPtr<AsyncLatencyLogger> mLatencyLog;
597 /**
598 * If this is not null, all the audio output for the MSG will be mixed down.
599 */
600 nsAutoPtr<AudioMixer> mMixer;
602 private:
603 virtual ~MediaStreamGraphImpl();
605 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
607 /**
608 * Used to signal that a memory report has been requested.
609 */
610 Monitor mMemoryReportMonitor;
611 /**
612 * This class uses manual memory management, and all pointers to it are raw
613 * pointers. However, in order for it to implement nsIMemoryReporter, it needs
614 * to implement nsISupports and so be ref-counted. So it maintains a single
615 * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime,
616 * and Destroy() nulls this self-reference in order to trigger self-deletion.
617 */
618 nsRefPtr<MediaStreamGraphImpl> mSelfRef;
619 /**
620 * Used to pass memory report information across threads.
621 */
622 nsTArray<AudioNodeSizes> mAudioStreamSizes;
623 /**
624 * Indicates that the MSG thread should gather data for a memory report.
625 */
626 bool mNeedsMemoryReport;
628 #ifdef DEBUG
629 /**
630 * Used to assert when AppendMessage() runs ControlMessages synchronously.
631 */
632 bool mCanRunMessagesSynchronously;
633 #endif
635 };
637 }
639 #endif /* MEDIASTREAMGRAPHIMPL_H_ */