Bug 823273 - Part 1: Music volume should be faded when notification sound is on going - AudioChannel changes. r=baku
authorMarco Chen <mchen@mozilla.com>
Mon, 02 Sep 2013 17:45:44 +0800
changeset 160197 fd8ca2b2cbdb91f3af8f77145076aaee04a86d4a
parent 160081 e63b6d17fbfd966d2ad77c2d4cd8dbaf1efbf2e8
child 160198 7b4046fcaf530b4ad96a042e5e51ad2818ab093d
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs823273
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 823273 - Part 1: Music volume should be faded when notification sound is on going - AudioChannel changes. r=baku
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelCommon.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/AudioChannelServiceChild.cpp
dom/audiochannel/AudioChannelServiceChild.h
dom/audiochannel/nsIAudioChannelAgent.idl
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -90,27 +90,27 @@ AudioChannelAgent::InitInternal(int32_t 
   } else {
     mCallback = aCallback;
   }
 
   return NS_OK;
 }
 
 /* boolean startPlaying (); */
-NS_IMETHODIMP AudioChannelAgent::StartPlaying(bool *_retval)
+NS_IMETHODIMP AudioChannelAgent::StartPlaying(int32_t *_retval)
 {
   AudioChannelService *service = AudioChannelService::GetAudioChannelService();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this,
     static_cast<AudioChannelType>(mAudioChannelType));
-  *_retval = !service->GetMuted(this, !mVisible);
+  *_retval = service->GetState(this, !mVisible);
   mIsRegToService = true;
   return NS_OK;
 }
 
 /* void stopPlaying (); */
 NS_IMETHODIMP AudioChannelAgent::StopPlaying(void)
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
@@ -129,27 +129,27 @@ NS_IMETHODIMP AudioChannelAgent::SetVisi
 {
   bool oldVisibility = mVisible;
 
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
 
   mVisible = visible;
   if (mIsRegToService && oldVisibility != mVisible && callback) {
     AudioChannelService *service = AudioChannelService::GetAudioChannelService();
-    callback->CanPlayChanged(!service->GetMuted(this, !mVisible));
+    callback->CanPlayChanged(service->GetState(this, !mVisible));
   }
   return NS_OK;
 }
 
 void AudioChannelAgent::NotifyAudioChannelStateChanged()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
   if (callback) {
     AudioChannelService *service = AudioChannelService::GetAudioChannelService();
-    callback->CanPlayChanged(!service->GetMuted(this, !mVisible));
+    callback->CanPlayChanged(service->GetState(this, !mVisible));
   }
 }
 
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
   if (!callback) {
--- a/dom/audiochannel/AudioChannelCommon.h
+++ b/dom/audiochannel/AudioChannelCommon.h
@@ -19,13 +19,19 @@ enum AudioChannelType {
   AUDIO_CHANNEL_NOTIFICATION,
   AUDIO_CHANNEL_ALARM,
   AUDIO_CHANNEL_TELEPHONY,
   AUDIO_CHANNEL_RINGER,
   AUDIO_CHANNEL_PUBLICNOTIFICATION,
   AUDIO_CHANNEL_LAST
 };
 
+enum AudioChannelState {
+  AUDIO_CHANNEL_STATE_NORMAL = 0,
+  AUDIO_CHANNEL_STATE_MUTED,
+  AUDIO_CHANNEL_STATE_FADED,
+  AUDIO_CHANNEL_STATE_LAST
+};
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -93,18 +93,18 @@ AudioChannelService::~AudioChannelServic
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                AudioChannelType aType)
 {
   MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
 
   AudioChannelAgentData* data = new AudioChannelAgentData(aType,
-                                                          true /* mElementHidden */,
-                                                          true /* mMuted */);
+                                true /* aElementHidden */,
+                                AUDIO_CHANNEL_STATE_MUTED /* aState */);
   mAgents.Put(aAgent, data);
   RegisterType(aType, CONTENT_PROCESS_ID_MAIN);
 }
 
 void
 AudioChannelService::RegisterType(AudioChannelType aType, uint64_t aChildID)
 {
   AudioChannelInternalType type = GetInternalType(aType, true);
@@ -198,37 +198,35 @@ AudioChannelService::UpdateChannelType(A
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID));
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 }
 
