Bug 1190040 - Part 2: Teach the audio channel service how to not notify audio-playback, and do that when a media element has no audio track; r=padenot
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 05 Aug 2015 09:06:57 -0400
changeset 256589 f7bb505664efc43a89d54aa2defed3fca53b9ee9
parent 256588 08301f579388fc32715f44942ccef9eefe6e6c7e
child 256590 27eab13d3cf2e7eeb174a3eb9798ed6df41f26c9
push id14483
push userryanvm@gmail.com
push dateThu, 06 Aug 2015 19:53:53 +0000
treeherderfx-team@b735f4eea935 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1190040
milestone42.0a1
Bug 1190040 - Part 2: Teach the audio channel service how to not notify audio-playback, and do that when a media element has no audio track; r=padenot
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/nsIAudioChannelAgent.idl
dom/camera/DOMCameraControl.cpp
dom/fmradio/FMRadio.cpp
dom/html/HTMLMediaElement.cpp
dom/media/webaudio/AudioDestinationNode.cpp
dom/plugins/base/nsNPAPIPlugin.cpp
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -44,17 +44,17 @@ AudioChannelAgent::~AudioChannelAgent()
 {
   Shutdown();
 }
 
 void
 AudioChannelAgent::Shutdown()
 {
   if (mIsRegToService) {
-    NotifyStoppedPlaying();
+    NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 }
 
 NS_IMETHODIMP AudioChannelAgent::GetAudioChannelType(int32_t *aAudioChannelType)
 {
   *aAudioChannelType = mAudioChannelType;
   return NS_OK;
 }
@@ -122,46 +122,47 @@ AudioChannelAgent::InitInternal(nsIDOMWi
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume,
+NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(uint32_t aNotifyPlayback,
+                                                      float *aVolume,
                                                       bool* aMuted)
 {
   MOZ_ASSERT(aVolume);
   MOZ_ASSERT(aMuted);
 
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
-  service->RegisterAudioChannelAgent(this,
+  service->RegisterAudioChannelAgent(this, aNotifyPlayback,
     static_cast<AudioChannel>(mAudioChannelType));
 
   service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
 
   mIsRegToService = true;
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying(void)
+NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying(uint32_t aNotifyPlayback)
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       !mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
-  service->UnregisterAudioChannelAgent(this);
+  service->UnregisterAudioChannelAgent(this, aNotifyPlayback);
   mIsRegToService = false;
   return NS_OK;
 }
 
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -228,16 +228,17 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                               uint32_t aNotifyPlayback,
                                                AudioChannel aChannel)
 {
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (!winData) {
     winData = new AudioChannelWindow(windowID);
     mWindows.AppendElement(winData);
   }
@@ -248,27 +249,29 @@ AudioChannelService::RegisterAudioChanne
   ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents;
 
   // The first one, we must inform the BrowserElementAudioChannel.
   if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) {
     NotifyChannelActive(aAgent->WindowID(), aChannel, true);
   }
 
   // If this is the first agent for this window, we must notify the observers.
-  if (winData->mAgents.Length() == 1) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.Length() == 1) {
     nsRefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), true /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
 void
-AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
+AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                                 uint32_t aNotifyPlayback)
 {
   AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
   if (!winData) {
     return;
   }
 
   if (winData->mAgents.Contains(aAgent)) {
     int32_t channel = aAgent->AudioChannelType();
@@ -290,17 +293,18 @@ AudioChannelService::UnregisterAudioChan
 #ifdef MOZ_WIDGET_GONK
   bool active = AnyAudioChannelIsActive();
   for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
     mSpeakerManager[i]->SetAudioChannelActive(active);
   }
 #endif
 
   // If this is the last agent for this window, we must notify the observers.
-  if (winData->mAgents.IsEmpty()) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.IsEmpty()) {
     nsRefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -44,23 +44,26 @@ public:
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
   static bool IsAudioChannelMutedByDefault();
 
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
-  void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel);
+  void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                 uint32_t aNotifyPlayback,
+                                 AudioChannel aChannel);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
-  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
+  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                   uint32_t aNotifyPlayback);
 
   /**
    * Return the state to indicate this audioChannel for his window should keep
    * playing/muted.
    */
   void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel,
                 float* aVolume, bool* aMuted);
 
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -29,17 +29,17 @@ interface nsIAudioChannelAgentCallback :
  *   3. Notifying the agent when they start/stop using this channel.
  *   4. Notifying the agent of changes to the visibility of the component using
  *      this channel.
  *
  * The agent will invoke a callback to notify Gecko components of
  *   1. Changes to the playable status of this channel.
  */
 
