content/media/webaudio/AudioDestinationNode.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webaudio/AudioDestinationNode.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,603 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "AudioDestinationNode.h"
    1.11 +#include "mozilla/dom/AudioDestinationNodeBinding.h"
    1.12 +#include "mozilla/Preferences.h"
    1.13 +#include "AudioChannelAgent.h"
    1.14 +#include "AudioChannelService.h"
    1.15 +#include "AudioNodeEngine.h"
    1.16 +#include "AudioNodeStream.h"
    1.17 +#include "MediaStreamGraph.h"
    1.18 +#include "OfflineAudioCompletionEvent.h"
    1.19 +#include "nsIInterfaceRequestorUtils.h"
    1.20 +#include "nsIDocShell.h"
    1.21 +#include "nsIPermissionManager.h"
    1.22 +#include "nsIScriptObjectPrincipal.h"
    1.23 +#include "nsServiceManagerUtils.h"
    1.24 +#include "nsIAppShell.h"
    1.25 +#include "nsWidgetsCID.h"
    1.26 +
    1.27 +namespace mozilla {
    1.28 +namespace dom {
    1.29 +
    1.30 +static uint8_t gWebAudioOutputKey;
    1.31 +
    1.32 +class OfflineDestinationNodeEngine : public AudioNodeEngine
    1.33 +{
    1.34 +public:
    1.35 +  typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
    1.36 +
    1.37 +  OfflineDestinationNodeEngine(AudioDestinationNode* aNode,
    1.38 +                               uint32_t aNumberOfChannels,
    1.39 +                               uint32_t aLength,
    1.40 +                               float aSampleRate)
    1.41 +    : AudioNodeEngine(aNode)
    1.42 +    , mWriteIndex(0)
    1.43 +    , mLength(aLength)
    1.44 +    , mSampleRate(aSampleRate)
    1.45 +  {
    1.46 +    // These allocations might fail if content provides a huge number of
    1.47 +    // channels or size, but it's OK since we'll deal with the failure
    1.48 +    // gracefully.
    1.49 +    if (mInputChannels.SetLength(aNumberOfChannels)) {
    1.50 +      static const fallible_t fallible = fallible_t();
    1.51 +      for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
    1.52 +        mInputChannels[i] = new(fallible) float[aLength];
    1.53 +        if (!mInputChannels[i]) {
    1.54 +          mInputChannels.Clear();
    1.55 +          break;
    1.56 +        }
    1.57 +      }
    1.58 +    }
    1.59 +  }
    1.60 +
    1.61 +  virtual void ProcessBlock(AudioNodeStream* aStream,
    1.62 +                            const AudioChunk& aInput,
    1.63 +                            AudioChunk* aOutput,
    1.64 +                            bool* aFinished) MOZ_OVERRIDE
    1.65 +  {
    1.66 +    // Do this just for the sake of political correctness; this output
    1.67 +    // will not go anywhere.
    1.68 +    *aOutput = aInput;
    1.69 +
    1.70 +    // Handle the case of allocation failure in the input buffer
    1.71 +    if (mInputChannels.IsEmpty()) {
    1.72 +      return;
    1.73 +    }
    1.74 +
    1.75 +    if (mWriteIndex >= mLength) {
    1.76 +      NS_ASSERTION(mWriteIndex == mLength, "Overshot length");
    1.77 +      // Don't record any more.
    1.78 +      return;
    1.79 +    }
    1.80 +
    1.81 +    // Record our input buffer
    1.82 +    MOZ_ASSERT(mWriteIndex < mLength, "How did this happen?");
    1.83 +    const uint32_t duration = std::min(WEBAUDIO_BLOCK_SIZE, mLength - mWriteIndex);
    1.84 +    const uint32_t commonChannelCount = std::min(mInputChannels.Length(),
    1.85 +                                                 aInput.mChannelData.Length());
    1.86 +    // First, copy as many channels in the input as we have
    1.87 +    for (uint32_t i = 0; i < commonChannelCount; ++i) {
    1.88 +      if (aInput.IsNull()) {
    1.89 +        PodZero(mInputChannels[i] + mWriteIndex, duration);
    1.90 +      } else {
    1.91 +        const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
    1.92 +        if (duration == WEBAUDIO_BLOCK_SIZE) {
    1.93 +          // Use the optimized version of the copy with scale operation
    1.94 +          AudioBlockCopyChannelWithScale(inputBuffer, aInput.mVolume,
    1.95 +                                         mInputChannels[i] + mWriteIndex);
    1.96 +        } else {
    1.97 +          if (aInput.mVolume == 1.0f) {
    1.98 +            PodCopy(mInputChannels[i] + mWriteIndex, inputBuffer, duration);
    1.99 +          } else {
   1.100 +            for (uint32_t j = 0; j < duration; ++j) {
   1.101 +              mInputChannels[i][mWriteIndex + j] = aInput.mVolume * inputBuffer[j];
   1.102 +            }
   1.103 +          }
   1.104 +        }
   1.105 +      }
   1.106 +    }
   1.107 +    // Then, silence all of the remaining channels
   1.108 +    for (uint32_t i = commonChannelCount; i < mInputChannels.Length(); ++i) {
   1.109 +      PodZero(mInputChannels[i] + mWriteIndex, duration);
   1.110 +    }
   1.111 +    mWriteIndex += duration;
   1.112 +
   1.113 +    if (mWriteIndex >= mLength) {
   1.114 +      NS_ASSERTION(mWriteIndex == mLength, "Overshot length");
   1.115 +      // Go to finished state. When the graph's current time eventually reaches
   1.116 +      // the end of the stream, then the main thread will be notified and we'll
   1.117 +      // shut down the AudioContext.
   1.118 +      *aFinished = true;
   1.119 +    }
   1.120 +  }
   1.121 +
   1.122 +  void FireOfflineCompletionEvent(AudioDestinationNode* aNode)
   1.123 +  {
   1.124 +    AudioContext* context = aNode->Context();
   1.125 +    context->Shutdown();
   1.126 +    // Shutdown drops self reference, but the context is still referenced by aNode,
   1.127 +    // which is strongly referenced by the runnable that called
   1.128 +    // AudioDestinationNode::FireOfflineCompletionEvent.
   1.129 +
   1.130 +    AutoPushJSContext cx(context->GetJSContext());
   1.131 +    if (!cx) {
   1.132 +
   1.133 +      return;
   1.134 +    }
   1.135 +    JSAutoRequest ar(cx);
   1.136 +
   1.137 +    // Create the input buffer
   1.138 +    ErrorResult rv;
   1.139 +    nsRefPtr<AudioBuffer> renderedBuffer =
   1.140 +      AudioBuffer::Create(context, mInputChannels.Length(),
   1.141 +                          mLength, mSampleRate, cx, rv);
   1.142 +    if (rv.Failed()) {
   1.143 +      return;
   1.144 +    }
   1.145 +    for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
   1.146 +      renderedBuffer->SetRawChannelContents(cx, i, mInputChannels[i]);
   1.147 +    }
   1.148 +
   1.149 +    nsRefPtr<OfflineAudioCompletionEvent> event =
   1.150 +        new OfflineAudioCompletionEvent(context, nullptr, nullptr);
   1.151 +    event->InitEvent(renderedBuffer);
   1.152 +    context->DispatchTrustedEvent(event);
   1.153 +  }
   1.154 +
   1.155 +  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   1.156 +  {
   1.157 +    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
   1.158 +    amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
   1.159 +    return amount;
   1.160 +  }
   1.161 +
   1.162 +  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   1.163 +  {
   1.164 +    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.165 +  }
   1.166 +
   1.167 +private:
   1.168 +  // The input to the destination node is recorded in the mInputChannels buffer.
   1.169 +  // When this buffer fills up with mLength frames, the buffered input is sent
   1.170 +  // to the main thread in order to dispatch OfflineAudioCompletionEvent.
   1.171 +  InputChannels mInputChannels;
   1.172 +  // An index representing the next offset in mInputChannels to be written to.
   1.173 +  uint32_t mWriteIndex;
   1.174 +  // How many frames the OfflineAudioContext intends to produce.
   1.175 +  uint32_t mLength;
   1.176 +  float mSampleRate;
   1.177 +};
   1.178 +
   1.179 +class DestinationNodeEngine : public AudioNodeEngine
   1.180 +{
   1.181 +public:
   1.182 +  explicit DestinationNodeEngine(AudioDestinationNode* aNode)
   1.183 +    : AudioNodeEngine(aNode)
   1.184 +    , mVolume(1.0f)
   1.185 +  {
   1.186 +  }
   1.187 +
   1.188 +  virtual void ProcessBlock(AudioNodeStream* aStream,
   1.189 +                            const AudioChunk& aInput,
   1.190 +                            AudioChunk* aOutput,
   1.191 +                            bool* aFinished) MOZ_OVERRIDE
   1.192 +  {
   1.193 +    *aOutput = aInput;
   1.194 +    aOutput->mVolume *= mVolume;
   1.195 +  }
   1.196 +
   1.197 +  virtual void SetDoubleParameter(uint32_t aIndex, double aParam) MOZ_OVERRIDE
   1.198 +  {
   1.199 +    if (aIndex == VOLUME) {
   1.200 +      mVolume = aParam;
   1.201 +    }
   1.202 +  }
   1.203 +
   1.204 +  enum Parameters {
   1.205 +    VOLUME,
   1.206 +  };
   1.207 +
   1.208 +  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   1.209 +  {
   1.210 +    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.211 +  }
   1.212 +
   1.213 +private:
   1.214 +  float mVolume;
   1.215 +};
   1.216 +
   1.217 +static bool UseAudioChannelService()
   1.218 +{
   1.219 +  return Preferences::GetBool("media.useAudioChannelService");
   1.220 +}
   1.221 +
   1.222 +NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
   1.223 +                                   mAudioChannelAgent)
   1.224 +
   1.225 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
   1.226 +  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   1.227 +  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
   1.228 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1.229 +NS_INTERFACE_MAP_END_INHERITING(AudioNode)
   1.230 +
   1.231 +NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)
   1.232 +NS_IMPL_RELEASE_INHERITED(AudioDestinationNode, AudioNode)
   1.233 +
   1.234 +AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
   1.235 +                                           bool aIsOffline,
   1.236 +                                           AudioChannel aChannel,
   1.237 +                                           uint32_t aNumberOfChannels,
   1.238 +                                           uint32_t aLength,
   1.239 +                                           float aSampleRate)
   1.240 +  : AudioNode(aContext,
   1.241 +              aIsOffline ? aNumberOfChannels : 2,
   1.242 +              ChannelCountMode::Explicit,
   1.243 +              ChannelInterpretation::Speakers)
   1.244 +  , mFramesToProduce(aLength)
   1.245 +  , mAudioChannel(AudioChannel::Normal)
   1.246 +  , mIsOffline(aIsOffline)
   1.247 +  , mHasFinished(false)
   1.248 +  , mExtraCurrentTime(0)
   1.249 +  , mExtraCurrentTimeSinceLastStartedBlocking(0)
   1.250 +  , mExtraCurrentTimeUpdatedSinceLastStableState(false)
   1.251 +{
   1.252 +  MediaStreamGraph* graph = aIsOffline ?
   1.253 +                            MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
   1.254 +                            MediaStreamGraph::GetInstance();
   1.255 +  AudioNodeEngine* engine = aIsOffline ?
   1.256 +                            new OfflineDestinationNodeEngine(this, aNumberOfChannels,
   1.257 +                                                             aLength, aSampleRate) :
   1.258 +                            static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
   1.259 +
   1.260 +  mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
   1.261 +  mStream->SetAudioChannelType(aChannel);
   1.262 +  mStream->AddMainThreadListener(this);
   1.263 +  mStream->AddAudioOutput(&gWebAudioOutputKey);
   1.264 +
   1.265 +  if (aChannel != AudioChannel::Normal) {
   1.266 +    ErrorResult rv;
   1.267 +    SetMozAudioChannelType(aChannel, rv);
   1.268 +  }
   1.269 +
   1.270 +  if (!aIsOffline && UseAudioChannelService()) {
   1.271 +    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
   1.272 +    if (target) {
   1.273 +      target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
   1.274 +                                     /* useCapture = */ true,
   1.275 +                                     /* wantsUntrusted = */ false);
   1.276 +    }
   1.277 +
   1.278 +    CreateAudioChannelAgent();
   1.279 +  }
   1.280 +}
   1.281 +
   1.282 +size_t
   1.283 +AudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   1.284 +{
   1.285 +  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   1.286 +  // Might be useful in the future:
   1.287 +  // - mAudioChannelAgent
   1.288 +  return amount;
   1.289 +}
   1.290 +
   1.291 +size_t
   1.292 +AudioDestinationNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.293 +{
   1.294 +  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.295 +}
   1.296 +
   1.297 +void
   1.298 +AudioDestinationNode::DestroyMediaStream()
   1.299 +{
   1.300 +  if (mAudioChannelAgent && !Context()->IsOffline()) {
   1.301 +    mAudioChannelAgent->StopPlaying();
   1.302 +    mAudioChannelAgent = nullptr;
   1.303 +
   1.304 +    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
   1.305 +    NS_ENSURE_TRUE_VOID(target);
   1.306 +
   1.307 +    target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
   1.308 +                                      /* useCapture = */ true);
   1.309 +  }
   1.310 +
   1.311 +  if (!mStream)
   1.312 +    return;
   1.313 +
   1.314 +  mStream->RemoveMainThreadListener(this);
   1.315 +  MediaStreamGraph* graph = mStream->Graph();
   1.316 +  if (graph->IsNonRealtime()) {
   1.317 +    MediaStreamGraph::DestroyNonRealtimeInstance(graph);
   1.318 +  }
   1.319 +  AudioNode::DestroyMediaStream();
   1.320 +}
   1.321 +
   1.322 +void
   1.323 +AudioDestinationNode::NotifyMainThreadStateChanged()
   1.324 +{
   1.325 +  if (mStream->IsFinished() && !mHasFinished) {
   1.326 +    mHasFinished = true;
   1.327 +    if (mIsOffline) {
   1.328 +      nsCOMPtr<nsIRunnable> runnable =
   1.329 +        NS_NewRunnableMethod(this, &AudioDestinationNode::FireOfflineCompletionEvent);
   1.330 +      NS_DispatchToCurrentThread(runnable);
   1.331 +    }
   1.332 +  }
   1.333 +}
   1.334 +
   1.335 +void
   1.336 +AudioDestinationNode::FireOfflineCompletionEvent()
   1.337 +{
   1.338 +  AudioNodeStream* stream = static_cast<AudioNodeStream*>(Stream());
   1.339 +  OfflineDestinationNodeEngine* engine =
   1.340 +    static_cast<OfflineDestinationNodeEngine*>(stream->Engine());
   1.341 +  engine->FireOfflineCompletionEvent(this);
   1.342 +}
   1.343 +
   1.344 +uint32_t
   1.345 +AudioDestinationNode::MaxChannelCount() const
   1.346 +{
   1.347 +  return Context()->MaxChannelCount();
   1.348 +}
   1.349 +
   1.350 +void
   1.351 +AudioDestinationNode::SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
   1.352 +{
   1.353 +  if (aChannelCount > MaxChannelCount()) {
   1.354 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.355 +    return;
   1.356 +  }
   1.357 +
   1.358 +  AudioNode::SetChannelCount(aChannelCount, aRv);
   1.359 +}
   1.360 +
   1.361 +void
   1.362 +AudioDestinationNode::Mute()
   1.363 +{
   1.364 +  MOZ_ASSERT(Context() && !Context()->IsOffline());
   1.365 +  SendDoubleParameterToStream(DestinationNodeEngine::VOLUME, 0.0f);
   1.366 +}
   1.367 +
   1.368 +void
   1.369 +AudioDestinationNode::Unmute()
   1.370 +{
   1.371 +  MOZ_ASSERT(Context() && !Context()->IsOffline());
   1.372 +  SendDoubleParameterToStream(DestinationNodeEngine::VOLUME, 1.0f);
   1.373 +}
   1.374 +
   1.375 +void
   1.376 +AudioDestinationNode::OfflineShutdown()
   1.377 +{
   1.378 +  MOZ_ASSERT(Context() && Context()->IsOffline(),
   1.379 +             "Should only be called on a valid OfflineAudioContext");
   1.380 +
   1.381 +  MediaStreamGraph::DestroyNonRealtimeInstance(mStream->Graph());
   1.382 +  mOfflineRenderingRef.Drop(this);
   1.383 +}
   1.384 +
   1.385 +JSObject*
   1.386 +AudioDestinationNode::WrapObject(JSContext* aCx)
   1.387 +{
   1.388 +  return AudioDestinationNodeBinding::Wrap(aCx, this);
   1.389 +}
   1.390 +
   1.391 +void
   1.392 +AudioDestinationNode::StartRendering()
   1.393 +{
   1.394 +  mOfflineRenderingRef.Take(this);
   1.395 +  mStream->Graph()->StartNonRealtimeProcessing(TrackRate(Context()->SampleRate()), mFramesToProduce);
   1.396 +}
   1.397 +
   1.398 +void
   1.399 +AudioDestinationNode::SetCanPlay(bool aCanPlay)
   1.400 +{
   1.401 +  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, aCanPlay);
   1.402 +}
   1.403 +
   1.404 +NS_IMETHODIMP
   1.405 +AudioDestinationNode::HandleEvent(nsIDOMEvent* aEvent)
   1.406 +{
   1.407 +  nsAutoString type;
   1.408 +  aEvent->GetType(type);
   1.409 +
   1.410 +  if (!type.EqualsLiteral("visibilitychange")) {
   1.411 +    return NS_ERROR_FAILURE;
   1.412 +  }
   1.413 +
   1.414 +  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
   1.415 +  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
   1.416 +
   1.417 +  bool isActive = false;
   1.418 +  docshell->GetIsActive(&isActive);
   1.419 +
   1.420 +  mAudioChannelAgent->SetVisibilityState(isActive);
   1.421 +  return NS_OK;
   1.422 +}
   1.423 +
   1.424 +NS_IMETHODIMP
   1.425 +AudioDestinationNode::CanPlayChanged(int32_t aCanPlay)
   1.426 +{
   1.427 +  SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
   1.428 +  return NS_OK;
   1.429 +}
   1.430 +
   1.431 +NS_IMETHODIMP
   1.432 +AudioDestinationNode::WindowVolumeChanged()
   1.433 +{
   1.434 +  MOZ_ASSERT(mAudioChannelAgent);
   1.435 +
   1.436 +  if (!mStream) {
   1.437 +    return NS_OK;
   1.438 +  }
   1.439 +
   1.440 +  float volume;
   1.441 +  nsresult rv = mAudioChannelAgent->GetWindowVolume(&volume);
   1.442 +  NS_ENSURE_SUCCESS(rv, rv);
   1.443 +
   1.444 +  mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
   1.445 +  return NS_OK;
   1.446 +}
   1.447 +
   1.448 +AudioChannel
   1.449 +AudioDestinationNode::MozAudioChannelType() const
   1.450 +{
   1.451 +  return mAudioChannel;
   1.452 +}
   1.453 +
   1.454 +void
   1.455 +AudioDestinationNode::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
   1.456 +{
   1.457 +  if (Context()->IsOffline()) {
   1.458 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1.459 +    return;
   1.460 +  }
   1.461 +
   1.462 +  if (aValue != mAudioChannel &&
   1.463 +      CheckAudioChannelPermissions(aValue)) {
   1.464 +    mAudioChannel = aValue;
   1.465 +
   1.466 +    if (mAudioChannelAgent) {
   1.467 +      CreateAudioChannelAgent();
   1.468 +    }
   1.469 +  }
   1.470 +}
   1.471 +
   1.472 +bool
   1.473 +AudioDestinationNode::CheckAudioChannelPermissions(AudioChannel aValue)
   1.474 +{
   1.475 +  if (!Preferences::GetBool("media.useAudioChannelService")) {
   1.476 +    return true;
   1.477 +  }
   1.478 +
   1.479 +  // Only normal channel doesn't need permission.
   1.480 +  if (aValue == AudioChannel::Normal) {
   1.481 +    return true;
   1.482 +  }
   1.483 +
   1.484 +  // Maybe this audio channel is equal to the default one.
   1.485 +  if (aValue == AudioChannelService::GetDefaultAudioChannel()) {
   1.486 +    return true;
   1.487 +  }
   1.488 +
   1.489 +  nsCOMPtr<nsIPermissionManager> permissionManager =
   1.490 +    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   1.491 +  if (!permissionManager) {
   1.492 +    return false;
   1.493 +  }
   1.494 +
   1.495 +  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
   1.496 +  NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
   1.497 +  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   1.498 +
   1.499 +  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
   1.500 +
   1.501 +  nsCString channel;
   1.502 +  channel.AssignASCII(AudioChannelValues::strings[uint32_t(aValue)].value,
   1.503 +                      AudioChannelValues::strings[uint32_t(aValue)].length);
   1.504 +  permissionManager->TestExactPermissionFromPrincipal(principal,
   1.505 +    nsCString(NS_LITERAL_CSTRING("audio-channel-") + channel).get(),
   1.506 +    &perm);
   1.507 +
   1.508 +  return perm == nsIPermissionManager::ALLOW_ACTION;
   1.509 +}
   1.510 +
   1.511 +void
   1.512 +AudioDestinationNode::CreateAudioChannelAgent()
   1.513 +{
   1.514 +  if (mAudioChannelAgent) {
   1.515 +    mAudioChannelAgent->StopPlaying();
   1.516 +  }
   1.517 +
   1.518 +  mAudioChannelAgent = new AudioChannelAgent();
   1.519 +  mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
   1.520 +                                           static_cast<int32_t>(mAudioChannel),
   1.521 +                                           this);
   1.522 +
   1.523 +  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
   1.524 +  if (docshell) {
   1.525 +    bool isActive = false;
   1.526 +    docshell->GetIsActive(&isActive);
   1.527 +    mAudioChannelAgent->SetVisibilityState(isActive);
   1.528 +  }
   1.529 +
   1.530 +  int32_t state = 0;
   1.531 +  mAudioChannelAgent->StartPlaying(&state);
   1.532 +  SetCanPlay(state == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
   1.533 +}
   1.534 +
   1.535 +void
   1.536 +AudioDestinationNode::NotifyStableState()
   1.537 +{
   1.538 +  mExtraCurrentTimeUpdatedSinceLastStableState = false;
   1.539 +}
   1.540 +
   1.541 +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
   1.542 +
   1.543 +void
   1.544 +AudioDestinationNode::ScheduleStableStateNotification()
   1.545 +{
   1.546 +  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   1.547 +  if (appShell) {
   1.548 +    nsCOMPtr<nsIRunnable> event =
   1.549 +      NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
   1.550 +    appShell->RunInStableState(event);
   1.551 +  }
   1.552 +}
   1.553 +
   1.554 +double
   1.555 +AudioDestinationNode::ExtraCurrentTime()
   1.556 +{
   1.557 +  if (!mStartedBlockingDueToBeingOnlyNode.IsNull() &&
   1.558 +      !mExtraCurrentTimeUpdatedSinceLastStableState) {
   1.559 +    mExtraCurrentTimeUpdatedSinceLastStableState = true;
   1.560 +    mExtraCurrentTimeSinceLastStartedBlocking =
   1.561 +      (TimeStamp::Now() - mStartedBlockingDueToBeingOnlyNode).ToSeconds();
   1.562 +    ScheduleStableStateNotification();
   1.563 +  }
   1.564 +  return mExtraCurrentTime + mExtraCurrentTimeSinceLastStartedBlocking;
   1.565 +}
   1.566 +
   1.567 +void
   1.568 +AudioDestinationNode::SetIsOnlyNodeForContext(bool aIsOnlyNode)
   1.569 +{
   1.570 +  if (!mStartedBlockingDueToBeingOnlyNode.IsNull() == aIsOnlyNode) {
   1.571 +    // Nothing changed.
   1.572 +    return;
   1.573 +  }
   1.574 +
   1.575 +  if (!mStream) {
   1.576 +    // DestroyMediaStream has been called, presumably during CC Unlink().
   1.577 +    return;
   1.578 +  }
   1.579 +
   1.580 +  if (mIsOffline) {
   1.581 +    // Don't block the destination stream for offline AudioContexts, since
   1.582 +    // we expect the zero data produced when there are no other nodes to
   1.583 +    // show up in its result buffer. Also, we would get confused by adding
   1.584 +    // ExtraCurrentTime before StartRendering has even been called.
   1.585 +    return;
   1.586 +  }
   1.587 +
   1.588 +  if (aIsOnlyNode) {
   1.589 +    mStream->ChangeExplicitBlockerCount(1);
   1.590 +    mStartedBlockingDueToBeingOnlyNode = TimeStamp::Now();
   1.591 +    // Don't do an update of mExtraCurrentTimeSinceLastStartedBlocking until the next stable state.
   1.592 +    mExtraCurrentTimeUpdatedSinceLastStableState = true;
   1.593 +    ScheduleStableStateNotification();
   1.594 +  } else {
   1.595 +    // Force update of mExtraCurrentTimeSinceLastStartedBlocking if necessary
   1.596 +    ExtraCurrentTime();
   1.597 +    mExtraCurrentTime += mExtraCurrentTimeSinceLastStartedBlocking;
   1.598 +    mExtraCurrentTimeSinceLastStartedBlocking = 0;
   1.599 +    mStream->ChangeExplicitBlockerCount(-1);
   1.600 +    mStartedBlockingDueToBeingOnlyNode = TimeStamp();
   1.601 +  }
   1.602 +}
   1.603 +
   1.604 +}
   1.605 +
   1.606 +}

mercurial