зеркало из https://github.com/mozilla/gecko-dev.git
Bug 848954 - Part 5 - Mix down all audio and only output a single stream. r=roc
This commit is contained in:
Родитель
7e987d0735
Коммит
735258f143
|
@ -10,6 +10,7 @@
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ struct MixerCallbackReceiver {
|
||||||
uint32_t aFrames,
|
uint32_t aFrames,
|
||||||
uint32_t aSampleRate) = 0;
|
uint32_t aSampleRate) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class mixes multiple streams of audio together to output a single audio
|
* This class mixes multiple streams of audio together to output a single audio
|
||||||
* stream.
|
* stream.
|
||||||
|
@ -93,6 +93,16 @@ public:
|
||||||
mCallbacks.insertBack(new MixerCallback(aReceiver));
|
mCallbacks.insertBack(new MixerCallback(aReceiver));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FindCallback(MixerCallbackReceiver* aReceiver) {
|
||||||
|
for (MixerCallback* cb = mCallbacks.getFirst();
|
||||||
|
cb != nullptr; cb = cb->getNext()) {
|
||||||
|
if (cb->mReceiver == aReceiver) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool RemoveCallback(MixerCallbackReceiver* aReceiver) {
|
bool RemoveCallback(MixerCallbackReceiver* aReceiver) {
|
||||||
for (MixerCallback* cb = mCallbacks.getFirst();
|
for (MixerCallback* cb = mCallbacks.getFirst();
|
||||||
cb != nullptr; cb = cb->getNext()) {
|
cb != nullptr; cb = cb->getNext()) {
|
||||||
|
|
|
@ -147,7 +147,7 @@ void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInR
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
|
AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels, uint32_t aSampleRate)
|
||||||
{
|
{
|
||||||
uint32_t outputChannels = aOutput->GetChannels();
|
uint32_t outputChannels = aOutput->GetChannels();
|
||||||
nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
|
nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
|
||||||
|
@ -159,7 +159,7 @@ AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t outBufferLength = GetDuration() * outputChannels;
|
uint32_t outBufferLength = GetDuration() * aOutputChannels;
|
||||||
buf.SetLength(outBufferLength);
|
buf.SetLength(outBufferLength);
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,36 +172,33 @@ AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
|
||||||
// AudioStream, and we don't have real data to write to it (just silence).
|
// AudioStream, and we don't have real data to write to it (just silence).
|
||||||
// To avoid overbuffering in the AudioStream, we simply drop the silence,
|
// To avoid overbuffering in the AudioStream, we simply drop the silence,
|
||||||
// here. The stream will underrun and output silence anyways.
|
// here. The stream will underrun and output silence anyways.
|
||||||
if (c.mBuffer || aOutput->GetWritten()) {
|
if (c.mBuffer && c.mBufferFormat != AUDIO_FORMAT_SILENCE) {
|
||||||
if (c.mBuffer && c.mBufferFormat != AUDIO_FORMAT_SILENCE) {
|
channelData.SetLength(c.mChannelData.Length());
|
||||||
channelData.SetLength(c.mChannelData.Length());
|
for (uint32_t i = 0; i < channelData.Length(); ++i) {
|
||||||
for (uint32_t i = 0; i < channelData.Length(); ++i) {
|
channelData[i] = c.mChannelData[i];
|
||||||
channelData[i] = c.mChannelData[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelData.Length() < outputChannels) {
|
|
||||||
// Up-mix. Note that this might actually make channelData have more
|
|
||||||
// than outputChannels temporarily.
|
|
||||||
AudioChannelsUpMix(&channelData, outputChannels, gZeroChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelData.Length() > outputChannels) {
|
|
||||||
// Down-mix.
|
|
||||||
DownmixAndInterleave(channelData, c.mBufferFormat, frames,
|
|
||||||
c.mVolume, outputChannels, buf.Elements() + offset);
|
|
||||||
} else {
|
|
||||||
InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
|
|
||||||
frames, c.mVolume,
|
|
||||||
outputChannels,
|
|
||||||
buf.Elements() + offset);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Assumes that a bit pattern of zeroes == 0.0f
|
|
||||||
memset(buf.Elements() + offset, 0, outputChannels * frames * sizeof(AudioDataValue));
|
|
||||||
}
|
}
|
||||||
offset += frames * outputChannels;
|
if (channelData.Length() < aOutputChannels) {
|
||||||
|
// Up-mix. Note that this might actually make channelData have more
|
||||||
|
// than aOutputChannels temporarily.
|
||||||
|
AudioChannelsUpMix(&channelData, aOutputChannels, gZeroChannel);
|
||||||
|
}
|
||||||
|
if (channelData.Length() > aOutputChannels) {
|
||||||
|
// Down-mix.
|
||||||
|
DownmixAndInterleave(channelData, c.mBufferFormat, frames,
|
||||||
|
c.mVolume, aOutputChannels, buf.Elements() + offset);
|
||||||
|
} else {
|
||||||
|
InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
|
||||||
|
frames, c.mVolume,
|
||||||
|
aOutputChannels,
|
||||||
|
buf.Elements() + offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Assumes that a bit pattern of zeroes == 0.0f
|
||||||
|
memset(buf.Elements() + offset, 0, aOutputChannels * frames * sizeof(AudioDataValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset += frames * aOutputChannels;
|
||||||
|
|
||||||
if (!c.mTimeStamp.IsNull()) {
|
if (!c.mTimeStamp.IsNull()) {
|
||||||
TimeStamp now = TimeStamp::Now();
|
TimeStamp now = TimeStamp::Now();
|
||||||
// would be more efficient to c.mTimeStamp to ms on create time then pass here
|
// would be more efficient to c.mTimeStamp to ms on create time then pass here
|
||||||
|
@ -210,15 +207,9 @@ AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aOutput->Write(buf.Elements(), offset / outputChannels, &(mChunks[mChunks.Length() - 1].mTimeStamp));
|
if (offset) {
|
||||||
|
aMixer.Mix(buf.Elements(), aOutputChannels, offset, aSampleRate);
|
||||||
// `offset` is zero when all the chunks above are null (silence). We can
|
|
||||||
// safely skip the mixing here because filling `buf` with zero and then mixing
|
|
||||||
// it would have absolutly no effect in the mix.
|
|
||||||
if (aMixer && offset) {
|
|
||||||
aMixer->Mix(buf.Elements(), outputChannels, GetDuration(), aOutput->GetRate());
|
|
||||||
}
|
}
|
||||||
aOutput->Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,7 @@ public:
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
void ApplyVolume(float aVolume);
|
void ApplyVolume(float aVolume);
|
||||||
void WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer = nullptr);
|
void WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aChannelCount, uint32_t aSampleRate);
|
||||||
|
|
||||||
int ChannelCount() {
|
int ChannelCount() {
|
||||||
NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
|
NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
|
||||||
|
|
|
@ -334,10 +334,10 @@ MediaStreamGraphImpl::StreamTimeToGraphTime(MediaStream* aStream,
|
||||||
GraphTime
|
GraphTime
|
||||||
MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
|
MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
|
||||||
{
|
{
|
||||||
if (aStream->mAudioOutputStreams.IsEmpty()) {
|
if (!mMixedAudioStream) {
|
||||||
return IterationEnd();
|
return IterationEnd();
|
||||||
}
|
}
|
||||||
int64_t positionInFrames = aStream->mAudioOutputStreams[0].mStream->GetPositionInFrames();
|
int64_t positionInFrames = mMixedAudioStream->GetPositionInFrames();
|
||||||
if (positionInFrames < 0) {
|
if (positionInFrames < 0) {
|
||||||
return IterationEnd();
|
return IterationEnd();
|
||||||
}
|
}
|
||||||
|
@ -500,6 +500,7 @@ void
|
||||||
MediaStreamGraphImpl::UpdateStreamOrder()
|
MediaStreamGraphImpl::UpdateStreamOrder()
|
||||||
{
|
{
|
||||||
bool shouldMix = false;
|
bool shouldMix = false;
|
||||||
|
bool audioTrackPresent = false;
|
||||||
// Value of mCycleMarker for unvisited streams in cycle detection.
|
// Value of mCycleMarker for unvisited streams in cycle detection.
|
||||||
const uint32_t NOT_VISITED = UINT32_MAX;
|
const uint32_t NOT_VISITED = UINT32_MAX;
|
||||||
// Value of mCycleMarker for ordered streams in muted cycles.
|
// Value of mCycleMarker for ordered streams in muted cycles.
|
||||||
|
@ -513,26 +514,25 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
||||||
stream->AsSourceStream()->NeedsMixing()) {
|
stream->AsSourceStream()->NeedsMixing()) {
|
||||||
shouldMix = true;
|
shouldMix = true;
|
||||||
}
|
}
|
||||||
|
for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer(), MediaSegment::AUDIO);
|
||||||
|
!tracks.IsEnded(); tracks.Next()) {
|
||||||
|
audioTrackPresent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mMixer && shouldMix) {
|
if (!mMixer && shouldMix) {
|
||||||
mMixer = new AudioMixer(AudioMixerCallback);
|
mMixedAudioStream->SetMicrophoneActive(true);
|
||||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
if (shouldMix) {
|
||||||
for (uint32_t j = 0; j < mStreams[i]->mAudioOutputStreams.Length(); ++j) {
|
if (gFarendObserver && !mMixer.FindCallback(gFarendObserver)) {
|
||||||
mStreams[i]->mAudioOutputStreams[j].mStream->SetMicrophoneActive(true);
|
mMixer.AddCallback(gFarendObserver);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gFarendObserver) {
|
|
||||||
mMixer->AddCallback(gFarendObserver);
|
|
||||||
}
|
|
||||||
} else if (mMixer && !shouldMix) {
|
|
||||||
mMixer->RemoveCallback(gFarendObserver);
|
|
||||||
mMixer = nullptr;
|
|
||||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
|
||||||
for (uint32_t j = 0; j < mStreams[i]->mAudioOutputStreams.Length(); ++j) {
|
|
||||||
mStreams[i]->mAudioOutputStreams[j].mStream->SetMicrophoneActive(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mMixer.RemoveCallback(gFarendObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!audioTrackPresent && mMixedAudioStream) {
|
||||||
|
mMixedAudioStream = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The algorithm for finding cycles is based on Tim Leslie's iterative
|
// The algorithm for finding cycles is based on Tim Leslie's iterative
|
||||||
|
@ -890,50 +890,48 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
|
||||||
if (i < audioOutputStreamsFound.Length()) {
|
if (i < audioOutputStreamsFound.Length()) {
|
||||||
audioOutputStreamsFound[i] = true;
|
audioOutputStreamsFound[i] = true;
|
||||||
} else {
|
} else {
|
||||||
// No output stream created for this track yet. Check if it's time to
|
|
||||||
// create one.
|
|
||||||
GraphTime startTime =
|
|
||||||
StreamTimeToGraphTime(aStream, tracks->GetStartTimeRoundDown(),
|
|
||||||
INCLUDE_TRAILING_BLOCKED_INTERVAL);
|
|
||||||
if (startTime >= CurrentDriver()->StateComputedTime()) {
|
|
||||||
// The stream wants to play audio, but nothing will play for the forseeable
|
|
||||||
// future, so don't create the stream.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocating a AudioStream would be slow, so we finish the Init async
|
|
||||||
MediaStream::AudioOutputStream* audioOutputStream =
|
MediaStream::AudioOutputStream* audioOutputStream =
|
||||||
aStream->mAudioOutputStreams.AppendElement();
|
aStream->mAudioOutputStreams.AppendElement();
|
||||||
audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
|
audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
|
||||||
audioOutputStream->mBlockedAudioTime = 0;
|
audioOutputStream->mBlockedAudioTime = 0;
|
||||||
audioOutputStream->mLastTickWritten = 0;
|
audioOutputStream->mLastTickWritten = 0;
|
||||||
audioOutputStream->mStream = new AudioStream();
|
|
||||||
// XXX for now, allocate stereo output. But we need to fix this to
|
|
||||||
// match the system's ideal channel configuration.
|
|
||||||
// NOTE: we presume this is either fast or async-under-the-covers
|
|
||||||
audioOutputStream->mStream->Init(2, mSampleRate,
|
|
||||||
aStream->mAudioChannelType,
|
|
||||||
AudioStream::LowLatency);
|
|
||||||
audioOutputStream->mTrackID = tracks->GetID();
|
audioOutputStream->mTrackID = tracks->GetID();
|
||||||
|
|
||||||
// If there is a mixer, there is a micrphone active.
|
// If there is a mixer, there is a micrphone active.
|
||||||
audioOutputStream->mStream->SetMicrophoneActive(mMixer);
|
audioOutputStream->mStream->SetMicrophoneActive(mMixer);
|
||||||
|
|
||||||
LogLatency(AsyncLatencyLogger::AudioStreamCreate,
|
if (!mMixedAudioStream) {
|
||||||
reinterpret_cast<uint64_t>(aStream),
|
mMixedAudioStream = new AudioStream();
|
||||||
reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
|
// XXX for now, allocate stereo output. But we need to fix this to
|
||||||
|
// match the system's ideal channel configuration.
|
||||||
|
// NOTE: we presume this is either fast or async-under-the-covers
|
||||||
|
mMixedAudioStream->Init(AudioChannelCount(), mSampleRate,
|
||||||
|
AudioChannel::Normal,
|
||||||
|
AudioStream::LowLatency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
|
for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
|
||||||
if (!audioOutputStreamsFound[i]) {
|
if (!audioOutputStreamsFound[i]) {
|
||||||
aStream->mAudioOutputStreams[i].mStream->Shutdown();
|
|
||||||
aStream->mAudioOutputStreams.RemoveElementAt(i);
|
aStream->mAudioOutputStreams.RemoveElementAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MediaStreamGraphImpl::MixerCallback(AudioDataValue* aMixedBuffer,
|
||||||
|
AudioSampleFormat aFormat,
|
||||||
|
uint32_t aChannels,
|
||||||
|
uint32_t aFrames,
|
||||||
|
uint32_t aSampleRate)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mMixedAudioStream);
|
||||||
|
mMixedAudioStream->Write(aMixedBuffer, aFrames, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TrackTicks
|
TrackTicks
|
||||||
MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
||||||
GraphTime aFrom, GraphTime aTo)
|
GraphTime aFrom, GraphTime aTo)
|
||||||
|
@ -952,8 +950,6 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we're playing multiple copies of this stream at the same time, they're
|
|
||||||
// perfectly correlated so adding volumes is the right thing to do.
|
|
||||||
float volume = 0.0f;
|
float volume = 0.0f;
|
||||||
for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
|
for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
|
||||||
volume += aStream->mAudioOutputs[i].mVolume;
|
volume += aStream->mAudioOutputs[i].mVolume;
|
||||||
|
@ -1036,7 +1032,8 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
||||||
|
|
||||||
// Need unique id for stream & track - and we want it to match the inserter
|
// Need unique id for stream & track - and we want it to match the inserter
|
||||||
output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
|
output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
|
||||||
audioOutput.mStream, mMixer);
|
mMixer, AudioChannelCount(),
|
||||||
|
mSampleRate);
|
||||||
}
|
}
|
||||||
return ticksWritten;
|
return ticksWritten;
|
||||||
}
|
}
|
||||||
|
@ -1242,11 +1239,8 @@ MediaStreamGraphImpl::PauseAllAudioOutputs()
|
||||||
if (mAudioOutputsPaused) {
|
if (mAudioOutputsPaused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
if (mMixedAudioStream) {
|
||||||
MediaStream* s = mStreams[i];
|
mMixedAudioStream->Pause();
|
||||||
for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
|
|
||||||
s->mAudioOutputStreams[j].mStream->Pause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mAudioOutputsPaused = true;
|
mAudioOutputsPaused = true;
|
||||||
}
|
}
|
||||||
|
@ -1258,11 +1252,8 @@ MediaStreamGraphImpl::ResumeAllAudioOutputs()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
if (mMixedAudioStream) {
|
||||||
MediaStream* s = mStreams[i];
|
mMixedAudioStream->Resume();
|
||||||
for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
|
|
||||||
s->mAudioOutputStreams[j].mStream->Resume();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mAudioOutputsPaused = false;
|
mAudioOutputsPaused = false;
|
||||||
|
@ -1325,6 +1316,9 @@ MediaStreamGraphImpl::Process(GraphTime aFrom, GraphTime aTo)
|
||||||
// This is the number of frame that are written to the AudioStreams, for
|
// This is the number of frame that are written to the AudioStreams, for
|
||||||
// this cycle.
|
// this cycle.
|
||||||
TrackTicks ticksPlayed = 0;
|
TrackTicks ticksPlayed = 0;
|
||||||
|
|
||||||
|
mMixer.StartMixing();
|
||||||
|
|
||||||
// Figure out what each stream wants to do
|
// Figure out what each stream wants to do
|
||||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
||||||
MediaStream* stream = mStreams[i];
|
MediaStream* stream = mStreams[i];
|
||||||
|
@ -1356,8 +1350,8 @@ MediaStreamGraphImpl::Process(GraphTime aFrom, GraphTime aTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NotifyHasCurrentData(stream);
|
NotifyHasCurrentData(stream);
|
||||||
|
// Only playback audio and video in real-time mode
|
||||||
if (mRealtime) {
|
if (mRealtime) {
|
||||||
// Only playback audio and video in real-time mode
|
|
||||||
CreateOrDestroyAudioStreams(aFrom, stream);
|
CreateOrDestroyAudioStreams(aFrom, stream);
|
||||||
TrackTicks ticksPlayedForThisStream = PlayAudio(stream, aFrom, aTo);
|
TrackTicks ticksPlayedForThisStream = PlayAudio(stream, aFrom, aTo);
|
||||||
if (!ticksPlayed) {
|
if (!ticksPlayed) {
|
||||||
|
@ -1378,8 +1372,8 @@ MediaStreamGraphImpl::Process(GraphTime aFrom, GraphTime aTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMixer) {
|
if (ticksPlayed) {
|
||||||
mMixer->FinishMixing();
|
mMixer.FinishMixing();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allBlockedForever) {
|
if (!allBlockedForever) {
|
||||||
|
@ -1780,7 +1774,7 @@ MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||||
// - mVideoOutputs - elements
|
// - mVideoOutputs - elements
|
||||||
// - mLastPlayedVideoFrame
|
// - mLastPlayedVideoFrame
|
||||||
// - mListeners - elements
|
// - mListeners - elements
|
||||||
// - mAudioOutputStreams - elements
|
// - mAudioOutputStream - elements
|
||||||
|
|
||||||
amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
|
amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
|
||||||
amount += mAudioOutputs.SizeOfExcludingThis(aMallocSizeOf);
|
amount += mAudioOutputs.SizeOfExcludingThis(aMallocSizeOf);
|
||||||
|
@ -1792,10 +1786,6 @@ MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||||
amount += mBlocked.SizeOfExcludingThis(aMallocSizeOf);
|
amount += mBlocked.SizeOfExcludingThis(aMallocSizeOf);
|
||||||
amount += mGraphUpdateIndices.SizeOfExcludingThis(aMallocSizeOf);
|
amount += mGraphUpdateIndices.SizeOfExcludingThis(aMallocSizeOf);
|
||||||
amount += mConsumers.SizeOfExcludingThis(aMallocSizeOf);
|
amount += mConsumers.SizeOfExcludingThis(aMallocSizeOf);
|
||||||
amount += mAudioOutputStreams.SizeOfExcludingThis(aMallocSizeOf);
|
|
||||||
for (size_t i = 0; i < mAudioOutputStreams.Length(); i++) {
|
|
||||||
amount += mAudioOutputStreams[i].SizeOfExcludingThis(aMallocSizeOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
@ -1906,10 +1896,6 @@ MediaStream::DestroyImpl()
|
||||||
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
|
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
|
||||||
mConsumers[i]->Disconnect();
|
mConsumers[i]->Disconnect();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < mAudioOutputStreams.Length(); ++i) {
|
|
||||||
mAudioOutputStreams[i].mStream->Shutdown();
|
|
||||||
}
|
|
||||||
mAudioOutputStreams.Clear();
|
|
||||||
mGraph = nullptr;
|
mGraph = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2671,7 +2657,7 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate
|
||||||
, mNonRealtimeProcessing(false)
|
, mNonRealtimeProcessing(false)
|
||||||
, mStreamOrderDirty(false)
|
, mStreamOrderDirty(false)
|
||||||
, mLatencyLog(AsyncLatencyLogger::Get())
|
, mLatencyLog(AsyncLatencyLogger::Get())
|
||||||
, mMixer(nullptr)
|
, mMixedAudioStream(nullptr)
|
||||||
, mMemoryReportMonitor("MSGIMemory")
|
, mMemoryReportMonitor("MSGIMemory")
|
||||||
, mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
|
, mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
|
||||||
, mAudioStreamSizes()
|
, mAudioStreamSizes()
|
||||||
|
@ -2695,6 +2681,8 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate
|
||||||
mDriverHolder.Switch(new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS));
|
mDriverHolder.Switch(new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mMixer.AddCallback(this);
|
||||||
|
|
||||||
mLastMainThreadUpdate = TimeStamp::Now();
|
mLastMainThreadUpdate = TimeStamp::Now();
|
||||||
|
|
||||||
RegisterWeakMemoryReporter(this);
|
RegisterWeakMemoryReporter(this);
|
||||||
|
|
|
@ -435,6 +435,11 @@ public:
|
||||||
{
|
{
|
||||||
mAudioOutputs.AppendElement(AudioOutput(aKey));
|
mAudioOutputs.AppendElement(AudioOutput(aKey));
|
||||||
}
|
}
|
||||||
|
// Returns true if this stream has an audio output.
|
||||||
|
bool HasAudioOutput()
|
||||||
|
{
|
||||||
|
return !mAudioOutputs.IsEmpty();
|
||||||
|
}
|
||||||
void RemoveAudioOutputImpl(void* aKey);
|
void RemoveAudioOutputImpl(void* aKey);
|
||||||
void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
|
void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
|
||||||
{
|
{
|
||||||
|
@ -616,15 +621,7 @@ protected:
|
||||||
MediaTime mBlockedAudioTime;
|
MediaTime mBlockedAudioTime;
|
||||||
// Last tick written to the audio output.
|
// Last tick written to the audio output.
|
||||||
TrackTicks mLastTickWritten;
|
TrackTicks mLastTickWritten;
|
||||||
RefPtr<AudioStream> mStream;
|
|
||||||
TrackID mTrackID;
|
TrackID mTrackID;
|
||||||
|
|
||||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
||||||
{
|
|
||||||
size_t amount = 0;
|
|
||||||
amount += mStream->SizeOfIncludingThis(aMallocSizeOf);
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
nsTArray<AudioOutputStream> mAudioOutputStreams;
|
nsTArray<AudioOutputStream> mAudioOutputStreams;
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,13 @@
|
||||||
#include "Latency.h"
|
#include "Latency.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "GraphDriver.h"
|
#include "GraphDriver.h"
|
||||||
|
#include "AudioMixer.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class LinkedList;
|
class LinkedList;
|
||||||
|
|
||||||
class AudioMixer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assume we can run an iteration of the MediaStreamGraph loop in this much time
|
* Assume we can run an iteration of the MediaStreamGraph loop in this much time
|
||||||
* or less.
|
* or less.
|
||||||
|
@ -116,7 +115,8 @@ struct MessageBlock {
|
||||||
* OfflineAudioContext object.
|
* OfflineAudioContext object.
|
||||||
*/
|
*/
|
||||||
class MediaStreamGraphImpl : public MediaStreamGraph,
|
class MediaStreamGraphImpl : public MediaStreamGraph,
|
||||||
public nsIMemoryReporter {
|
public nsIMemoryReporter,
|
||||||
|
public MixerCallbackReceiver {
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIMEMORYREPORTER
|
NS_DECL_NSIMEMORYREPORTER
|
||||||
|
@ -350,13 +350,21 @@ public:
|
||||||
* If aStream needs an audio stream but doesn't have one, create it.
|
* If aStream needs an audio stream but doesn't have one, create it.
|
||||||
* If aStream doesn't need an audio stream but has one, destroy it.
|
* If aStream doesn't need an audio stream but has one, destroy it.
|
||||||
*/
|
*/
|
||||||
void CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
|
void CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime, MediaStream* aStream);
|
||||||
MediaStream* aStream);
|
|
||||||
/**
|
/**
|
||||||
* Queue audio (mix of stream audio and silence for blocked intervals)
|
* Queue audio (mix of stream audio and silence for blocked intervals)
|
||||||
* to the audio output stream. Returns the number of frames played.
|
* to the audio output stream. Returns the number of frames played.
|
||||||
*/
|
*/
|
||||||
TrackTicks PlayAudio(MediaStream* aStream, GraphTime aFrom, GraphTime aTo);
|
TrackTicks PlayAudio(MediaStream* aStream, GraphTime aFrom, GraphTime aTo);
|
||||||
|
|
||||||
|
/* The mixer call the Graph back when all the media streams that have audio
|
||||||
|
* outputs have been mixed down, so it can write to its AudioStream to output
|
||||||
|
* sound. */
|
||||||
|
virtual void MixerCallback(AudioDataValue* aMixedBuffer,
|
||||||
|
AudioSampleFormat aFormat,
|
||||||
|
uint32_t aChannels,
|
||||||
|
uint32_t aFrames,
|
||||||
|
uint32_t aSampleRate) MOZ_OVERRIDE;
|
||||||
/**
|
/**
|
||||||
* Set the correct current video frame for stream aStream.
|
* Set the correct current video frame for stream aStream.
|
||||||
*/
|
*/
|
||||||
|
@ -412,6 +420,8 @@ public:
|
||||||
|
|
||||||
TrackRate AudioSampleRate() const { return mSampleRate; }
|
TrackRate AudioSampleRate() const { return mSampleRate; }
|
||||||
TrackRate GraphRate() const { return mSampleRate; }
|
TrackRate GraphRate() const { return mSampleRate; }
|
||||||
|
// Always stereo for now.
|
||||||
|
uint32_t AudioChannelCount() { return 2; }
|
||||||
|
|
||||||
double MediaTimeToSeconds(GraphTime aTime)
|
double MediaTimeToSeconds(GraphTime aTime)
|
||||||
{
|
{
|
||||||
|
@ -600,10 +610,11 @@ public:
|
||||||
* Hold a ref to the Latency logger
|
* Hold a ref to the Latency logger
|
||||||
*/
|
*/
|
||||||
nsRefPtr<AsyncLatencyLogger> mLatencyLog;
|
nsRefPtr<AsyncLatencyLogger> mLatencyLog;
|
||||||
|
AudioMixer mMixer;
|
||||||
/**
|
/**
|
||||||
* If this is not null, all the audio output for the MSG will be mixed down.
|
* The mixed down audio output for this graph.
|
||||||
*/
|
*/
|
||||||
nsAutoPtr<AudioMixer> mMixer;
|
nsRefPtr<AudioStream> mMixedAudioStream;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~MediaStreamGraphImpl();
|
virtual ~MediaStreamGraphImpl();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче