content/media/MediaStreamGraph.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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_MEDIASTREAMGRAPH_H_
michael@0 7 #define MOZILLA_MEDIASTREAMGRAPH_H_
michael@0 8
michael@0 9 #include "mozilla/Mutex.h"
michael@0 10 #include "mozilla/LinkedList.h"
michael@0 11 #include "AudioStream.h"
michael@0 12 #include "nsTArray.h"
michael@0 13 #include "nsIRunnable.h"
michael@0 14 #include "StreamBuffer.h"
michael@0 15 #include "TimeVarying.h"
michael@0 16 #include "VideoFrameContainer.h"
michael@0 17 #include "VideoSegment.h"
michael@0 18 #include "MainThreadUtils.h"
michael@0 19 #include "nsAutoRef.h"
michael@0 20 #include "speex/speex_resampler.h"
michael@0 21 #include "AudioMixer.h"
michael@0 22 #include "mozilla/dom/AudioChannelBinding.h"
michael@0 23
michael@0 24 class nsIRunnable;
michael@0 25
michael@0 26 template <>
michael@0 27 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
michael@0 28 {
michael@0 29 public:
michael@0 30 static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
michael@0 31 };
michael@0 32
michael@0 33 namespace mozilla {
michael@0 34
michael@0 35 class DOMMediaStream;
michael@0 36
michael@0 37 #ifdef PR_LOGGING
michael@0 38 extern PRLogModuleInfo* gMediaStreamGraphLog;
michael@0 39 #endif
michael@0 40
michael@0 41 /**
michael@0 42 * Microseconds relative to the start of the graph timeline.
michael@0 43 */
michael@0 44 typedef int64_t GraphTime;
michael@0 45 const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX;
michael@0 46
michael@0 47 /*
michael@0 48 * MediaStreamGraph is a framework for synchronized audio/video processing
michael@0 49 * and playback. It is designed to be used by other browser components such as
michael@0 50 * HTML media elements, media capture APIs, real-time media streaming APIs,
michael@0 51 * multitrack media APIs, and advanced audio APIs.
michael@0 52 *
michael@0 53 * The MediaStreamGraph uses a dedicated thread to process media --- the media
michael@0 54 * graph thread. This ensures that we can process media through the graph
michael@0 55 * without blocking on main-thread activity. The media graph is only modified
michael@0 56 * on the media graph thread, to ensure graph changes can be processed without
michael@0 57 * interfering with media processing. All interaction with the media graph
michael@0 58 * thread is done with message passing.
michael@0 59 *
michael@0 60 * APIs that modify the graph or its properties are described as "control APIs".
michael@0 61 * These APIs are asynchronous; they queue graph changes internally and
michael@0 62 * those changes are processed all-at-once by the MediaStreamGraph. The
michael@0 63 * MediaStreamGraph monitors the main thread event loop via nsIAppShell::RunInStableState
michael@0 64 * to ensure that graph changes from a single event loop task are always
michael@0 65 * processed all together. Control APIs should only be used on the main thread,
michael@0 66 * currently; we may be able to relax that later.
michael@0 67 *
michael@0 68 * To allow precise synchronization of times in the control API, the
michael@0 69 * MediaStreamGraph maintains a "media timeline". Control APIs that take or
michael@0 70 * return times use that timeline. Those times never advance during
michael@0 71 * an event loop task. This time is returned by MediaStreamGraph::GetCurrentTime().
michael@0 72 *
michael@0 73 * Media decoding, audio processing and media playback use thread-safe APIs to
michael@0 74 * the media graph to ensure they can continue while the main thread is blocked.
michael@0 75 *
michael@0 76 * When the graph is changed, we may need to throw out buffered data and
michael@0 77 * reprocess it. This is triggered automatically by the MediaStreamGraph.
michael@0 78 */
michael@0 79
michael@0 80 class MediaStreamGraph;
michael@0 81
michael@0 82 /**
michael@0 83 * This is a base class for media graph thread listener callbacks.
michael@0 84 * Override methods to be notified of audio or video data or changes in stream
michael@0 85 * state.
michael@0 86 *
michael@0 87 * This can be used by stream recorders or network connections that receive
michael@0 88 * stream input. It could also be used for debugging.
michael@0 89 *
michael@0 90 * All notification methods are called from the media graph thread. Overriders
michael@0 91 * of these methods are responsible for all synchronization. Beware!
michael@0 92 * These methods are called without the media graph monitor held, so
michael@0 93 * reentry into media graph methods is possible, although very much discouraged!
michael@0 94 * You should do something non-blocking and non-reentrant (e.g. dispatch an
michael@0 95 * event to some thread) and return.
michael@0 96 * The listener is not allowed to add/remove any listeners from the stream.
michael@0 97 *
michael@0 98 * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
michael@0 99 * callback to notify of the initial blocking state. Also, if a listener is
michael@0 100 * attached to a stream that has already finished, we'll call NotifyFinished.
michael@0 101 */
michael@0 102 class MediaStreamListener {
michael@0 103 protected:
michael@0 104 // Protected destructor, to discourage deletion outside of Release():
michael@0 105 virtual ~MediaStreamListener() {}
michael@0 106
michael@0 107 public:
michael@0 108 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
michael@0 109
michael@0 110 enum Consumption {
michael@0 111 CONSUMED,
michael@0 112 NOT_CONSUMED
michael@0 113 };
michael@0 114 /**
michael@0 115 * Notify that the stream is hooked up and we'd like to start or stop receiving
michael@0 116 * data on it. Only fires on SourceMediaStreams.
michael@0 117 * The initial state is assumed to be NOT_CONSUMED.
michael@0 118 */
michael@0 119 virtual void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) {}
michael@0 120
michael@0 121 /**
michael@0 122 * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
michael@0 123 * control loop is ready to pull, this gets called. A NotifyPull implementation
michael@0 124 * is allowed to call the SourceMediaStream methods that alter track
michael@0 125 * data. It is not allowed to make other MediaStream API calls, including
michael@0 126 * calls to add or remove MediaStreamListeners. It is not allowed to block
michael@0 127 * for any length of time.
michael@0 128 * aDesiredTime is the stream time we would like to get data up to. Data
michael@0 129 * beyond this point will not be played until NotifyPull runs again, so there's
michael@0 130 * not much point in providing it. Note that if the stream is blocked for
michael@0 131 * some reason, then data before aDesiredTime may not be played immediately.
michael@0 132 */
michael@0 133 virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
michael@0 134
michael@0 135 enum Blocking {
michael@0 136 BLOCKED,
michael@0 137 UNBLOCKED
michael@0 138 };
michael@0 139 /**
michael@0 140 * Notify that the blocking status of the stream changed. The initial state
michael@0 141 * is assumed to be BLOCKED.
michael@0 142 */
michael@0 143 virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
michael@0 144
michael@0 145 /**
michael@0 146 * Notify that the stream has data in each track
michael@0 147 * for the stream's current time. Once this state becomes true, it will
michael@0 148 * always be true since we block stream time from progressing to times where
michael@0 149 * there isn't data in each track.
michael@0 150 */
michael@0 151 virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
michael@0 152
michael@0 153 /**
michael@0 154 * Notify that the stream output is advancing. aCurrentTime is the graph's
michael@0 155 * current time. MediaStream::GraphTimeToStreamTime can be used to get the
michael@0 156 * stream time.
michael@0 157 */
michael@0 158 virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
michael@0 159
michael@0 160 /**
michael@0 161 * Notify that the stream finished.
michael@0 162 */
michael@0 163 virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
michael@0 164
michael@0 165 /**
michael@0 166 * Notify that your listener has been removed, either due to RemoveListener(),
michael@0 167 * or due to the stream being destroyed. You will get no further notifications.
michael@0 168 */
michael@0 169 virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
michael@0 170
michael@0 171 enum {
michael@0 172 TRACK_EVENT_CREATED = 0x01,
michael@0 173 TRACK_EVENT_ENDED = 0x02
michael@0 174 };
michael@0 175 /**
michael@0 176 * Notify that changes to one of the stream tracks have been queued.
michael@0 177 * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
michael@0 178 * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
michael@0 179 * at aTrackOffset (relative to the start of the stream).
michael@0 180 */
michael@0 181 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
michael@0 182 TrackRate aTrackRate,
michael@0 183 TrackTicks aTrackOffset,
michael@0 184 uint32_t aTrackEvents,
michael@0 185 const MediaSegment& aQueuedMedia) {}
michael@0 186 };
michael@0 187
michael@0 188 /**
michael@0 189 * This is a base class for media graph thread listener direct callbacks
michael@0 190 * from within AppendToTrack(). Note that your regular listener will
michael@0 191 * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
michael@0 192 * you must be careful to ignore them if AddDirectListener was successful.
michael@0 193 */
michael@0 194 class MediaStreamDirectListener : public MediaStreamListener
michael@0 195 {
michael@0 196 public:
michael@0 197 virtual ~MediaStreamDirectListener() {}
michael@0 198
michael@0 199 /*
michael@0 200 * This will be called on any MediaStreamDirectListener added to a
michael@0 201 * a SourceMediaStream when AppendToTrack() is called. The MediaSegment
michael@0 202 * will be the RawSegment (unresampled) if available in AppendToTrack().
michael@0 203 * Note that NotifyQueuedTrackChanges() calls will also still occur.
michael@0 204 */
michael@0 205 virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
michael@0 206 TrackRate aTrackRate,
michael@0 207 TrackTicks aTrackOffset,
michael@0 208 uint32_t aTrackEvents,
michael@0 209 const MediaSegment& aMedia) {}
michael@0 210 };
michael@0 211
michael@0 212 /**
michael@0 213 * This is a base class for main-thread listener callbacks.
michael@0 214 * This callback is invoked on the main thread when the main-thread-visible
michael@0 215 * state of a stream has changed.
michael@0 216 *
michael@0 217 * These methods are called with the media graph monitor held, so
michael@0 218 * reentry into general media graph methods is not possible.
michael@0 219 * You should do something non-blocking and non-reentrant (e.g. dispatch an
michael@0 220 * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
michael@0 221 * would be a good choice.
michael@0 222 * The listener is allowed to synchronously remove itself from the stream, but
michael@0 223 * not add or remove any other listeners.
michael@0 224 */
michael@0 225 class MainThreadMediaStreamListener {
michael@0 226 public:
michael@0 227 virtual void NotifyMainThreadStateChanged() = 0;
michael@0 228 };
michael@0 229
michael@0 230 /**
michael@0 231 * Helper struct used to keep track of memory usage by AudioNodes.
michael@0 232 */
michael@0 233 struct AudioNodeSizes
michael@0 234 {
michael@0 235 size_t mDomNode;
michael@0 236 size_t mStream;
michael@0 237 size_t mEngine;
michael@0 238 nsCString mNodeType;
michael@0 239 };
michael@0 240
michael@0 241 class MediaStreamGraphImpl;
michael@0 242 class SourceMediaStream;
michael@0 243 class ProcessedMediaStream;
michael@0 244 class MediaInputPort;
michael@0 245 class AudioNodeEngine;
michael@0 246 class AudioNodeExternalInputStream;
michael@0 247 class AudioNodeStream;
michael@0 248 struct AudioChunk;
michael@0 249
michael@0 250 /**
michael@0 251 * A stream of synchronized audio and video data. All (not blocked) streams
michael@0 252 * progress at the same rate --- "real time". Streams cannot seek. The only
michael@0 253 * operation readers can perform on a stream is to read the next data.
michael@0 254 *
michael@0 255 * Consumers of a stream can be reading from it at different offsets, but that
michael@0 256 * should only happen due to the order in which consumers are being run.
michael@0 257 * Those offsets must not diverge in the long term, otherwise we would require
michael@0 258 * unbounded buffering.
michael@0 259 *
michael@0 260 * Streams can be in a "blocked" state. While blocked, a stream does not
michael@0 261 * produce data. A stream can be explicitly blocked via the control API,
michael@0 262 * or implicitly blocked by whatever's generating it (e.g. an underrun in the
michael@0 263 * source resource), or implicitly blocked because something consuming it
michael@0 264 * blocks, or implicitly because it has finished.
michael@0 265 *
michael@0 266 * A stream can be in a "finished" state. "Finished" streams are permanently
michael@0 267 * blocked.
michael@0 268 *
michael@0 269 * Transitions into and out of the "blocked" and "finished" states are managed
michael@0 270 * by the MediaStreamGraph on the media graph thread.
michael@0 271 *
michael@0 272 * We buffer media data ahead of the consumers' reading offsets. It is possible
michael@0 273 * to have buffered data but still be blocked.
michael@0 274 *
michael@0 275 * Any stream can have its audio and video playing when requested. The media
michael@0 276 * stream graph plays audio by constructing audio output streams as necessary.
michael@0 277 * Video is played by setting video frames into an VideoFrameContainer at the right
michael@0 278 * time. To ensure video plays in sync with audio, make sure that the same
michael@0 279 * stream is playing both the audio and video.
michael@0 280 *
michael@0 281 * The data in a stream is managed by StreamBuffer. It consists of a set of
michael@0 282 * tracks of various types that can start and end over time.
michael@0 283 *
michael@0 284 * Streams are explicitly managed. The client creates them via
michael@0 285 * MediaStreamGraph::CreateInput/ProcessedMediaStream, and releases them by calling
michael@0 286 * Destroy() when no longer needed (actual destruction will be deferred).
michael@0 287 * The actual object is owned by the MediaStreamGraph. The basic idea is that
michael@0 288 * main thread objects will keep Streams alive as long as necessary (using the
michael@0 289 * cycle collector to clean up whenever needed).
michael@0 290 *
michael@0 291 * We make them refcounted only so that stream-related messages with MediaStream*
michael@0 292 * pointers can be sent to the main thread safely.
michael@0 293 *
michael@0 294 * The lifetimes of MediaStreams are controlled from the main thread.
michael@0 295 * For MediaStreams exposed to the DOM, the lifetime is controlled by the DOM
michael@0 296 * wrapper; the DOM wrappers own their associated MediaStreams. When a DOM
michael@0 297 * wrapper is destroyed, it sends a Destroy message for the associated
michael@0 298 * MediaStream and clears its reference (the last main-thread reference to
michael@0 299 * the object). When the Destroy message is processed on the graph
michael@0 300 * manager thread we immediately release the affected objects (disentangling them
michael@0 301 * from other objects as necessary).
michael@0 302 *
michael@0 303 * This could cause problems for media processing if a MediaStream is
michael@0 304 * destroyed while a downstream MediaStream is still using it. Therefore
michael@0 305 * the DOM wrappers must keep upstream MediaStreams alive as long as they
michael@0 306 * could be being used in the media graph.
michael@0 307 *
michael@0 308 * At any time, however, a set of MediaStream wrappers could be
michael@0 309 * collected via cycle collection. Destroy messages will be sent
michael@0 310 * for those objects in arbitrary order and the MediaStreamGraph has to be able
michael@0 311 * to handle this.
michael@0 312 */
michael@0 313 class MediaStream : public mozilla::LinkedListElement<MediaStream> {
michael@0 314 public:
michael@0 315 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
michael@0 316
michael@0 317 MediaStream(DOMMediaStream* aWrapper);
michael@0 318
michael@0 319 protected:
michael@0 320 // Protected destructor, to discourage deletion outside of Release():
michael@0 321 virtual ~MediaStream()
michael@0 322 {
michael@0 323 MOZ_COUNT_DTOR(MediaStream);
michael@0 324 NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
michael@0 325 NS_ASSERTION(mMainThreadListeners.IsEmpty(),
michael@0 326 "All main thread listeners should have been removed");
michael@0 327 }
michael@0 328
michael@0 329 public:
michael@0 330 /**
michael@0 331 * Returns the graph that owns this stream.
michael@0 332 */
michael@0 333 MediaStreamGraphImpl* GraphImpl();
michael@0 334 MediaStreamGraph* Graph();
michael@0 335 /**
michael@0 336 * Sets the graph that owns this stream. Should only be called once.
michael@0 337 */
michael@0 338 void SetGraphImpl(MediaStreamGraphImpl* aGraph);
michael@0 339 void SetGraphImpl(MediaStreamGraph* aGraph);
michael@0 340
michael@0 341 // Control API.
michael@0 342 // Since a stream can be played multiple ways, we need to combine independent
michael@0 343 // volume settings. The aKey parameter is used to keep volume settings
michael@0 344 // separate. Since the stream is always playing the same contents, only
michael@0 345 // a single audio output stream is used; the volumes are combined.
michael@0 346 // Currently only the first enabled audio track is played.
michael@0 347 // XXX change this so all enabled audio tracks are mixed and played.
michael@0 348 virtual void AddAudioOutput(void* aKey);
michael@0 349 virtual void SetAudioOutputVolume(void* aKey, float aVolume);
michael@0 350 virtual void RemoveAudioOutput(void* aKey);
michael@0 351 // Since a stream can be played multiple ways, we need to be able to
michael@0 352 // play to multiple VideoFrameContainers.
michael@0 353 // Only the first enabled video track is played.
michael@0 354 virtual void AddVideoOutput(VideoFrameContainer* aContainer);
michael@0 355 virtual void RemoveVideoOutput(VideoFrameContainer* aContainer);
michael@0 356 // Explicitly block. Useful for example if a media element is pausing
michael@0 357 // and we need to stop its stream emitting its buffered data.
michael@0 358 virtual void ChangeExplicitBlockerCount(int32_t aDelta);
michael@0 359 // Events will be dispatched by calling methods of aListener.
michael@0 360 virtual void AddListener(MediaStreamListener* aListener);
michael@0 361 virtual void RemoveListener(MediaStreamListener* aListener);
michael@0 362 // A disabled track has video replaced by black, and audio replaced by
michael@0 363 // silence.
michael@0 364 void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
michael@0 365 // Events will be dispatched by calling methods of aListener. It is the
michael@0 366 // responsibility of the caller to remove aListener before it is destroyed.
michael@0 367 void AddMainThreadListener(MainThreadMediaStreamListener* aListener)
michael@0 368 {
michael@0 369 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
michael@0 370 mMainThreadListeners.AppendElement(aListener);
michael@0 371 }
michael@0 372 // It's safe to call this even if aListener is not currently a listener;
michael@0 373 // the call will be ignored.
michael@0 374 void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener)
michael@0 375 {
michael@0 376 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
michael@0 377 mMainThreadListeners.RemoveElement(aListener);
michael@0 378 }
michael@0 379 /**
michael@0 380 * Ensure a runnable will run on the main thread after running all pending
michael@0 381 * updates that were sent from the graph thread or will be sent before the
michael@0 382 * graph thread receives the next graph update.
michael@0 383 *
michael@0 384 * If the graph has been shut down or destroyed, then the runnable will be
michael@0 385 * dispatched to the event queue immediately. If the graph is non-realtime
michael@0 386 * and has not started, then the runnable will be run
michael@0 387 * synchronously/immediately. (There are no pending updates in these
michael@0 388 * situations.)
michael@0 389 *
michael@0 390 * Main thread only.
michael@0 391 */
michael@0 392 void RunAfterPendingUpdates(nsRefPtr<nsIRunnable> aRunnable);
michael@0 393
michael@0 394 // Signal that the client is done with this MediaStream. It will be deleted later.
michael@0 395 virtual void Destroy();
michael@0 396 // Returns the main-thread's view of how much data has been processed by
michael@0 397 // this stream.
michael@0 398 StreamTime GetCurrentTime()
michael@0 399 {
michael@0 400 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
michael@0 401 return mMainThreadCurrentTime;
michael@0 402 }
michael@0 403 // Return the main thread's view of whether this stream has finished.
michael@0 404 bool IsFinished()
michael@0 405 {
michael@0 406 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
michael@0 407 return mMainThreadFinished;
michael@0 408 }
michael@0 409 bool IsDestroyed()
michael@0 410 {
michael@0 411 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
michael@0 412 return mMainThreadDestroyed;
michael@0 413 }
michael@0 414
michael@0 415 friend class MediaStreamGraphImpl;
michael@0 416 friend class MediaInputPort;
michael@0 417 friend class AudioNodeExternalInputStream;
michael@0 418
michael@0 419 virtual SourceMediaStream* AsSourceStream() { return nullptr; }
michael@0 420 virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; }
michael@0 421 virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; }
michael@0 422
michael@0 423 // media graph thread only
michael@0 424 void Init();
michael@0 425 // These Impl methods perform the core functionality of the control methods
michael@0 426 // above, on the media graph thread.
michael@0 427 /**
michael@0 428 * Stop all stream activity and disconnect it from all inputs and outputs.
michael@0 429 * This must be idempotent.
michael@0 430 */
michael@0 431 virtual void DestroyImpl();
michael@0 432 StreamTime GetBufferEnd() { return mBuffer.GetEnd(); }
michael@0 433 #ifdef DEBUG
michael@0 434 void DumpTrackInfo() { return mBuffer.DumpTrackInfo(); }
michael@0 435 #endif
michael@0 436 void SetAudioOutputVolumeImpl(void* aKey, float aVolume);
michael@0 437 void AddAudioOutputImpl(void* aKey)
michael@0 438 {
michael@0 439 mAudioOutputs.AppendElement(AudioOutput(aKey));
michael@0 440 }
michael@0 441 void RemoveAudioOutputImpl(void* aKey);
michael@0 442 void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
michael@0 443 {
michael@0 444 *mVideoOutputs.AppendElement() = aContainer;
michael@0 445 }
michael@0 446 void RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
michael@0 447 {
michael@0 448 mVideoOutputs.RemoveElement(aContainer);
michael@0 449 }
michael@0 450 void ChangeExplicitBlockerCountImpl(GraphTime aTime, int32_t aDelta)
michael@0 451 {
michael@0 452 mExplicitBlockerCount.SetAtAndAfter(aTime, mExplicitBlockerCount.GetAt(aTime) + aDelta);
michael@0 453 }
michael@0 454 void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
michael@0 455 void RemoveListenerImpl(MediaStreamListener* aListener);
michael@0 456 void RemoveAllListenersImpl();
michael@0 457 void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
michael@0 458 /**
michael@0 459 * Returns true when this stream requires the contents of its inputs even if
michael@0 460 * its own outputs are not being consumed. This is used to signal inputs to
michael@0 461 * this stream that they are being consumed; when they're not being consumed,
michael@0 462 * we make some optimizations.
michael@0 463 */
michael@0 464 virtual bool IsIntrinsicallyConsumed() const
michael@0 465 {
michael@0 466 return !mAudioOutputs.IsEmpty() || !mVideoOutputs.IsEmpty();
michael@0 467 }
michael@0 468
michael@0 469 void AddConsumer(MediaInputPort* aPort)
michael@0 470 {
michael@0 471 mConsumers.AppendElement(aPort);
michael@0 472 }
michael@0 473 void RemoveConsumer(MediaInputPort* aPort)
michael@0 474 {
michael@0 475 mConsumers.RemoveElement(aPort);
michael@0 476 }
michael@0 477 uint32_t ConsumerCount()
michael@0 478 {
michael@0 479 return mConsumers.Length();
michael@0 480 }
michael@0 481 const StreamBuffer& GetStreamBuffer() { return mBuffer; }
michael@0 482 GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
michael@0 483 /**
michael@0 484 * Convert graph time to stream time. aTime must be <= mStateComputedTime
michael@0 485 * to ensure we know exactly how much time this stream will be blocked during
michael@0 486 * the interval.
michael@0 487 */
michael@0 488 StreamTime GraphTimeToStreamTime(GraphTime aTime);
michael@0 489 /**
michael@0 490 * Convert graph time to stream time. aTime can be > mStateComputedTime,
michael@0 491 * in which case we optimistically assume the stream will not be blocked
michael@0 492 * after mStateComputedTime.
michael@0 493 */
michael@0 494 StreamTime GraphTimeToStreamTimeOptimistic(GraphTime aTime);
michael@0 495 /**
michael@0 496 * Convert stream time to graph time. The result can be > mStateComputedTime,
michael@0 497 * in which case we did the conversion optimistically assuming the stream
michael@0 498 * will not be blocked after mStateComputedTime.
michael@0 499 */
michael@0 500 GraphTime StreamTimeToGraphTime(StreamTime aTime);
michael@0 501 bool IsFinishedOnGraphThread() { return mFinished; }
michael@0 502 void FinishOnGraphThread();
michael@0 503 /**
michael@0 504 * Identify which graph update index we are currently processing.
michael@0 505 */
michael@0 506 int64_t GetProcessingGraphUpdateIndex();
michael@0 507
michael@0 508 bool HasCurrentData() { return mHasCurrentData; }
michael@0 509
michael@0 510 StreamBuffer::Track* EnsureTrack(TrackID aTrack, TrackRate aSampleRate);
michael@0 511
michael@0 512 void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment = nullptr);
michael@0 513
michael@0 514 DOMMediaStream* GetWrapper()
michael@0 515 {
michael@0 516 NS_ASSERTION(NS_IsMainThread(), "Only use DOMMediaStream on main thread");
michael@0 517 return mWrapper;
michael@0 518 }
michael@0 519
michael@0 520 // Return true if the main thread needs to observe updates from this stream.
michael@0 521 virtual bool MainThreadNeedsUpdates() const
michael@0 522 {
michael@0 523 return true;
michael@0 524 }
michael@0 525
michael@0 526 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 527 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 528
michael@0 529 void SetAudioChannelType(dom::AudioChannel aType) { mAudioChannelType = aType; }
michael@0 530
michael@0 531 protected:
michael@0 532 virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime)
michael@0 533 {
michael@0 534 mBufferStartTime += aBlockedTime;
michael@0 535 mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime);
michael@0 536 mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime);
michael@0 537 mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime);
michael@0 538
michael@0 539 mBuffer.ForgetUpTo(aCurrentTime - mBufferStartTime);
michael@0 540 }
michael@0 541
michael@0 542 // This state is all initialized on the main thread but
michael@0 543 // otherwise modified only on the media graph thread.
michael@0 544
michael@0 545 // Buffered data. The start of the buffer corresponds to mBufferStartTime.
michael@0 546 // Conceptually the buffer contains everything this stream has ever played,
michael@0 547 // but we forget some prefix of the buffered data to bound the space usage.
michael@0 548 StreamBuffer mBuffer;
michael@0 549 // The time when the buffered data could be considered to have started playing.
michael@0 550 // This increases over time to account for time the stream was blocked before
michael@0 551 // mCurrentTime.
michael@0 552 GraphTime mBufferStartTime;
michael@0 553
michael@0 554 // Client-set volume of this stream
michael@0 555 struct AudioOutput {
michael@0 556 AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {}
michael@0 557 void* mKey;
michael@0 558 float mVolume;
michael@0 559 };
michael@0 560 nsTArray<AudioOutput> mAudioOutputs;
michael@0 561 nsTArray<nsRefPtr<VideoFrameContainer> > mVideoOutputs;
michael@0 562 // We record the last played video frame to avoid redundant setting
michael@0 563 // of the current video frame.
michael@0 564 VideoFrame mLastPlayedVideoFrame;
michael@0 565 // The number of times this stream has been explicitly blocked by the control
michael@0 566 // API, minus the number of times it has been explicitly unblocked.
michael@0 567 TimeVarying<GraphTime,uint32_t,0> mExplicitBlockerCount;
michael@0 568 nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
michael@0 569 nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
michael@0 570 nsTArray<TrackID> mDisabledTrackIDs;
michael@0 571
michael@0 572 // Precomputed blocking status (over GraphTime).
michael@0 573 // This is only valid between the graph's mCurrentTime and
michael@0 574 // mStateComputedTime. The stream is considered to have
michael@0 575 // not been blocked before mCurrentTime (its mBufferStartTime is increased
michael@0 576 // as necessary to account for that time instead) --- this avoids us having to
michael@0 577 // record the entire history of the stream's blocking-ness in mBlocked.
michael@0 578 TimeVarying<GraphTime,bool,5> mBlocked;
michael@0 579 // Maps graph time to the graph update that affected this stream at that time
michael@0 580 TimeVarying<GraphTime,int64_t,0> mGraphUpdateIndices;
michael@0 581
michael@0 582 // MediaInputPorts to which this is connected
michael@0 583 nsTArray<MediaInputPort*> mConsumers;
michael@0 584
michael@0 585 // Where audio output is going. There is one AudioOutputStream per
michael@0 586 // audio track.
michael@0 587 struct AudioOutputStream {
michael@0 588 // When we started audio playback for this track.
michael@0 589 // Add mStream->GetPosition() to find the current audio playback position.
michael@0 590 GraphTime mAudioPlaybackStartTime;
michael@0 591 // Amount of time that we've wanted to play silence because of the stream
michael@0 592 // blocking.
michael@0 593 MediaTime mBlockedAudioTime;
michael@0 594 // Last tick written to the audio output.
michael@0 595 TrackTicks mLastTickWritten;
michael@0 596 RefPtr<AudioStream> mStream;
michael@0 597 TrackID mTrackID;
michael@0 598
michael@0 599 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 600 {
michael@0 601 size_t amount = 0;
michael@0 602 amount += mStream->SizeOfIncludingThis(aMallocSizeOf);
michael@0 603 return amount;
michael@0 604 }
michael@0 605 };
michael@0 606 nsTArray<AudioOutputStream> mAudioOutputStreams;
michael@0 607
michael@0 608 /**
michael@0 609 * When true, this means the stream will be finished once all
michael@0 610 * buffered data has been consumed.
michael@0 611 */
michael@0 612 bool mFinished;
michael@0 613 /**
michael@0 614 * When true, mFinished is true and we've played all the data in this stream
michael@0 615 * and fired NotifyFinished notifications.
michael@0 616 */
michael@0 617 bool mNotifiedFinished;
michael@0 618 /**
michael@0 619 * When true, the last NotifyBlockingChanged delivered to the listeners
michael@0 620 * indicated that the stream is blocked.
michael@0 621 */
michael@0 622 bool mNotifiedBlocked;
michael@0 623 /**
michael@0 624 * True if some data can be present by this stream if/when it's unblocked.
michael@0 625 * Set by the stream itself on the MediaStreamGraph thread. Only changes
michael@0 626 * from false to true once a stream has data, since we won't
michael@0 627 * unblock it until there's more data.
michael@0 628 */
michael@0 629 bool mHasCurrentData;
michael@0 630 /**
michael@0 631 * True if mHasCurrentData is true and we've notified listeners.
michael@0 632 */
michael@0 633 bool mNotifiedHasCurrentData;
michael@0 634
michael@0 635 // Temporary data for ordering streams by dependency graph
michael@0 636 bool mHasBeenOrdered;
michael@0 637 bool mIsOnOrderingStack;
michael@0 638 // True if the stream is being consumed (i.e. has track data being played,
michael@0 639 // or is feeding into some stream that is being consumed).
michael@0 640 bool mIsConsumed;
michael@0 641 // Temporary data for computing blocking status of streams
michael@0 642 // True if we've added this stream to the set of streams we're computing
michael@0 643 // blocking for.
michael@0 644 bool mInBlockingSet;
michael@0 645 // True if this stream should be blocked in this phase.
michael@0 646 bool mBlockInThisPhase;
michael@0 647
michael@0 648 // This state is only used on the main thread.
michael@0 649 DOMMediaStream* mWrapper;
michael@0 650 // Main-thread views of state
michael@0 651 StreamTime mMainThreadCurrentTime;
michael@0 652 bool mMainThreadFinished;
michael@0 653 bool mMainThreadDestroyed;
michael@0 654
michael@0 655 // Our media stream graph. null if destroyed on the graph thread.
michael@0 656 MediaStreamGraphImpl* mGraph;
michael@0 657
michael@0 658 dom::AudioChannel mAudioChannelType;
michael@0 659 };
michael@0 660
michael@0 661 /**
michael@0 662 * This is a stream into which a decoder can write audio and video.
michael@0 663 *
michael@0 664 * Audio and video can be written on any thread, but you probably want to
michael@0 665 * always write from the same thread to avoid unexpected interleavings.
michael@0 666 */
michael@0 667 class SourceMediaStream : public MediaStream {
michael@0 668 public:
michael@0 669 SourceMediaStream(DOMMediaStream* aWrapper) :
michael@0 670 MediaStream(aWrapper),
michael@0 671 mLastConsumptionState(MediaStreamListener::NOT_CONSUMED),
michael@0 672 mMutex("mozilla::media::SourceMediaStream"),
michael@0 673 mUpdateKnownTracksTime(0),
michael@0 674 mPullEnabled(false),
michael@0 675 mUpdateFinished(false)
michael@0 676 {}
michael@0 677
michael@0 678 virtual SourceMediaStream* AsSourceStream() { return this; }
michael@0 679
michael@0 680 // Media graph thread only
michael@0 681 virtual void DestroyImpl();
michael@0 682
michael@0 683 // Call these on any thread.
michael@0 684 /**
michael@0 685 * Enable or disable pulling. When pulling is enabled, NotifyPull
michael@0 686 * gets called on MediaStreamListeners for this stream during the
michael@0 687 * MediaStreamGraph control loop. Pulling is initially disabled.
michael@0 688 * Due to unavoidable race conditions, after a call to SetPullEnabled(false)
michael@0 689 * it is still possible for a NotifyPull to occur.
michael@0 690 */
michael@0 691 void SetPullEnabled(bool aEnabled);
michael@0 692
michael@0 693 void AddDirectListener(MediaStreamDirectListener* aListener);
michael@0 694 void RemoveDirectListener(MediaStreamDirectListener* aListener);
michael@0 695
michael@0 696 /**
michael@0 697 * Add a new track to the stream starting at the given base time (which
michael@0 698 * must be greater than or equal to the last time passed to
michael@0 699 * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should
michael@0 700 * contain data starting after aStart.
michael@0 701 */
michael@0 702 void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
michael@0 703 MediaSegment* aSegment);
michael@0 704
michael@0 705 /**
michael@0 706 * Append media data to a track. Ownership of aSegment remains with the caller,
michael@0 707 * but aSegment is emptied.
michael@0 708 * Returns false if the data was not appended because no such track exists
michael@0 709 * or the stream was already finished.
michael@0 710 */
michael@0 711 bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
michael@0 712 /**
michael@0 713 * Returns true if the buffer currently has enough data.
michael@0 714 * Returns false if there isn't enough data or if no such track exists.
michael@0 715 */
michael@0 716 bool HaveEnoughBuffered(TrackID aID);
michael@0 717 /**
michael@0 718 * Ensures that aSignalRunnable will be dispatched to aSignalThread
michael@0 719 * when we don't have enough buffered data in the track (which could be
michael@0 720 * immediately). Will dispatch the runnable immediately if the track
michael@0 721 * does not exist. No op if a runnable is already present for this track.
michael@0 722 */
michael@0 723 void DispatchWhenNotEnoughBuffered(TrackID aID,
michael@0 724 nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable);
michael@0 725 /**
michael@0 726 * Indicate that a track has ended. Do not do any more API calls
michael@0 727 * affecting this track.
michael@0 728 * Ignored if the track does not exist.
michael@0 729 */
michael@0 730 void EndTrack(TrackID aID);
michael@0 731 /**
michael@0 732 * Indicate that no tracks will be added starting before time aKnownTime.
michael@0 733 * aKnownTime must be >= its value at the last call to AdvanceKnownTracksTime.
michael@0 734 */
michael@0 735 void AdvanceKnownTracksTime(StreamTime aKnownTime);
michael@0 736 /**
michael@0 737 * Indicate that this stream should enter the "finished" state. All tracks
michael@0 738 * must have been ended via EndTrack. The finish time of the stream is
michael@0 739 * when all tracks have ended.
michael@0 740 */
michael@0 741 void FinishWithLockHeld();
michael@0 742 void Finish()
michael@0 743 {
michael@0 744 MutexAutoLock lock(mMutex);
michael@0 745 FinishWithLockHeld();
michael@0 746 }
michael@0 747
michael@0 748 // Overriding allows us to hold the mMutex lock while changing the track enable status
michael@0 749 void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
michael@0 750 MutexAutoLock lock(mMutex);
michael@0 751 MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
michael@0 752 }
michael@0 753
michael@0 754 /**
michael@0 755 * End all tracks and Finish() this stream. Used to voluntarily revoke access
michael@0 756 * to a LocalMediaStream.
michael@0 757 */
michael@0 758 void EndAllTrackAndFinish();
michael@0 759
michael@0 760 /**
michael@0 761 * Note: Only call from Media Graph thread (eg NotifyPull)
michael@0 762 *
michael@0 763 * Returns amount of time (data) that is currently buffered in the track,
michael@0 764 * assuming playout via PlayAudio or via a TrackUnion - note that
michael@0 765 * NotifyQueuedTrackChanges() on a SourceMediaStream will occur without
michael@0 766 * any "extra" buffering, but NotifyQueued TrackChanges() on a TrackUnion
michael@0 767 * will be buffered.
michael@0 768 */
michael@0 769 TrackTicks GetBufferedTicks(TrackID aID);
michael@0 770
michael@0 771 void RegisterForAudioMixing();
michael@0 772
michael@0 773 // XXX need a Reset API
michael@0 774
michael@0 775 friend class MediaStreamGraphImpl;
michael@0 776
michael@0 777 protected:
michael@0 778 struct ThreadAndRunnable {
michael@0 779 void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
michael@0 780 {
michael@0 781 mTarget = aTarget;
michael@0 782 mRunnable = aRunnable;
michael@0 783 }
michael@0 784
michael@0 785 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 786 nsCOMPtr<nsIRunnable> mRunnable;
michael@0 787 };
michael@0 788 enum TrackCommands {
michael@0 789 TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
michael@0 790 TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED
michael@0 791 };
michael@0 792 /**
michael@0 793 * Data for each track that hasn't ended.
michael@0 794 */
michael@0 795 struct TrackData {
michael@0 796 TrackID mID;
michael@0 797 // Sample rate of the input data.
michael@0 798 TrackRate mInputRate;
michael@0 799 // Sample rate of the output data, always equal to the sample rate of the
michael@0 800 // graph.
michael@0 801 TrackRate mOutputRate;
michael@0 802 // Resampler if the rate of the input track does not match the
michael@0 803 // MediaStreamGraph's.
michael@0 804 nsAutoRef<SpeexResamplerState> mResampler;
michael@0 805 TrackTicks mStart;
michael@0 806 // Each time the track updates are flushed to the media graph thread,
michael@0 807 // this is cleared.
michael@0 808 uint32_t mCommands;
michael@0 809 // Each time the track updates are flushed to the media graph thread,
michael@0 810 // the segment buffer is emptied.
michael@0 811 nsAutoPtr<MediaSegment> mData;
michael@0 812 nsTArray<ThreadAndRunnable> mDispatchWhenNotEnough;
michael@0 813 bool mHaveEnough;
michael@0 814 };
michael@0 815
michael@0 816 bool NeedsMixing();
michael@0 817
michael@0 818 void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
michael@0 819
michael@0 820 TrackData* FindDataForTrack(TrackID aID)
michael@0 821 {
michael@0 822 for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
michael@0 823 if (mUpdateTracks[i].mID == aID) {
michael@0 824 return &mUpdateTracks[i];
michael@0 825 }
michael@0 826 }
michael@0 827 return nullptr;
michael@0 828 }
michael@0 829
michael@0 830 /**
michael@0 831 * Notify direct consumers of new data to one of the stream tracks.
michael@0 832 * The data doesn't have to be resampled (though it may be). This is called
michael@0 833 * from AppendToTrack on the thread providing the data, and will call
michael@0 834 * the Listeners on this thread.
michael@0 835 */
michael@0 836 void NotifyDirectConsumers(TrackData *aTrack,
michael@0 837 MediaSegment *aSegment);
michael@0 838
michael@0 839 // Media stream graph thread only
michael@0 840 MediaStreamListener::Consumption mLastConsumptionState;
michael@0 841
michael@0 842 // This must be acquired *before* MediaStreamGraphImpl's lock, if they are
michael@0 843 // held together.
michael@0 844 Mutex mMutex;
michael@0 845 // protected by mMutex
michael@0 846 StreamTime mUpdateKnownTracksTime;
michael@0 847 nsTArray<TrackData> mUpdateTracks;
michael@0 848 nsTArray<nsRefPtr<MediaStreamDirectListener> > mDirectListeners;
michael@0 849 bool mPullEnabled;
michael@0 850 bool mUpdateFinished;
michael@0 851 bool mNeedsMixing;
michael@0 852 };
michael@0 853
michael@0 854 /**
michael@0 855 * Represents a connection between a ProcessedMediaStream and one of its
michael@0 856 * input streams.
michael@0 857 * We make these refcounted so that stream-related messages with MediaInputPort*
michael@0 858 * pointers can be sent to the main thread safely.
michael@0 859 *
michael@0 860 * When a port's source or destination stream dies, the stream's DestroyImpl
michael@0 861 * calls MediaInputPort::Disconnect to disconnect the port from
michael@0 862 * the source and destination streams.
michael@0 863 *
michael@0 864 * The lifetimes of MediaInputPort are controlled from the main thread.
michael@0 865 * The media graph adds a reference to the port. When a MediaInputPort is no
michael@0 866 * longer needed, main-thread code sends a Destroy message for the port and
michael@0 867 * clears its reference (the last main-thread reference to the object). When
michael@0 868 * the Destroy message is processed on the graph manager thread we disconnect
michael@0 869 * the port and drop the graph's reference, destroying the object.
michael@0 870 */
michael@0 871 class MediaInputPort MOZ_FINAL {
michael@0 872 private:
michael@0 873 // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
michael@0 874 MediaInputPort(MediaStream* aSource, ProcessedMediaStream* aDest,
michael@0 875 uint32_t aFlags, uint16_t aInputNumber,
michael@0 876 uint16_t aOutputNumber)
michael@0 877 : mSource(aSource)
michael@0 878 , mDest(aDest)
michael@0 879 , mFlags(aFlags)
michael@0 880 , mInputNumber(aInputNumber)
michael@0 881 , mOutputNumber(aOutputNumber)
michael@0 882 , mGraph(nullptr)
michael@0 883 {
michael@0 884 MOZ_COUNT_CTOR(MediaInputPort);
michael@0 885 }
michael@0 886
michael@0 887 // Private destructor, to discourage deletion outside of Release():
michael@0 888 ~MediaInputPort()
michael@0 889 {
michael@0 890 MOZ_COUNT_DTOR(MediaInputPort);
michael@0 891 }
michael@0 892
michael@0 893 public:
michael@0 894 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort)
michael@0 895
michael@0 896 /**
michael@0 897 * The FLAG_BLOCK_INPUT and FLAG_BLOCK_OUTPUT flags can be used to control
michael@0 898 * exactly how the blocking statuses of the input and output streams affect
michael@0 899 * each other.
michael@0 900 */
michael@0 901 enum {
michael@0 902 // When set, blocking on the output stream forces blocking on the input
michael@0 903 // stream.
michael@0 904 FLAG_BLOCK_INPUT = 0x01,
michael@0 905 // When set, blocking on the input stream forces blocking on the output
michael@0 906 // stream.
michael@0 907 FLAG_BLOCK_OUTPUT = 0x02
michael@0 908 };
michael@0 909
michael@0 910 // Called on graph manager thread
michael@0 911 // Do not call these from outside MediaStreamGraph.cpp!
michael@0 912 void Init();
michael@0 913 // Called during message processing to trigger removal of this stream.
michael@0 914 void Disconnect();
michael@0 915
michael@0 916 // Control API
michael@0 917 /**
michael@0 918 * Disconnects and destroys the port. The caller must not reference this
michael@0 919 * object again.
michael@0 920 */
michael@0 921 void Destroy();
michael@0 922
michael@0 923 // Any thread
michael@0 924 MediaStream* GetSource() { return mSource; }
michael@0 925 ProcessedMediaStream* GetDestination() { return mDest; }
michael@0 926
michael@0 927 uint16_t InputNumber() const { return mInputNumber; }
michael@0 928 uint16_t OutputNumber() const { return mOutputNumber; }
michael@0 929
michael@0 930 // Call on graph manager thread
michael@0 931 struct InputInterval {
michael@0 932 GraphTime mStart;
michael@0 933 GraphTime mEnd;
michael@0 934 bool mInputIsBlocked;
michael@0 935 };
michael@0 936 // Find the next time interval starting at or after aTime during which
michael@0 937 // mDest is not blocked and mSource's blocking status does not change.
michael@0 938 InputInterval GetNextInputInterval(GraphTime aTime);
michael@0 939
michael@0 940 /**
michael@0 941 * Returns the graph that owns this port.
michael@0 942 */
michael@0 943 MediaStreamGraphImpl* GraphImpl();
michael@0 944 MediaStreamGraph* Graph();
michael@0 945 /**
michael@0 946 * Sets the graph that owns this stream. Should only be called once.
michael@0 947 */
michael@0 948 void SetGraphImpl(MediaStreamGraphImpl* aGraph);
michael@0 949
michael@0 950 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 951 {
michael@0 952 size_t amount = 0;
michael@0 953
michael@0 954 // Not owned:
michael@0 955 // - mSource
michael@0 956 // - mDest
michael@0 957 // - mGraph
michael@0 958 return amount;
michael@0 959 }
michael@0 960
michael@0 961 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 962 {
michael@0 963 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 964 }
michael@0 965
michael@0 966 private:
michael@0 967 friend class MediaStreamGraphImpl;
michael@0 968 friend class MediaStream;
michael@0 969 friend class ProcessedMediaStream;
michael@0 970 // Never modified after Init()
michael@0 971 MediaStream* mSource;
michael@0 972 ProcessedMediaStream* mDest;
michael@0 973 uint32_t mFlags;
michael@0 974 // The input and output numbers are optional, and are currently only used by
michael@0 975 // Web Audio.
michael@0 976 const uint16_t mInputNumber;
michael@0 977 const uint16_t mOutputNumber;
michael@0 978
michael@0 979 // Our media stream graph
michael@0 980 MediaStreamGraphImpl* mGraph;
michael@0 981 };
michael@0 982
michael@0 983 /**
michael@0 984 * This stream processes zero or more input streams in parallel to produce
michael@0 985 * its output. The details of how the output is produced are handled by
michael@0 986 * subclasses overriding the ProcessInput method.
michael@0 987 */
michael@0 988 class ProcessedMediaStream : public MediaStream {
michael@0 989 public:
michael@0 990 ProcessedMediaStream(DOMMediaStream* aWrapper)
michael@0 991 : MediaStream(aWrapper), mAutofinish(false), mInCycle(false)
michael@0 992 {}
michael@0 993
michael@0 994 // Control API.
michael@0 995 /**
michael@0 996 * Allocates a new input port attached to source aStream.
michael@0 997 * This stream can be removed by calling MediaInputPort::Remove().
michael@0 998 */
michael@0 999 already_AddRefed<MediaInputPort> AllocateInputPort(MediaStream* aStream,
michael@0 1000 uint32_t aFlags = 0,
michael@0 1001 uint16_t aInputNumber = 0,
michael@0 1002 uint16_t aOutputNumber = 0);
michael@0 1003 /**
michael@0 1004 * Force this stream into the finished state.
michael@0 1005 */
michael@0 1006 void Finish();
michael@0 1007 /**
michael@0 1008 * Set the autofinish flag on this stream (defaults to false). When this flag
michael@0 1009 * is set, and all input streams are in the finished state (including if there
michael@0 1010 * are no input streams), this stream automatically enters the finished state.
michael@0 1011 */
michael@0 1012 void SetAutofinish(bool aAutofinish);
michael@0 1013
michael@0 1014 virtual ProcessedMediaStream* AsProcessedStream() { return this; }
michael@0 1015
michael@0 1016 friend class MediaStreamGraphImpl;
michael@0 1017
michael@0 1018 // Do not call these from outside MediaStreamGraph.cpp!
michael@0 1019 virtual void AddInput(MediaInputPort* aPort);
michael@0 1020 virtual void RemoveInput(MediaInputPort* aPort)
michael@0 1021 {
michael@0 1022 mInputs.RemoveElement(aPort);
michael@0 1023 }
michael@0 1024 bool HasInputPort(MediaInputPort* aPort)
michael@0 1025 {
michael@0 1026 return mInputs.Contains(aPort);
michael@0 1027 }
michael@0 1028 uint32_t InputPortCount()
michael@0 1029 {
michael@0 1030 return mInputs.Length();
michael@0 1031 }
michael@0 1032 virtual void DestroyImpl();
michael@0 1033 /**
michael@0 1034 * This gets called after we've computed the blocking states for all
michael@0 1035 * streams (mBlocked is up to date up to mStateComputedTime).
michael@0 1036 * Also, we've produced output for all streams up to this one. If this stream
michael@0 1037 * is not in a cycle, then all its source streams have produced data.
michael@0 1038 * Generate output from aFrom to aTo.
michael@0 1039 * This will be called on streams that have finished. Most stream types should
michael@0 1040 * just return immediately if IsFinishedOnGraphThread(), but some may wish to
michael@0 1041 * update internal state (see AudioNodeStream).
michael@0 1042 * ProcessInput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
michael@0 1043 * is in aFlags. (This flag will be set when aTo >= mStateComputedTime, i.e.
michael@0 1044 * when we've producing the last block of data we need to produce.) Otherwise
michael@0 1045 * we can get into a situation where we've determined the stream should not
michael@0 1046 * block before mStateComputedTime, but the stream finishes before
michael@0 1047 * mStateComputedTime, violating the invariant that finished streams are blocked.
michael@0 1048 */
michael@0 1049 enum {
michael@0 1050 ALLOW_FINISH = 0x01
michael@0 1051 };
michael@0 1052 virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
michael@0 1053 void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
michael@0 1054
michael@0 1055 /**
michael@0 1056 * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
michael@0 1057 */
michael@0 1058 virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
michael@0 1059
michael@0 1060 bool InCycle() const { return mInCycle; }
michael@0 1061
michael@0 1062 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 1063 {
michael@0 1064 size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
michael@0 1065 // Not owned:
michael@0 1066 // - mInputs elements
michael@0 1067 amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1068 return amount;
michael@0 1069 }
michael@0 1070
michael@0 1071 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 1072 {
michael@0 1073 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 1074 }
michael@0 1075
michael@0 1076 protected:
michael@0 1077 // This state is all accessed only on the media graph thread.
michael@0 1078
michael@0 1079 // The list of all inputs that are currently enabled or waiting to be enabled.
michael@0 1080 nsTArray<MediaInputPort*> mInputs;
michael@0 1081 bool mAutofinish;
michael@0 1082 // True if and only if this stream is in a cycle.
michael@0 1083 // Updated by MediaStreamGraphImpl::UpdateStreamOrder.
michael@0 1084 bool mInCycle;
michael@0 1085 };
michael@0 1086
michael@0 1087 /**
michael@0 1088 * Initially, at least, we will have a singleton MediaStreamGraph per
michael@0 1089 * process. Each OfflineAudioContext object creates its own MediaStreamGraph
michael@0 1090 * object too.
michael@0 1091 */
michael@0 1092 class MediaStreamGraph {
michael@0 1093 public:
michael@0 1094 // We ensure that the graph current time advances in multiples of
michael@0 1095 // IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that
michael@0 1096 // never blocks and has a track with the ideal audio rate will produce audio
michael@0 1097 // in multiples of the block size.
michael@0 1098
michael@0 1099 // Main thread only
michael@0 1100 static MediaStreamGraph* GetInstance();
michael@0 1101 static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate);
michael@0 1102 // Idempotent
michael@0 1103 static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
michael@0 1104
michael@0 1105 // Control API.
michael@0 1106 /**
michael@0 1107 * Create a stream that a media decoder (or some other source of
michael@0 1108 * media data, such as a camera) can write to.
michael@0 1109 */
michael@0 1110 SourceMediaStream* CreateSourceStream(DOMMediaStream* aWrapper);
michael@0 1111 /**
michael@0 1112 * Create a stream that will form the union of the tracks of its input
michael@0 1113 * streams.
michael@0 1114 * A TrackUnionStream contains all the tracks of all its input streams.
michael@0 1115 * Adding a new input stream makes that stream's tracks immediately appear as new
michael@0 1116 * tracks starting at the time the input stream was added.
michael@0 1117 * Removing an input stream makes the output tracks corresponding to the
michael@0 1118 * removed tracks immediately end.
michael@0 1119 * For each added track, the track ID of the output track is the track ID
michael@0 1120 * of the input track or one plus the maximum ID of all previously added
michael@0 1121 * tracks, whichever is greater.
michael@0 1122 * TODO at some point we will probably need to add API to select
michael@0 1123 * particular tracks of each input stream.
michael@0 1124 */
michael@0 1125 ProcessedMediaStream* CreateTrackUnionStream(DOMMediaStream* aWrapper);
michael@0 1126 // Internal AudioNodeStreams can only pass their output to another
michael@0 1127 // AudioNode, whereas external AudioNodeStreams can pass their output
michael@0 1128 // to an nsAudioStream for playback.
michael@0 1129 enum AudioNodeStreamKind { SOURCE_STREAM, INTERNAL_STREAM, EXTERNAL_STREAM };
michael@0 1130 /**
michael@0 1131 * Create a stream that will process audio for an AudioNode.
michael@0 1132 * Takes ownership of aEngine. aSampleRate is the sampling rate used
michael@0 1133 * for the stream. If 0 is passed, the sampling rate of the engine's
michael@0 1134 * node will get used.
michael@0 1135 */
michael@0 1136 AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
michael@0 1137 AudioNodeStreamKind aKind,
michael@0 1138 TrackRate aSampleRate = 0);
michael@0 1139
michael@0 1140 AudioNodeExternalInputStream*
michael@0 1141 CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine,
michael@0 1142 TrackRate aSampleRate = 0);
michael@0 1143
michael@0 1144 bool IsNonRealtime() const;
michael@0 1145 /**
michael@0 1146 * Start processing non-realtime for a specific number of ticks.
michael@0 1147 */
michael@0 1148 void StartNonRealtimeProcessing(TrackRate aRate, uint32_t aTicksToProcess);
michael@0 1149
michael@0 1150 /**
michael@0 1151 * Media graph thread only.
michael@0 1152 * Dispatches a runnable that will run on the main thread after all
michael@0 1153 * main-thread stream state has been next updated.
michael@0 1154 * Should only be called during MediaStreamListener callbacks or during
michael@0 1155 * ProcessedMediaStream::ProcessInput().
michael@0 1156 */
michael@0 1157 void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
michael@0 1158 {
michael@0 1159 *mPendingUpdateRunnables.AppendElement() = aRunnable;
michael@0 1160 }
michael@0 1161
michael@0 1162 protected:
michael@0 1163 MediaStreamGraph()
michael@0 1164 : mNextGraphUpdateIndex(1)
michael@0 1165 {
michael@0 1166 MOZ_COUNT_CTOR(MediaStreamGraph);
michael@0 1167 }
michael@0 1168 virtual ~MediaStreamGraph()
michael@0 1169 {
michael@0 1170 MOZ_COUNT_DTOR(MediaStreamGraph);
michael@0 1171 }
michael@0 1172
michael@0 1173 // Media graph thread only
michael@0 1174 nsTArray<nsCOMPtr<nsIRunnable> > mPendingUpdateRunnables;
michael@0 1175
michael@0 1176 // Main thread only
michael@0 1177 // The number of updates we have sent to the media graph thread + 1.
michael@0 1178 // We start this at 1 just to ensure that 0 is usable as a special value.
michael@0 1179 int64_t mNextGraphUpdateIndex;
michael@0 1180 };
michael@0 1181
michael@0 1182 }
michael@0 1183
michael@0 1184 #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */

mercurial