Bug 823273 - Part 3: Music volume should be faded when notification sound is on going - Media element. a=kinetik
authorMarco Chen <mchen@mozilla.com>
Mon, 02 Sep 2013 17:45:44 +0800
changeset 160199 583edf914fd7339c10c1e3d94eac2b4ecd3e745b
parent 160198 7b4046fcaf530b4ad96a042e5e51ad2818ab093d
child 160200 39408bd9b8132f1681971c8a7421c935de51349e
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)
reviewerskinetik
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 3: Music volume should be faded when notification sound is on going - Media element. a=kinetik
content/html/content/public/HTMLMediaElement.h
content/html/content/src/HTMLAudioElement.cpp
content/html/content/src/HTMLMediaElement.cpp
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -807,17 +807,17 @@ protected:
   void ProcessMediaFragmentURI();
 
   /**
    * Mute or unmute the audio and change the value that the |muted| map.
    */
   void SetMutedInternal(uint32_t aMuted);
   /**
    * Update the volume of the output audio stream to match the element's
-   * current mMuted/mVolume state.
+   * current mMuted/mVolume/mAudioChannelFaded state.
    */
   void SetVolumeInternal();
 
   /**
    * Suspend (if aPauseForInactiveDocument) or resume element playback and
    * resource download.  If aSuspendEvents is true, event delivery is
    * suspended (and events queued) until the element is resumed.
    */
@@ -836,18 +836,18 @@ protected:
     bool isPaused = false;
     GetPaused(&isPaused);
     return isPaused;
   }
 
   // Check the permissions for audiochannel.
   bool CheckAudioChannelPermissions(const nsAString& aType);
 
-  // This method does the check for muting/unmuting the audio channel.
-  nsresult UpdateChannelMuteState(bool aCanPlay);
+  // This method does the check for muting/fading/unmuting the audio channel.
+  nsresult UpdateChannelMuteState(mozilla::dom::AudioChannelState aCanPlay);
 
   // Update the audio channel playing state
   virtual void UpdateAudioChannelPlayingState();
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
   nsRefPtr<MediaDecoder> mDecoder;
 
@@ -1122,16 +1122,19 @@ protected:
   bool mHasAudio;
 
   // True if the media's channel's download has been suspended.
   bool mDownloadSuspendedByCache;
 
   // Audio Channel Type.
   AudioChannelType mAudioChannelType;
 
+  // The audio channel has been faded.
+  bool mAudioChannelFaded;
+
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
 
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
   // List of our attached text track objects.
   nsRefPtr<TextTrackList> mTextTracks;
--- a/content/html/content/src/HTMLAudioElement.cpp
+++ b/content/html/content/src/HTMLAudioElement.cpp
@@ -227,26 +227,26 @@ nsresult HTMLAudioElement::SetAcceptHead
 JSObject*
 HTMLAudioElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLAudioElementBinding::Wrap(aCx, aScope, this);
 }
 
 /* void canPlayChanged (in boolean canPlay); */
 NS_IMETHODIMP
-HTMLAudioElement::CanPlayChanged(bool canPlay)
+HTMLAudioElement::CanPlayChanged(int32_t canPlay)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
   // Only Audio_Data API will initialize the mAudioStream, so we call the parent
   // one when this audio tag is not used by Audio_Data API.
   if (!mAudioStream) {
     return HTMLMediaElement::CanPlayChanged(canPlay);
   }
 #ifdef MOZ_B2G
-  if (canPlay) {
+  if (canPlay != AUDIO_CHANNEL_STATE_MUTED) {
     SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
   } else {
     SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
   }
 
 #endif
   return NS_OK;
 }