-[uuid(ee39a34b-a5c7-4b30-b1ac-cd64ceedef67)]
+[uuid(62e0037c-9786-4b79-b986-27111f6e553b)]
 interface nsIAudioChannelAgent : nsISupports
 {
   const long AUDIO_AGENT_CHANNEL_NORMAL             = 0;
   const long AUDIO_AGENT_CHANNEL_CONTENT            = 1;
   const long AUDIO_AGENT_CHANNEL_NOTIFICATION       = 2;
   const long AUDIO_AGENT_CHANNEL_ALARM              = 3;
   const long AUDIO_AGENT_CHANNEL_TELEPHONY          = 4;
   const long AUDIO_AGENT_CHANNEL_RINGER             = 5;
@@ -47,16 +47,19 @@ interface nsIAudioChannelAgent : nsISupp
   const long AUDIO_AGENT_CHANNEL_SYSTEM             = 7;
 
   const long AUDIO_AGENT_CHANNEL_ERROR              = 1000;
 
   const long AUDIO_AGENT_STATE_NORMAL               = 0;
   const long AUDIO_AGENT_STATE_MUTED                = 1;
   const long AUDIO_AGENT_STATE_FADED                = 2;
 
+  const long AUDIO_AGENT_DONT_NOTIFY                = 0;
+  const long AUDIO_AGENT_NOTIFY                     = 1;
+
   /**
    * Before init() is called, this returns AUDIO_AGENT_CHANNEL_ERROR.
    */
   readonly attribute long audioChannelType;
 
   %{C++
   inline int32_t AudioChannelType() {
     int32_t channel;
@@ -93,29 +96,36 @@ interface nsIAudioChannelAgent : nsISupp
   void initWithWeakCallback(in nsIDOMWindow window, in long channelType,
                             in nsIAudioChannelAgentCallback callback);
 
   /**
    * Notify the agent that we want to start playing.
    * Note: Gecko component SHOULD call this function first then start to
    *          play audio stream only when return value is true.
    *
+   * @param notifyPlaying
+   *   Whether to send audio-playback notifications, one of AUDIO_CHANNEL_NOTIFY
+   *   or AUDIO_CHANNEL_DONT_NOTIFY.
    *
    * @return
    *    normal state: the agent has registered with audio channel service and
    *          the component should start playback.
    *    muted state: the agent has registered with audio channel service but
    *          the component should not start playback.
    *    faded state: the agent has registered with audio channel service the
    *          component should start playback as well as reducing the volume.
    */
-  void notifyStartedPlaying(out float volume, out bool muted);
+  void notifyStartedPlaying(in unsigned long notifyPlayback, out float volume, out bool muted);
 
   /**
    * Notify the agent we no longer want to play.
    *
+   * @param notifyPlaying
+   *   Whether to send audio-playback notifications, one of AUDIO_CHANNEL_NOTIFY
+   *   or AUDIO_CHANNEL_DONT_NOTIFY.
+   *
    * Note : even if notifyStartedPlaying() returned false, the agent would
    * still be registered with the audio channel service and receive callbacks
    * for status changes. So notifyStoppedPlaying must still eventually be
    * called to unregister the agent with the channel service.
    */
-  void notifyStoppedPlaying();
+  void notifyStoppedPlaying(in unsigned long notifyPlayback);
 };
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1096,17 +1096,17 @@ nsDOMCameraControl::Shutdown()
   }
 }
 
 void
 nsDOMCameraControl::ReleaseAudioChannelAgent()
 {
 #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY);
     mAudioChannelAgent = nullptr;
   }
 #endif
 }
 
 nsresult
 nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
 {
@@ -1132,17 +1132,18 @@ nsDOMCameraControl::NotifyRecordingStatu
       return NS_ERROR_UNEXPECTED;
     }
 
     // Camera app will stop recording when it falls to the background, so no callback is necessary.
     mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
     // Video recording doesn't output any sound, so it's not necessary to check canPlay.
     float volume = 0.0;
     bool muted = true;
