content/media/AudioNodeStream.cpp

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 #include "AudioNodeStream.h"
michael@0 7
michael@0 8 #include "MediaStreamGraphImpl.h"
michael@0 9 #include "AudioNodeEngine.h"
michael@0 10 #include "ThreeDPoint.h"
michael@0 11 #include "AudioChannelFormat.h"
michael@0 12 #include "AudioParamTimeline.h"
michael@0 13 #include "AudioContext.h"
michael@0 14
michael@0 15 using namespace mozilla::dom;
michael@0 16
michael@0 17 namespace mozilla {
michael@0 18
michael@0 19 /**
michael@0 20 * An AudioNodeStream produces a single audio track with ID
michael@0 21 * AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate
michael@0 22 * for regular audio contexts, and the rate requested by the web content
michael@0 23 * for offline audio contexts.
michael@0 24 * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
michael@0 25 * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
michael@0 26 */
michael@0 27
michael@0 28 AudioNodeStream::~AudioNodeStream()
michael@0 29 {
michael@0 30 MOZ_COUNT_DTOR(AudioNodeStream);
michael@0 31 }
michael@0 32
michael@0 33 size_t
michael@0 34 AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 35 {
michael@0 36 size_t amount = 0;
michael@0 37
michael@0 38 // Not reported:
michael@0 39 // - mEngine
michael@0 40
michael@0 41 amount += ProcessedMediaStream::SizeOfExcludingThis(aMallocSizeOf);
michael@0 42 amount += mLastChunks.SizeOfExcludingThis(aMallocSizeOf);
michael@0 43 for (size_t i = 0; i < mLastChunks.Length(); i++) {
michael@0 44 // NB: This is currently unshared only as there are instances of
michael@0 45 // double reporting in DMD otherwise.
michael@0 46 amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 47 }
michael@0 48
michael@0 49 return amount;
michael@0 50 }
michael@0 51
michael@0 52 size_t
michael@0 53 AudioNodeStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 54 {
michael@0 55 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 56 }
michael@0 57
michael@0 58 void
michael@0 59 AudioNodeStream::SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 60 AudioNodeSizes& aUsage) const
michael@0 61 {
michael@0 62 // Explicitly separate out the stream memory.
michael@0 63 aUsage.mStream = SizeOfIncludingThis(aMallocSizeOf);
michael@0 64
michael@0 65 if (mEngine) {
michael@0 66 // This will fill out the rest of |aUsage|.
michael@0 67 mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
michael@0 68 }
michael@0 69 }
michael@0 70
michael@0 71 void
michael@0 72 AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
michael@0 73 double aStreamTime)
michael@0 74 {
michael@0 75 class Message : public ControlMessage {
michael@0 76 public:
michael@0 77 Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
michael@0 78 double aStreamTime)
michael@0 79 : ControlMessage(aStream), mStreamTime(aStreamTime),
michael@0 80 mRelativeToStream(aRelativeToStream), mIndex(aIndex) {}
michael@0 81 virtual void Run()
michael@0 82 {
michael@0 83 static_cast<AudioNodeStream*>(mStream)->
michael@0 84 SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
michael@0 85 }
michael@0 86 double mStreamTime;
michael@0 87 MediaStream* mRelativeToStream;
michael@0 88 uint32_t mIndex;
michael@0 89 };
michael@0 90
michael@0 91 MOZ_ASSERT(this);
michael@0 92 GraphImpl()->AppendMessage(new Message(this, aIndex,
michael@0 93 aContext->DestinationStream(),
michael@0 94 aContext->DOMTimeToStreamTime(aStreamTime)));
michael@0 95 }
michael@0 96
michael@0 97 void
michael@0 98 AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
michael@0 99 double aStreamTime)
michael@0 100 {
michael@0 101 TrackTicks ticks = TicksFromDestinationTime(aRelativeToStream, aStreamTime);
michael@0 102 mEngine->SetStreamTimeParameter(aIndex, ticks);
michael@0 103 }
michael@0 104
michael@0 105 void
michael@0 106 AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
michael@0 107 {
michael@0 108 class Message : public ControlMessage {
michael@0 109 public:
michael@0 110 Message(AudioNodeStream* aStream, uint32_t aIndex, double aValue)
michael@0 111 : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
michael@0 112 virtual void Run()
michael@0 113 {
michael@0 114 static_cast<AudioNodeStream*>(mStream)->Engine()->
michael@0 115 SetDoubleParameter(mIndex, mValue);
michael@0 116 }
michael@0 117 double mValue;
michael@0 118 uint32_t mIndex;
michael@0 119 };
michael@0 120
michael@0 121 MOZ_ASSERT(this);
michael@0 122 GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
michael@0 123 }
michael@0 124
michael@0 125 void
michael@0 126 AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
michael@0 127 {
michael@0 128 class Message : public ControlMessage {
michael@0 129 public:
michael@0 130 Message(AudioNodeStream* aStream, uint32_t aIndex, int32_t aValue)
michael@0 131 : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
michael@0 132 virtual void Run()
michael@0 133 {
michael@0 134 static_cast<AudioNodeStream*>(mStream)->Engine()->
michael@0 135 SetInt32Parameter(mIndex, mValue);
michael@0 136 }
michael@0 137 int32_t mValue;
michael@0 138 uint32_t mIndex;
michael@0 139 };
michael@0 140
michael@0 141 MOZ_ASSERT(this);
michael@0 142 GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
michael@0 143 }
michael@0 144
michael@0 145 void
michael@0 146 AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
michael@0 147 const AudioParamTimeline& aValue)
michael@0 148 {
michael@0 149 class Message : public ControlMessage {
michael@0 150 public:
michael@0 151 Message(AudioNodeStream* aStream, uint32_t aIndex,
michael@0 152 const AudioParamTimeline& aValue)
michael@0 153 : ControlMessage(aStream),
michael@0 154 mValue(aValue),
michael@0 155 mSampleRate(aStream->SampleRate()),
michael@0 156 mIndex(aIndex) {}
michael@0 157 virtual void Run()
michael@0 158 {
michael@0 159 static_cast<AudioNodeStream*>(mStream)->Engine()->
michael@0 160 SetTimelineParameter(mIndex, mValue, mSampleRate);
michael@0 161 }
michael@0 162 AudioParamTimeline mValue;
michael@0 163 TrackRate mSampleRate;
michael@0 164 uint32_t mIndex;
michael@0 165 };
michael@0 166 GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
michael@0 167 }
michael@0 168
michael@0 169 void
michael@0 170 AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
michael@0 171 {
michael@0 172 class Message : public ControlMessage {
michael@0 173 public:
michael@0 174 Message(AudioNodeStream* aStream, uint32_t aIndex, const ThreeDPoint& aValue)
michael@0 175 : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
michael@0 176 virtual void Run()
michael@0 177 {
michael@0 178 static_cast<AudioNodeStream*>(mStream)->Engine()->
michael@0 179 SetThreeDPointParameter(mIndex, mValue);
michael@0 180 }
michael@0 181 ThreeDPoint mValue;
michael@0 182 uint32_t mIndex;
michael@0 183 };
michael@0 184
michael@0 185 MOZ_ASSERT(this);
michael@0 186 GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
michael@0 187 }
michael@0 188
michael@0 189 void
michael@0 190 AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
michael@0 191 {
michael@0 192 class Message : public ControlMessage {
michael@0 193 public:
michael@0 194 Message(AudioNodeStream* aStream,
michael@0 195 already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
michael@0 196 : ControlMessage(aStream), mBuffer(aBuffer) {}
michael@0 197 virtual void Run()
michael@0 198 {
michael@0 199 static_cast<AudioNodeStream*>(mStream)->Engine()->
michael@0 200 SetBuffer(mBuffer.forget());
michael@0 201 }
michael@0 202 nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
michael@0 203 };
michael@0 204
michael@0 205 MOZ_ASSERT(this);
michael@0 206 GraphImpl()->AppendMessage(new Message(this, aBuffer));
michael@0 207 }
michael@0 208
michael@0 209 void
michael@0 210 AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
michael@0 211 {
michael@0 212 class Message : public ControlMessage {
michael@0 213 public:
michael@0 214 Message(AudioNodeStream* aStream,
michael@0 215 nsTArray<float>& aData)
michael@0 216 : ControlMessage(aStream)
michael@0 217 {
michael@0 218 mData.SwapElements(aData);
michael@0 219 }
michael@0 220 virtual void Run()
michael@0 221 {
michael@0 222 static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
michael@0 223 }
michael@0 224 nsTArray<float> mData;
michael@0 225 };
michael@0 226
michael@0 227 MOZ_ASSERT(this);
michael@0 228 GraphImpl()->AppendMessage(new Message(this, aData));
michael@0 229 }
michael@0 230
michael@0 231 void
michael@0 232 AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
michael@0 233 ChannelCountMode aChannelCountMode,
michael@0 234 ChannelInterpretation aChannelInterpretation)
michael@0 235 {
michael@0 236 class Message : public ControlMessage {
michael@0 237 public:
michael@0 238 Message(AudioNodeStream* aStream,
michael@0 239 uint32_t aNumberOfChannels,
michael@0 240 ChannelCountMode aChannelCountMode,
michael@0 241 ChannelInterpretation aChannelInterpretation)
michael@0 242 : ControlMessage(aStream),
michael@0 243 mNumberOfChannels(aNumberOfChannels),
michael@0 244 mChannelCountMode(aChannelCountMode),
michael@0 245 mChannelInterpretation(aChannelInterpretation)
michael@0 246 {}
michael@0 247 virtual void Run()
michael@0 248 {
michael@0 249 static_cast<AudioNodeStream*>(mStream)->
michael@0 250 SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
michael@0 251 mChannelInterpretation);
michael@0 252 }
michael@0 253 uint32_t mNumberOfChannels;
michael@0 254 ChannelCountMode mChannelCountMode;
michael@0 255 ChannelInterpretation mChannelInterpretation;
michael@0 256 };
michael@0 257
michael@0 258 MOZ_ASSERT(this);
michael@0 259 GraphImpl()->AppendMessage(new Message(this, aNumberOfChannels,
michael@0 260 aChannelCountMode,
michael@0 261 aChannelInterpretation));
michael@0 262 }
michael@0 263
michael@0 264 void
michael@0 265 AudioNodeStream::SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
michael@0 266 ChannelCountMode aChannelCountMode,
michael@0 267 ChannelInterpretation aChannelInterpretation)
michael@0 268 {
michael@0 269 // Make sure that we're not clobbering any significant bits by fitting these
michael@0 270 // values in 16 bits.
michael@0 271 MOZ_ASSERT(int(aChannelCountMode) < INT16_MAX);
michael@0 272 MOZ_ASSERT(int(aChannelInterpretation) < INT16_MAX);
michael@0 273
michael@0 274 mNumberOfInputChannels = aNumberOfChannels;
michael@0 275 mChannelCountMode = aChannelCountMode;
michael@0 276 mChannelInterpretation = aChannelInterpretation;
michael@0 277 }
michael@0 278
michael@0 279 uint32_t
michael@0 280 AudioNodeStream::ComputedNumberOfChannels(uint32_t aInputChannelCount)
michael@0 281 {
michael@0 282 switch (mChannelCountMode) {
michael@0 283 case ChannelCountMode::Explicit:
michael@0 284 // Disregard the channel count we've calculated from inputs, and just use
michael@0 285 // mNumberOfInputChannels.
michael@0 286 return mNumberOfInputChannels;
michael@0 287 case ChannelCountMode::Clamped_max:
michael@0 288 // Clamp the computed output channel count to mNumberOfInputChannels.
michael@0 289 return std::min(aInputChannelCount, mNumberOfInputChannels);
michael@0 290 default:
michael@0 291 case ChannelCountMode::Max:
michael@0 292 // Nothing to do here, just shut up the compiler warning.
michael@0 293 return aInputChannelCount;
michael@0 294 }
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 AudioNodeStream::ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex)
michael@0 299 {
michael@0 300 uint32_t inputCount = mInputs.Length();
michael@0 301 uint32_t outputChannelCount = 1;
michael@0 302 nsAutoTArray<AudioChunk*,250> inputChunks;
michael@0 303 for (uint32_t i = 0; i < inputCount; ++i) {
michael@0 304 if (aPortIndex != mInputs[i]->InputNumber()) {
michael@0 305 // This input is connected to a different port
michael@0 306 continue;
michael@0 307 }
michael@0 308 MediaStream* s = mInputs[i]->GetSource();
michael@0 309 AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
michael@0 310 MOZ_ASSERT(a == s->AsAudioNodeStream());
michael@0 311 if (a->IsAudioParamStream()) {
michael@0 312 continue;
michael@0 313 }
michael@0 314
michael@0 315 // It is possible for mLastChunks to be empty here, because `a` might be a
michael@0 316 // AudioNodeStream that has not been scheduled yet, because it is further
michael@0 317 // down the graph _but_ as a connection to this node. Because we enforce the
michael@0 318 // presence of at least one DelayNode, with at least one block of delay, and
michael@0 319 // because the output of a DelayNode when it has been fed less that
michael@0 320 // `delayTime` amount of audio is silence, we can simply continue here,
michael@0 321 // because this input would not influence the output of this node. Next
michael@0 322 // iteration, a->mLastChunks.IsEmpty() will be false, and everthing will
michael@0 323 // work as usual.
michael@0 324 if (a->mLastChunks.IsEmpty()) {
michael@0 325 continue;
michael@0 326 }
michael@0 327
michael@0 328 AudioChunk* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
michael@0 329 MOZ_ASSERT(chunk);
michael@0 330 if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
michael@0 331 continue;
michael@0 332 }
michael@0 333
michael@0 334 inputChunks.AppendElement(chunk);
michael@0 335 outputChannelCount =
michael@0 336 GetAudioChannelsSuperset(outputChannelCount, chunk->mChannelData.Length());
michael@0 337 }
michael@0 338
michael@0 339 outputChannelCount = ComputedNumberOfChannels(outputChannelCount);
michael@0 340
michael@0 341 uint32_t inputChunkCount = inputChunks.Length();
michael@0 342 if (inputChunkCount == 0 ||
michael@0 343 (inputChunkCount == 1 && inputChunks[0]->mChannelData.Length() == 0)) {
michael@0 344 aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 345 return;
michael@0 346 }
michael@0 347
michael@0 348 if (inputChunkCount == 1 &&
michael@0 349 inputChunks[0]->mChannelData.Length() == outputChannelCount) {
michael@0 350 aTmpChunk = *inputChunks[0];
michael@0 351 return;
michael@0 352 }
michael@0 353
michael@0 354 if (outputChannelCount == 0) {
michael@0 355 aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 356 return;
michael@0 357 }
michael@0 358
michael@0 359 AllocateAudioBlock(outputChannelCount, &aTmpChunk);
michael@0 360 // The static storage here should be 1KB, so it's fine
michael@0 361 nsAutoTArray<float, GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE> downmixBuffer;
michael@0 362
michael@0 363 for (uint32_t i = 0; i < inputChunkCount; ++i) {
michael@0 364 AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer);
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 void
michael@0 369 AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
michael@0 370 AudioChunk* aBlock,
michael@0 371 nsTArray<float>* aDownmixBuffer)
michael@0 372 {
michael@0 373 nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
michael@0 374 UpMixDownMixChunk(&aChunk, aBlock->mChannelData.Length(), channels, *aDownmixBuffer);
michael@0 375
michael@0 376 for (uint32_t c = 0; c < channels.Length(); ++c) {
michael@0 377 const float* inputData = static_cast<const float*>(channels[c]);
michael@0 378 float* outputData = static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c]));
michael@0 379 if (inputData) {
michael@0 380 if (aInputIndex == 0) {
michael@0 381 AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
michael@0 382 } else {
michael@0 383 AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
michael@0 384 }
michael@0 385 } else {
michael@0 386 if (aInputIndex == 0) {
michael@0 387 PodZero(outputData, WEBAUDIO_BLOCK_SIZE);
michael@0 388 }
michael@0 389 }
michael@0 390 }
michael@0 391 }
michael@0 392
michael@0 393 void
michael@0 394 AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
michael@0 395 uint32_t aOutputChannelCount,
michael@0 396 nsTArray<const void*>& aOutputChannels,
michael@0 397 nsTArray<float>& aDownmixBuffer)
michael@0 398 {
michael@0 399 static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {0.f};
michael@0 400
michael@0 401 aOutputChannels.AppendElements(aChunk->mChannelData);
michael@0 402 if (aOutputChannels.Length() < aOutputChannelCount) {
michael@0 403 if (mChannelInterpretation == ChannelInterpretation::Speakers) {
michael@0 404 AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, nullptr);
michael@0 405 NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
michael@0 406 "We called GetAudioChannelsSuperset to avoid this");
michael@0 407 } else {
michael@0 408 // Fill up the remaining aOutputChannels by zeros
michael@0 409 for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
michael@0 410 aOutputChannels.AppendElement(silenceChannel);
michael@0 411 }
michael@0 412 }
michael@0 413 } else if (aOutputChannels.Length() > aOutputChannelCount) {
michael@0 414 if (mChannelInterpretation == ChannelInterpretation::Speakers) {
michael@0 415 nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
michael@0 416 outputChannels.SetLength(aOutputChannelCount);
michael@0 417 aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
michael@0 418 for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
michael@0 419 outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
michael@0 420 }
michael@0 421
michael@0 422 AudioChannelsDownMix(aOutputChannels, outputChannels.Elements(),
michael@0 423 aOutputChannelCount, WEBAUDIO_BLOCK_SIZE);
michael@0 424
michael@0 425 aOutputChannels.SetLength(aOutputChannelCount);
michael@0 426 for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
michael@0 427 aOutputChannels[j] = outputChannels[j];
michael@0 428 }
michael@0 429 } else {
michael@0 430 // Drop the remaining aOutputChannels
michael@0 431 aOutputChannels.RemoveElementsAt(aOutputChannelCount,
michael@0 432 aOutputChannels.Length() - aOutputChannelCount);
michael@0 433 }
michael@0 434 }
michael@0 435 }
michael@0 436
michael@0 437 // The MediaStreamGraph guarantees that this is actually one block, for
michael@0 438 // AudioNodeStreams.
michael@0 439 void
michael@0 440 AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
michael@0 441 {
michael@0 442 EnsureTrack(AUDIO_TRACK, mSampleRate);
michael@0 443 // No more tracks will be coming
michael@0 444 mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
michael@0 445
michael@0 446 uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
michael@0 447 mLastChunks.SetLength(outputCount);
michael@0 448
michael@0 449 // Consider this stream blocked if it has already finished output. Normally
michael@0 450 // mBlocked would reflect this, but due to rounding errors our audio track may
michael@0 451 // appear to extend slightly beyond aFrom, so we might not be blocked yet.
michael@0 452 bool blocked = mFinished || mBlocked.GetAt(aFrom);
michael@0 453 // If the stream has finished at this time, it will be blocked.
michael@0 454 if (mMuted || blocked) {
michael@0 455 for (uint16_t i = 0; i < outputCount; ++i) {
michael@0 456 mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 457 }
michael@0 458 } else {
michael@0 459 // We need to generate at least one input
michael@0 460 uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
michael@0 461 OutputChunks inputChunks;
michael@0 462 inputChunks.SetLength(maxInputs);
michael@0 463 for (uint16_t i = 0; i < maxInputs; ++i) {
michael@0 464 ObtainInputBlock(inputChunks[i], i);
michael@0 465 }
michael@0 466 bool finished = false;
michael@0 467 if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
michael@0 468 mEngine->ProcessBlock(this, inputChunks[0], &mLastChunks[0], &finished);
michael@0 469 } else {
michael@0 470 mEngine->ProcessBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
michael@0 471 }
michael@0 472 for (uint16_t i = 0; i < outputCount; ++i) {
michael@0 473 NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
michael@0 474 "Invalid WebAudio chunk size");
michael@0 475 }
michael@0 476 if (finished) {
michael@0 477 mMarkAsFinishedAfterThisBlock = true;
michael@0 478 }
michael@0 479
michael@0 480 if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
michael@0 481 for (uint32_t i = 0; i < outputCount; ++i) {
michael@0 482 mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 483 }
michael@0 484 }
michael@0 485 }
michael@0 486
michael@0 487 if (!blocked) {
michael@0 488 // Don't output anything while blocked
michael@0 489 AdvanceOutputSegment();
michael@0 490 if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
michael@0 491 // This stream was finished the last time that we looked at it, and all
michael@0 492 // of the depending streams have finished their output as well, so now
michael@0 493 // it's time to mark this stream as finished.
michael@0 494 FinishOutput();
michael@0 495 }
michael@0 496 }
michael@0 497 }
michael@0 498
michael@0 499 void
michael@0 500 AudioNodeStream::AdvanceOutputSegment()
michael@0 501 {
michael@0 502 StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate);
michael@0 503 AudioSegment* segment = track->Get<AudioSegment>();
michael@0 504
michael@0 505 if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
michael@0 506 segment->AppendAndConsumeChunk(&mLastChunks[0]);
michael@0 507 } else {
michael@0 508 segment->AppendNullData(mLastChunks[0].GetDuration());
michael@0 509 }
michael@0 510
michael@0 511 for (uint32_t j = 0; j < mListeners.Length(); ++j) {
michael@0 512 MediaStreamListener* l = mListeners[j];
michael@0 513 AudioChunk copyChunk = mLastChunks[0];
michael@0 514 AudioSegment tmpSegment;
michael@0 515 tmpSegment.AppendAndConsumeChunk(&copyChunk);
michael@0 516 l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
michael@0 517 mSampleRate, segment->GetDuration(), 0,
michael@0 518 tmpSegment);
michael@0 519 }
michael@0 520 }
michael@0 521
michael@0 522 TrackTicks
michael@0 523 AudioNodeStream::GetCurrentPosition()
michael@0 524 {
michael@0 525 return EnsureTrack(AUDIO_TRACK, mSampleRate)->Get<AudioSegment>()->GetDuration();
michael@0 526 }
michael@0 527
michael@0 528 void
michael@0 529 AudioNodeStream::FinishOutput()
michael@0 530 {
michael@0 531 if (IsFinishedOnGraphThread()) {
michael@0 532 return;
michael@0 533 }
michael@0 534
michael@0 535 StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate);
michael@0 536 track->SetEnded();
michael@0 537 FinishOnGraphThread();
michael@0 538
michael@0 539 for (uint32_t j = 0; j < mListeners.Length(); ++j) {
michael@0 540 MediaStreamListener* l = mListeners[j];
michael@0 541 AudioSegment emptySegment;
michael@0 542 l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
michael@0 543 mSampleRate,
michael@0 544 track->GetSegment()->GetDuration(),
michael@0 545 MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
michael@0 546 }
michael@0 547 }
michael@0 548
michael@0 549 double
michael@0 550 AudioNodeStream::TimeFromDestinationTime(AudioNodeStream* aDestination,
michael@0 551 double aSeconds)
michael@0 552 {
michael@0 553 MOZ_ASSERT(aDestination->SampleRate() == SampleRate());
michael@0 554
michael@0 555 double destinationSeconds = std::max(0.0, aSeconds);
michael@0 556 StreamTime streamTime = SecondsToMediaTime(destinationSeconds);
michael@0 557 // MediaTime does not have the resolution of double
michael@0 558 double offset = destinationSeconds - MediaTimeToSeconds(streamTime);
michael@0 559
michael@0 560 GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime);
michael@0 561 StreamTime thisStreamTime = GraphTimeToStreamTimeOptimistic(graphTime);
michael@0 562 double thisSeconds = MediaTimeToSeconds(thisStreamTime) + offset;
michael@0 563 MOZ_ASSERT(thisSeconds >= 0.0);
michael@0 564 return thisSeconds;
michael@0 565 }
michael@0 566
michael@0 567 TrackTicks
michael@0 568 AudioNodeStream::TicksFromDestinationTime(MediaStream* aDestination,
michael@0 569 double aSeconds)
michael@0 570 {
michael@0 571 AudioNodeStream* destination = aDestination->AsAudioNodeStream();
michael@0 572 MOZ_ASSERT(destination);
michael@0 573
michael@0 574 double thisSeconds = TimeFromDestinationTime(destination, aSeconds);
michael@0 575 // Round to nearest
michael@0 576 TrackTicks ticks = thisSeconds * SampleRate() + 0.5;
michael@0 577 return ticks;
michael@0 578 }
michael@0 579
michael@0 580 double
michael@0 581 AudioNodeStream::DestinationTimeFromTicks(AudioNodeStream* aDestination,
michael@0 582 TrackTicks aPosition)
michael@0 583 {
michael@0 584 MOZ_ASSERT(SampleRate() == aDestination->SampleRate());
michael@0 585 StreamTime sourceTime = TicksToTimeRoundDown(SampleRate(), aPosition);
michael@0 586 GraphTime graphTime = StreamTimeToGraphTime(sourceTime);
michael@0 587 StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime);
michael@0 588 return MediaTimeToSeconds(destinationTime);
michael@0 589 }
michael@0 590
michael@0 591 }

mercurial