-bool
-AudioChannelService::GetMuted(AudioChannelAgent* aAgent, bool aElementHidden)
+AudioChannelState
+AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
 {
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
-    return true;
+    return AUDIO_CHANNEL_STATE_MUTED;
   }
 
   bool oldElementHidden = data->mElementHidden;
   // Update visibility.
   data->mElementHidden = aElementHidden;
 
-  bool muted = GetMutedInternal(data->mType, CONTENT_PROCESS_ID_MAIN,
+  data->mState = GetStateInternal(data->mType, CONTENT_PROCESS_ID_MAIN,
                                 aElementHidden, oldElementHidden);
-  data->mMuted = muted;
-
-  return muted;
+  return data->mState;
 }
 
-bool
-AudioChannelService::GetMutedInternal(AudioChannelType aType, uint64_t aChildID,
+AudioChannelState
+AudioChannelService::GetStateInternal(AudioChannelType aType, uint64_t aChildID,
                                       bool aElementHidden, bool aElementWasHidden)
 {
   UpdateChannelType(aType, aChildID, aElementHidden, aElementWasHidden);
 
   // Calculating the new and old type and update the hashtable if needed.
   AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
@@ -264,34 +262,74 @@ AudioChannelService::GetMutedInternal(Au
   if (newType != oldType && aType == AUDIO_CHANNEL_CONTENT) {
     Notify();
   }
 
   SendAudioChannelChangedNotification(aChildID);
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
-    return false;
+    if (CheckVolumeFadedCondition(newType, aElementHidden)) {
+      return AUDIO_CHANNEL_STATE_FADED;
+    }
+    return AUDIO_CHANNEL_STATE_NORMAL;
   }
 
-  bool muted = false;
-
   // We are not visible, maybe we have to mute.
   if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN ||
       (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
        !mActiveContentChildIDs.Contains(aChildID))) {
-    muted = true;
+    return AUDIO_CHANNEL_STATE_MUTED;
+  }
+
+  // After checking the condition on normal & content channel, if the state
+  // is not on muted then checking other higher channels type here.
+  if (ChannelsActiveWithHigherPriorityThan(newType)) {
+    MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
+    if (CheckVolumeFadedCondition(newType, aElementHidden)) {
+      return AUDIO_CHANNEL_STATE_FADED;
+    }
+    return AUDIO_CHANNEL_STATE_MUTED;
   }
 
-  if (!muted) {
-    MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
-    muted = ChannelsActiveWithHigherPriorityThan(newType);
+  return AUDIO_CHANNEL_STATE_NORMAL;
+}
+
+bool
+AudioChannelService::CheckVolumeFadedCondition(AudioChannelInternalType aType,
+                                               bool aElementHidden)
+{
+  // Only normal & content channels are considered
+  if (aType > AUDIO_CHANNEL_INT_CONTENT_HIDDEN) {
+    return false;
   }
 
-  return muted;
+  // Consider that audio from notification is with short duration
+  // so just fade the volume not pause it
+  if (mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty() &&
+      mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
+    return false;
+  }
+
+  // Since this element is on the foreground, it can be allowed to play always.
+  // So return true directly when there is any notification channel alive.
+  if (aElementHidden == false) {
+   return true;
+  }
+
+  // If element is on the background, it is possible paused by channels higher
+  // then notification.
+  for (int i = AUDIO_CHANNEL_INT_LAST - 1;
+    i != AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN; --i) {
+    if (!mChannelCounters[i].IsEmpty()) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 bool
 AudioChannelService::ContentOrNormalChannelIsActive()
 {
   return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
          !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() ||
          !mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty();
@@ -471,17 +509,18 @@ NS_IMETHODIMP
 AudioChannelService::Notify(nsITimer* aTimer)
 {
   UnregisterTypeInternal(AUDIO_CHANNEL_TELEPHONY, mTimerElementHidden, mTimerChildID);
   mDeferTelChannelTimer = nullptr;
   return NS_OK;
 }
 
 bool
-AudioChannelService::ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType)
+AudioChannelService::ChannelsActiveWithHigherPriorityThan(
+  AudioChannelInternalType aType)
 {
   for (int i = AUDIO_CHANNEL_INT_LAST - 1;
        i != AUDIO_CHANNEL_INT_CONTENT_HIDDEN; --i) {
     if (i == aType) {
       return false;
     }
 
     if (!mChannelCounters[i].IsEmpty()) {
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -24,21 +24,22 @@ class AudioChannelService
 , public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
 
   /**
-   * Returns the AudioChannelServce singleton. Only to be called from main thread.
+   * Returns the AudioChannelServce singleton. Only to be called from main
+   * thread.
+   *
    * @return NS_OK on proper assignment, NS_ERROR_FAILURE otherwise.
    */
-  static AudioChannelService*
-  GetAudioChannelService();
+  static AudioChannelService* GetAudioChannelService();
 
   /**
    * Shutdown the singleton.
    */
   static void Shutdown();
 
   /**
    * Any audio channel agent that starts playing should register itself to
@@ -49,29 +50,31 @@ public:
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
-   * Return true if this type should be muted.
+   * Return the state to indicate this agent should keep playing/
+   * fading volume/muted.
    */
-  virtual bool GetMuted(AudioChannelAgent* aAgent, bool aElementHidden);
+  virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
+                                     bool aElementHidden);
 
   /**
    * Return true if there is a content channel active in this process
    * or one of its subprocesses.
    */
   virtual bool ContentOrNormalChannelIsActive();
 
   /**
-   * Return true iff a normal or content channel is active for the given process
-   * ID.
+   * Return true if a normal or content channel is active for the given
+   * process ID.
    */
   virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
 
   /***
    * AudioChannelManager calls this function to notify the default channel used
    * to adjust volume when there is no any active channel.
    */
   virtual void SetDefaultVolumeControlChannel(AudioChannelType aType,
@@ -88,18 +91,19 @@ protected:
 
   /* Register/Unregister IPC types: */
   void RegisterType(AudioChannelType aType, uint64_t aChildID);
   void UnregisterType(AudioChannelType aType, bool aElementHidden,
                       uint64_t aChildID);
   void UnregisterTypeInternal(AudioChannelType aType, bool aElementHidden,
                               uint64_t aChildID);
 
-  bool GetMutedInternal(AudioChannelType aType, uint64_t aChildID,
-                        bool aElementHidden, bool aElementWasHidden);
+  AudioChannelState GetStateInternal(AudioChannelType aType, uint64_t aChildID,
+                                     bool aElementHidden,
+                                     bool aElementWasHidden);
 
   /* Update the internal type value following the visibility changes */
   void UpdateChannelType(AudioChannelType aType, uint64_t aChildID,
                          bool aElementHidden, bool aElementWasHidden);
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(AudioChannelType aType,
                                               bool aHidden, uint64_t aChildID);
@@ -122,34 +126,37 @@ protected:
     AUDIO_CHANNEL_INT_RINGER_HIDDEN,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN,
     AUDIO_CHANNEL_INT_LAST
   };
 
   bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
 
+  bool CheckVolumeFadedCondition(AudioChannelInternalType aType,
+                                 bool aElementHidden);
+
   const char* ChannelName(AudioChannelType aType);
 
   AudioChannelInternalType GetInternalType(AudioChannelType aType,
                                            bool aElementHidden);
 
   class AudioChannelAgentData {
   public:
     AudioChannelAgentData(AudioChannelType aType,
                           bool aElementHidden,
-                          bool aMuted)
+                          AudioChannelState aState)
     : mType(aType)
     , mElementHidden(aElementHidden)
-    , mMuted(aMuted)
+    , mState(aState)
     {}
 
     AudioChannelType mType;
     bool mElementHidden;
-    bool mMuted;
+    AudioChannelState mState;
   };
 
   static PLDHashOperator
   NotifyEnumerator(AudioChannelAgent* aAgent,
                    AudioChannelAgentData* aData, void *aUnused);
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 
--- a/dom/audiochannel/AudioChannelServiceChild.cpp
+++ b/dom/audiochannel/AudioChannelServiceChild.cpp
@@ -53,43 +53,43 @@ AudioChannelServiceChild::Shutdown()
 AudioChannelServiceChild::AudioChannelServiceChild()
 {
 }
 
 AudioChannelServiceChild::~AudioChannelServiceChild()
 {
 }
 
-bool
-AudioChannelServiceChild::GetMuted(AudioChannelAgent* aAgent, bool aElementHidden)
+AudioChannelState
+AudioChannelServiceChild::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
 {
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
-    return true;
+    return AUDIO_CHANNEL_STATE_MUTED;
   }
 
   ContentChild *cc = ContentChild::GetSingleton();
-  bool muted = true;
+  AudioChannelState state = AUDIO_CHANNEL_STATE_MUTED;
   bool oldElementHidden = data->mElementHidden;
 
   UpdateChannelType(data->mType, CONTENT_PROCESS_ID_MAIN, aElementHidden, oldElementHidden);
 
   // Update visibility.
   data->mElementHidden = aElementHidden;
 
   if (cc) {
-    cc->SendAudioChannelGetMuted(data->mType, aElementHidden, oldElementHidden, &muted);
+    cc->SendAudioChannelGetState(data->mType, aElementHidden, oldElementHidden, &state);
   }
-  data->mMuted = muted;
+  data->mState = state;
 
   if (cc) {
     cc->SendAudioChannelChangedNotification();
   }
 
-  return muted;
+  return state;
 }
 
 void
 AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                     AudioChannelType aType)
 {
   MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
 
--- a/dom/audiochannel/AudioChannelServiceChild.h
+++ b/dom/audiochannel/AudioChannelServiceChild.h
@@ -16,32 +16,35 @@
 namespace mozilla {
 namespace dom {
 
 class AudioChannelServiceChild : public AudioChannelService
 {
 public:
 
   /**
-   * Returns the AudioChannelServce singleton. Only to be called from main thread.
+   * Returns the AudioChannelServce singleton. Only to be called from main
+   * thread.
+   *
    * @return NS_OK on proper assignment, NS_ERROR_FAILURE otherwise.
    */
-  static AudioChannelService*
-  GetAudioChannelService();
+  static AudioChannelService* GetAudioChannelService();
 
   static void Shutdown();
 
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                          AudioChannelType aType);
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
-   * Return true if this type + this mozHidden should be muted.
+   * Return the state to indicate this agent should keep playing/
+   * fading volume/muted.
    */
-  virtual bool GetMuted(AudioChannelAgent* aAgent, bool aMozHidden);
+  virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
+                                     bool aElementHidden);
 
   virtual void SetDefaultVolumeControlChannel(AudioChannelType aType, bool aHidden);
 
 protected:
   AudioChannelServiceChild();
   virtual ~AudioChannelServiceChild();
 };
 
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -7,48 +7,53 @@
 [function, scriptable, uuid(c7227506-5f8e-11e2-8bb3-10bf48d64bd4)]
 interface nsIAudioChannelAgentCallback : nsISupports
 {
   /**
    * Notified when the playable status of channel is changed.
    *
    * @param canPlay
    *        Callback from agent to notify component of the playable status
-   *        of the channel. If canPlay is false, component SHOULD stop playing
-   *        media associated with this channel as soon as possible.
+   *        of the channel. If canPlay is muted state, component SHOULD stop
+   *        playing media associated with this channel as soon as possible. if
+   *        it is faded state then the volume of media should be reduced.
    */
-  void canPlayChanged(in boolean canPlay);
+  void canPlayChanged(in long canPlay);
 };
 
 /**
  * This interface provides an agent for gecko components to participate
  * in the audio channel service. Gecko components are responsible for
  *   1. Indicating what channel type they are using (via the init() member function).
  *   2. Before playing, checking the playable status of the channel.
  *   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.
  */
 
-[scriptable, uuid(f012a9b7-6431-4915-a4ac-4ba7d833e28e)]
+[scriptable, uuid(7a4c0b06-63a4-11e2-8c1b-10bf48d64bd4)]
 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;
   const long AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION = 6;
 
   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;
+
   /**
    * Before init() is called, this returns AUDIO_AGENT_CHANNEL_ERROR.
    */
   readonly attribute long audioChannelType;
 
   /**
    * Initialize the agent with a channel type.
    * Note: This function should only be called once.
@@ -75,22 +80,24 @@ interface nsIAudioChannelAgent : nsISupp
 
   /**
    * 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.
    *
    *
    * @return
-   *    true: the agent has registered with audio channel service and the
-   *          component should start playback.
-   *    false: the agent has registered with audio channel service but the
-   *          component should not start playback.
+   *    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.
    */
-  boolean startPlaying();
+  long startPlaying();
 
   /**
    * Notify the agent we no longer want to play.
    *
    * Note : even if startPlaying() returned false, the agent would still be
    *        registered with the audio channel service and receive callbacks for status changes.
    *        So stopPlaying must still eventually be called to unregister the agent with the
    *        channel service.