gecko-dev/dom/media/webaudio/AudioNodeStream.h

237 строки
8.9 KiB
C
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_AUDIONODESTREAM_H_
#define MOZILLA_AUDIONODESTREAM_H_
#include "MediaStreamGraph.h"
#include "mozilla/dom/AudioNodeBinding.h"
#include "AudioBlock.h"
namespace mozilla {
namespace dom {
struct ThreeDPoint;
struct AudioTimelineEvent;
class AudioContext;
} // namespace dom
class ThreadSharedFloatArrayBufferList;
class AudioNodeEngine;
/**
* An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
* The start time of the AudioTrack is aligned to the start time of the
* AudioContext's destination node stream, plus some multiple of BLOCK_SIZE
* samples.
*
* An AudioNodeStream has an AudioNodeEngine plugged into it that does the
* actual audio processing. AudioNodeStream contains the glue code that
* integrates audio processing with the MediaStreamGraph.
*/
class AudioNodeStream : public ProcessedMediaStream
{
typedef dom::ChannelCountMode ChannelCountMode;
typedef dom::ChannelInterpretation ChannelInterpretation;
public:
typedef mozilla::dom::AudioContext AudioContext;
enum { AUDIO_TRACK = 1 };
typedef nsAutoTArray<AudioBlock, 1> OutputChunks;
// Flags re main thread updates and stream output.
typedef unsigned Flags;
enum : Flags {
NO_STREAM_FLAGS = 0U,
NEED_MAIN_THREAD_FINISHED = 1U << 0,
NEED_MAIN_THREAD_CURRENT_TIME = 1U << 1,
// Internal AudioNodeStreams can only pass their output to another
// AudioNode, whereas external AudioNodeStreams can pass their output
// to other ProcessedMediaStreams or hardware audio output.
EXTERNAL_OUTPUT = 1U << 2,
};
/**
* Create a stream that will process audio for an AudioNode.
* Takes ownership of aEngine.
* If aGraph is non-null, use that as the MediaStreamGraph, otherwise use
* aCtx's graph. aGraph is only non-null when called for AudioDestinationNode
* since the context's graph hasn't been set up in that case.
*/
static already_AddRefed<AudioNodeStream>
Create(AudioContext* aCtx, AudioNodeEngine* aEngine, Flags aKind,
MediaStreamGraph* aGraph = nullptr);
protected:
/**
* Transfers ownership of aEngine to the new AudioNodeStream.
*/
AudioNodeStream(AudioNodeEngine* aEngine,
Flags aFlags,
TrackRate aSampleRate);
~AudioNodeStream();
public:
// Control API
/**
* Sets a parameter that's a time relative to some stream's played time.
* This time is converted to a time relative to this stream when it's set.
*/
void SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
double aStreamTime);
void SetDoubleParameter(uint32_t aIndex, double aValue);
void SetInt32Parameter(uint32_t aIndex, int32_t aValue);
void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue);
void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer);
// This sends a single event to the timeline on the MSG thread side.
void SendTimelineEvent(uint32_t aIndex, const dom::AudioTimelineEvent& aEvent);
// This consumes the contents of aData. aData will be emptied after this returns.
void SetRawArrayData(nsTArray<float>& aData);
void SetChannelMixingParameters(uint32_t aNumberOfChannels,
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
void SetPassThrough(bool aPassThrough);
ChannelInterpretation GetChannelInterpretation()
{
return mChannelInterpretation;
}
void SetAudioParamHelperStream()
{
MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
mAudioParamStream = true;
}
virtual AudioNodeStream* AsAudioNodeStream() override { return this; }
virtual void AddInput(MediaInputPort* aPort) override;
virtual void RemoveInput(MediaInputPort* aPort) override;
// Graph thread only
void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
double aStreamTime);
void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
/**
* Produce the next block of output, before input is provided.
* ProcessInput() will be called later, and it then should not change
* the output. This is used only for DelayNodeEngine in a feedback loop.
*/
void ProduceOutputBeforeInput(GraphTime aFrom);
bool IsAudioParamStream() const
{
return mAudioParamStream;
}
const OutputChunks& LastChunks() const
{
return mLastChunks;
}
virtual bool MainThreadNeedsUpdates() const override
{
return ((mFlags & NEED_MAIN_THREAD_FINISHED) && mFinished) ||
(mFlags & NEED_MAIN_THREAD_CURRENT_TIME);
}
// Any thread
AudioNodeEngine* Engine() { return mEngine; }
TrackRate SampleRate() const { return mSampleRate; }
/**
* Convert a time in seconds on the destination stream to ticks
* on this stream, including fractional position between ticks.
*/
double FractionalTicksFromDestinationTime(AudioNodeStream* aDestination,
double aSeconds);
/**
* Convert a time in seconds on the destination stream to StreamTime
* on this stream.
*/
StreamTime TicksFromDestinationTime(MediaStream* aDestination,
double aSeconds);
/**
* Get the destination stream time in seconds corresponding to a position on
* this stream.
*/
double DestinationTimeFromTicks(AudioNodeStream* aDestination,
StreamTime aPosition);
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
AudioNodeSizes& aUsage) const;
/*
* SetActive() is called when either an active input is added or the engine
* for a source node transitions from inactive to active. This is not
* called from engines for processing nodes because they only become active
* when there are active input streams, in which case this stream is already
* active.
*/
void SetActive();
/*
* CheckForInactive() is called when the engine transitions from active to
* inactive, or an active input is removed, or the stream finishes. If the
* stream is now inactive, then mInputChunks will be cleared and mLastChunks
* will be set to null. ProcessBlock() will not be called on the engine
* again until SetActive() is called.
*/
void CheckForInactive();
Bug 1094764 - Implement AudioContext.suspend and friends. r=roc,ehsan - Relevant spec text: - http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise - http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise - http://webaudio.github.io/web-audio-api/#widl-AudioContext-close-Promise - http://webaudio.github.io/web-audio-api/#widl-AudioContext-state - http://webaudio.github.io/web-audio-api/#widl-AudioContext-onstatechange - In a couple words, the behavior we want: - Closed context cannot have new nodes created, but can do decodeAudioData, and create buffers, and such. - OfflineAudioContexts don't support those methods, transitions happen at startRendering and at the end of processing. onstatechange is used to make this observable. - (regular) AudioContexts support those methods. The promises and onstatechange should be resolved/called when the operation has actually completed on the rendering thread. Once a context has been closed, it cannot transition back to "running". An AudioContext switches to "running" when the audio callback start running, this allow authors to know how long the audio stack takes to start running. - MediaStreams that feed in/go out of a suspended graph should respectively not buffer at the graph input, and output silence - suspended context should not be doing much on the CPU, and we should try to pause audio streams if we can (this behaviour is the main reason we need this in the first place, for saving battery on mobile, and CPU on all platforms) - Now, the implementation: - AudioNodeStreams are now tagged with a context id, to be able to operate on all the streams of a given AudioContext on the Graph thread without having to go and lock everytime to touch the AudioContext. This happens in the AudioNodeStream ctor. IDs are of course constant for the lifetime of the node. - When an AudioContext goes into suspended mode, streams for this AudioContext are moved out of the mStreams array to a second array, mSuspendedStreams. Streams in mSuspendedStream are not ordered, and are not processed. - The MSG will automatically switch to a SystemClockDriver when it finds that there are no more AudioNodeStream/Stream with an audio track. This is how pausing the audio subsystem and saving battery works. Subsequently, when the MSG finds that there are only streams in mSuspendedStreams, it will go to sleep (block on a monitor), so we save CPU, but it does not shut itself down. This is mostly not a new behaviour (this is what the MSG does since the refactoring), but is important to note. - Promises are gripped (addref-ed) on the main thread, and then shepherd down other threads and to the GraphDriver, if needed (sometimes we can resolve them right away). They move between threads as void* to prevent calling methods on them, as they are not thread safe. Then, the driver executes the operation, and when it's done (initializing and closing audio streams can take some time), we send the promise back to the main thread, and resolve it, casting back to Promise* after asserting we're back on the main thread. This way, we can send them back on the main thread once an operation has complete (suspending an audio stream, starting it again on resume(), etc.), without having to do bookkeeping between suspend calls and their result. Promises are not thread safe, so we can't move them around AddRef-ed. - The stream destruction logic now takes into account that a stream can be destroyed while not being in mStreams. - A graph can now switch GraphDriver twice or more per iteration, for example if an author goes suspend()/resume()/suspend() in the same script. - Some operation have to be done on suspended stream, so we now use double for-loop around mSuspendedStreams and mStreams in some places in MediaStreamGraph.cpp. - A tricky part was making sure everything worked at AudioContext boundaries. TrackUnionStream that have one of their input stream suspended append null ticks instead. - The graph ordering algorithm had to be altered to not include suspended streams. - There are some edge cases (adding a stream on a suspended graph, calling suspend/resume when a graph has just been close()d).
2015-02-27 20:22:05 +03:00
protected:
virtual void DestroyImpl() override;
void AdvanceOutputSegment();
void FinishOutput();
void AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk,
AudioBlock* aBlock,
nsTArray<float>* aDownmixBuffer);
void UpMixDownMixChunk(const AudioBlock* aChunk, uint32_t aOutputChannelCount,
nsTArray<const float*>& aOutputChannels,
nsTArray<float>& aDownmixBuffer);
uint32_t ComputedNumberOfChannels(uint32_t aInputChannelCount);
void ObtainInputBlock(AudioBlock& aTmpChunk, uint32_t aPortIndex);
void IncrementActiveInputCount();
void DecrementActiveInputCount();
// The engine that will generate output for this node.
nsAutoPtr<AudioNodeEngine> mEngine;
// The mixed input blocks are kept from iteration to iteration to avoid
// reallocating channel data arrays and any buffers for mixing.
OutputChunks mInputChunks;
// The last block produced by this node.
OutputChunks mLastChunks;
// The stream's sampling rate
const TrackRate mSampleRate;
// Whether this is an internal or external stream
const Flags mFlags;
// The number of input streams that may provide non-silent input.
uint32_t mActiveInputCount = 0;
// The number of input channels that this stream requires. 0 means don't care.
uint32_t mNumberOfInputChannels;
// The mixing modes
ChannelCountMode mChannelCountMode;
ChannelInterpretation mChannelInterpretation;
// Streams are considered active if the stream has not finished and either
// the engine is active or there are active input streams.
bool mIsActive;
// Whether the stream should be marked as finished as soon
// as the current time range has been computed block by block.
bool mMarkAsFinishedAfterThisBlock;
// Whether the stream is an AudioParamHelper stream.
bool mAudioParamStream;
// Whether the stream just passes its input through.
bool mPassThrough;
};
} // namespace mozilla
#endif /* MOZILLA_AUDIONODESTREAM_H_ */