@@ -281,17 +281,17 @@ HTMLAudioElement::UpdateAudioChannelPlay
       }
       // Use a weak ref so the audio channel agent can't leak |this|.
       mAudioChannelAgent->InitWithWeakCallback(mAudioChannelType, this);
 
       mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
     }
 
     if (mPlayingThroughTheAudioChannel) {
-      bool canPlay;
+      int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
       CanPlayChanged(canPlay);
     } else {
       mAudioChannelAgent->StopPlaying();
       mAudioChannelAgent = nullptr;
     }
   }
 #endif
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -102,16 +102,19 @@ using namespace mozilla::layers;
 using mozilla::net::nsMediaFragmentURIParser;
 
 namespace mozilla {
 namespace dom {
 
 // Number of milliseconds between timeupdate events as defined by spec
 #define TIMEUPDATE_MS 250
 
+// Used by AudioChannel for suppresssing the volume to this ratio.
+#define FADED_VOLUME_RATIO 0.25
+
 // These constants are arbitrary
 // Minimum playbackRate for a media
 static const double MIN_PLAYBACKRATE = 0.25;
 // Maximum playbackRate for a media
 static const double MAX_PLAYBACKRATE = 5.0;
 // These are the limits beyonds which SoundTouch does not perform too well and when
 // speech is hard to understand anyway.
 // Threshold above which audio is muted
@@ -1694,17 +1697,18 @@ void HTMLMediaElement::SetMutedInternal(
     return;
   }
 
   SetVolumeInternal();
 }
 
 void HTMLMediaElement::SetVolumeInternal()
 {
-  float effectiveVolume = mMuted ? 0.0f : float(mVolume);
+  float effectiveVolume = mMuted ? 0.0f :
+    mAudioChannelFaded ? float(mVolume) * FADED_VOLUME_RATIO : float(mVolume);
 
   if (mDecoder) {
     mDecoder->SetVolume(effectiveVolume);
   } else if (mAudioStream) {
     mAudioStream->SetVolume(effectiveVolume);
   } else if (mSrcStream) {
     GetSrcMediaStream()->SetAudioOutputVolume(this, effectiveVolume);
   }
@@ -1944,16 +1948,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mHasSelfReference(false),
     mShuttingDown(false),
     mSuspendedForPreloadNone(false),
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mHasAudio(false),
     mDownloadSuspendedByCache(false),
     mAudioChannelType(AUDIO_CHANNEL_NORMAL),
+    mAudioChannelFaded(false),
     mPlayingThroughTheAudioChannel(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
   if (!gMediaElementEventsLog) {
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
@@ -3755,27 +3760,34 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
 }
 
 ImageContainer* HTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
 
-nsresult HTMLMediaElement::UpdateChannelMuteState(bool aCanPlay)
+nsresult HTMLMediaElement::UpdateChannelMuteState(AudioChannelState aCanPlay)
 {
   if (!UseAudioChannelService()) {
     return NS_OK;
   }
 
+  if ((aCanPlay == AUDIO_CHANNEL_STATE_FADED && !mAudioChannelFaded) ||
+      (aCanPlay != AUDIO_CHANNEL_STATE_FADED && mAudioChannelFaded)) {
+    mAudioChannelFaded = !mAudioChannelFaded;
+    SetVolumeInternal();
+  }
+
   // We have to mute this channel.
-  if (!aCanPlay && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
+  if (aCanPlay == AUDIO_CHANNEL_STATE_MUTED && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
     SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
     DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
-  } else if (aCanPlay && (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
+  } else if (aCanPlay != AUDIO_CHANNEL_STATE_MUTED &&
+             (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
     SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
     DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
   }
 
   SuspendOrResumeElement(mMuted & MUTED_BY_AUDIO_CHANNEL, false);
   return NS_OK;
 }
 
@@ -3800,33 +3812,45 @@ void HTMLMediaElement::UpdateAudioChanne
         return;
       }
       // Use a weak ref so the audio channel agent can't leak |this|.
       mAudioChannelAgent->InitWithWeakCallback(mAudioChannelType, this);
       mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
     }
 
     if (mPlayingThroughTheAudioChannel) {
-      bool canPlay;
+      int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
       CanPlayChanged(canPlay);
     } else {
       mAudioChannelAgent->StopPlaying();
       mAudioChannelAgent = nullptr;
     }
   }
 }
 
 /* void canPlayChanged (in boolean canPlay); */
-NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(bool canPlay)
+NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(int32_t canPlay)
 {
+  static_assert(static_cast<AudioChannelState>(
+                nsIAudioChannelAgent::AUDIO_AGENT_STATE_NORMAL) ==
+                AUDIO_CHANNEL_STATE_NORMAL &&
+                static_cast<AudioChannelState>(
+                nsIAudioChannelAgent::AUDIO_AGENT_STATE_MUTED) ==
+                AUDIO_CHANNEL_STATE_MUTED &&
+                static_cast<AudioChannelState>(
+                nsIAudioChannelAgent::AUDIO_AGENT_STATE_FADED) ==
+                AUDIO_CHANNEL_STATE_FADED,
+                "Enum of channel state on nsIAudioChannelAgent.idl should be "
+                "the same with AudioChannelCommon.h");
+
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
-  UpdateChannelMuteState(canPlay);
-  mPaused.SetCanPlay(canPlay);
+  UpdateChannelMuteState(static_cast<AudioChannelState>(canPlay));
+  mPaused.SetCanPlay(canPlay != AUDIO_CHANNEL_STATE_MUTED);
   return NS_OK;
 }
 
 /* readonly attribute TextTrackList textTracks; */
 TextTrackList*
 HTMLMediaElement::TextTracks() const
 {
   return mTextTracks;