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