-    rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY,
+                                                  &volume, &muted);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 #endif
   return rv;
 }
 
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -190,17 +190,17 @@ FMRadio::Notify(const FMRadioEventType& 
     case FrequencyChanged:
       DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
       break;
     case EnabledChanged:
       if (Enabled()) {
         DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
       } else {
         if (mAudioChannelAgentEnabled) {
-          mAudioChannelAgent->NotifyStoppedPlaying();
+          mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
           mAudioChannelAgentEnabled = false;
         }
 
         DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
       }
       break;
     case RDSEnabledChanged:
       if (RdsEnabled()) {
@@ -452,17 +452,18 @@ FMRadio::DisableRDS()
 
 void
 FMRadio::EnableAudioChannelAgent()
 {
   NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
 
   float volume = 0.0;
   bool muted = true;
-  mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                           &volume, &muted);
   WindowVolumeChanged(volume, muted);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4504,37 +4504,36 @@ void HTMLMediaElement::UpdateAudioChanne
 
     NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
   }
 }
 
 void
 HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying)
 {
-  // Don't do anything if this element doesn't have any audio tracks.
-  if (!HasAudio()) {
-    return;
-  }
-
   // Immediately check if this should go to the MSG instead of the normal
   // media playback route.
   WindowAudioCaptureChanged();
 
   // This is needed to pass nsContentUtils::IsCallerChrome().
   // AudioChannel API should not called from content but it can happen that
   // this method has some content JS in its stack.
   AutoNoJSAPI nojsapi;
 
+  // Don't notify playback if this element doesn't have any audio tracks.
+  uint32_t notify = HasAudio() ? nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY :
+                                 nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY;
+
   if (aPlaying) {
     float volume = 0.0;
     bool muted = true;
-    mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    mAudioChannelAgent->NotifyStartedPlaying(notify, &volume, &muted);
     WindowVolumeChanged(volume, muted);
   } else {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(notify);
     mAudioChannelAgent = nullptr;
   }
 }
 
 NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -366,17 +366,17 @@ AudioDestinationNode::SizeOfIncludingThi
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
 void
 AudioDestinationNode::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     mAudioChannelAgent = nullptr;
   }
 }
 
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   DestroyAudioChannelAgent();
@@ -600,17 +600,17 @@ AudioDestinationNode::CheckAudioChannelP
 void
 AudioDestinationNode::CreateAudioChannelAgent()
 {
   if (mIsOffline || !UseAudioChannelService()) {
     return;
   }
 
   if (mAudioChannelAgent) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
 
   // The AudioChannelAgent must start playing immediately in order to avoid
@@ -693,23 +693,24 @@ AudioDestinationNode::InputMuted(bool aM
 {
   MOZ_ASSERT(Context() && !Context()->IsOffline());
 
   if (!mAudioChannelAgent) {
     return;
   }
 
   if (aMuted) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     return;
   }
 
   float volume = 0.0;
   bool muted = true;
-  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                                         &volume, &muted);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   WindowAudioCaptureChanged();
   WindowVolumeChanged(volume, muted);
 }
 
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2418,24 +2418,25 @@ NPError
       nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return NPERR_NO_ERROR;
       }
 
       MOZ_ASSERT(agent);
 
       if (isMuted) {
-        rv = agent->NotifyStoppedPlaying();
+        rv = agent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
       } else {
         float volume = 0.0;
         bool muted = true;
-        rv = agent->NotifyStartedPlaying(&volume, &muted);
+        rv = agent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                         &volume, &muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
 
         rv = inst->WindowVolumeChanged(volume, muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }