Bug 947856 - Mute the background content audio channel when playing any foreground normal video channel. r=mchen, a=koi+
authorBruce Sun <brsun@mozilla.com>
Mon, 16 Dec 2013 13:08:03 +0800
changeset 175330 17141bcf340a1d282f842d4c42b52de23c2812fc
parent 175329 cf74f3212bc8ed1bc8b797d6a43539b935efced5
child 175331 dfb969af5d4469033c753ffb7f01f11d2353a2d9
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchen, koi
bugs947856
milestone28.0a2
Bug 947856 - Mute the background content audio channel when playing any foreground normal video channel. r=mchen, a=koi+
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/tests/TestAudioChannelService.cpp
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -132,27 +132,31 @@ AudioChannelService::RegisterType(AudioC
       mDeferTelChannelTimer = nullptr;
       UnregisterTypeInternal(aType, mTimerElementHidden, mTimerChildID, false);
     }
 
     if (aWithVideo) {
       mWithVideoChildIDs.AppendElement(aChildID);
     }
 
+    // No hidden content channel can be playable if there is a content channel
+    // in foreground (bug 855208), nor if there is a normal channel with video
+    // in foreground (bug 894249).
+    if (type == AUDIO_CHANNEL_INT_CONTENT ||
+        (type == AUDIO_CHANNEL_INT_NORMAL &&
+         mWithVideoChildIDs.Contains(aChildID))) {
+      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
+    }
     // One hidden content channel can be playable only when there is no any
-    // content channel in the foreground.
-    if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+    // content channel in the foreground, and no normal channel with video in
+    // foreground.
+    else if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
         mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
       mPlayableHiddenContentChildID = aChildID;
     }
-    // No hidden content channel can be playable if there is an content channel
-    // in foreground.
-    else if (type == AUDIO_CHANNEL_INT_CONTENT) {
-      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-    }
 
     // In order to avoid race conditions, it's safer to notify any existing
     // agent any time a new one is registered.
     SendAudioChannelChangedNotification(aChildID);
     Notify();
   }
 }
 
@@ -249,28 +253,32 @@ AudioChannelService::UpdateChannelType(A
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID));
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 
-  // The last content channel which goes from foreground to background can also
-  // be playable.
-  if (oldType == AUDIO_CHANNEL_INT_CONTENT &&
+  // No hidden content channel can be playable if there is a content channel
+  // in foreground (bug 855208), nor if there is a normal channel with video
+  // in foreground (bug 894249).
+  if (newType == AUDIO_CHANNEL_INT_CONTENT ||
+      (newType == AUDIO_CHANNEL_INT_NORMAL &&
+       mWithVideoChildIDs.Contains(aChildID))) {
+    mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
+  }
+  // If there is no content channel in foreground and no normal channel with
+  // video in foreground, the last content channel which goes from foreground
+  // to background can be playable.
+  else if (oldType == AUDIO_CHANNEL_INT_CONTENT &&
       newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
       mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
     mPlayableHiddenContentChildID = aChildID;
   }
-  // No hidden content channel can be playable if there is an content channel
-  // in foreground.
-  else if (newType == AUDIO_CHANNEL_INT_CONTENT) {
-    mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
-  }
 }
 
 AudioChannelState
 AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
 {
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
     return AUDIO_CHANNEL_STATE_MUTED;
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -42,19 +42,25 @@ public:
 
   virtual ~Agent()
   {
     if (mRegistered) {
       StopPlaying();
     }
   }
 
-  nsresult Init()
+  nsresult Init(bool video=false)
   {
-    nsresult rv = mAgent->InitWithWeakCallback(mType, this);
+    nsresult rv = NS_OK;
+    if (video) {
+      rv = mAgent->InitWithVideo(mType, this, true);
+    }
+    else {
+      rv = mAgent->InitWithWeakCallback(mType, this);
+    }
     NS_ENSURE_SUCCESS(rv, rv);
 
     return mAgent->SetVisibilityState(false);
   }
 
   nsresult StartPlaying(AudioChannelState *_ret)
   {
     if (mRegistered) {
@@ -525,16 +531,97 @@ TestPriorities()
   rv = pNotificationAgent->GetCanPlay(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test5: A pNotification channel visible agent must be playable");
 
   return rv;
 }
 
+nsresult
+TestOneVideoNormalChannel()
+{
+  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsresult rv = agent1->Init(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
+  rv = agent2->Init(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AudioChannelState playable;
+  rv = agent1->StartPlaying(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
+    "Test6: A video normal channel invisible agent1 must be muted");
+
+  rv = agent2->StartPlaying(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A content channel invisible agent2 must be playable");
+
+  // one video normal channel in foreground and one content channel in background
+  rv = agent1->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent1->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A video normal channel visible agent1 must be playable");
+
+  rv = agent2->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
+    "Test6: A content channel invisible agent2 must be muted");
+
+  // both one video normal channel and one content channel in foreground
+  rv = agent2->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent1->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A video normal channel visible agent1 must be playable");
+
+  rv = agent2->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A content channel visible agent2 must be playable");
+
+  // one video normal channel in background and one content channel in foreground
+  rv = agent1->SetVisibilityState(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent1->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
+    "Test6: A video normal channel invisible agent1 must be muted");
+
+  rv = agent2->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A content channel visible agent2 must be playable");
+
+  // both one video normal channel and one content channel in background
+  rv = agent2->SetVisibilityState(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent1->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
+    "Test6: A video normal channel invisible agent1 must be muted");
+
+  rv = agent2->GetCanPlay(&playable);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test6: A content channel invisible agent2 must be playable");
+
+  return rv;
+}
+
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("AudioChannelService");
   if (xpcom.failed()) {
     return 1;
   }
 
   if (NS_FAILED(TestDoubleStartPlaying())) {
@@ -552,15 +639,24 @@ int main(int argc, char** argv)
   if (NS_FAILED(TestContentChannels())) {
     return 1;
   }
 
   if (NS_FAILED(TestFadedState())) {
     return 1;
   }
 
+  // Channel type with AUDIO_CHANNEL_TELEPHONY cannot be unregistered until the
+  // main thread has chances to process 1500 millisecond timer. In order to
+  // skip ambiguous return value of ChannelsActiveWithHigherPriorityThan(), new
+  // test cases are added before any test case that registers the channel type
+  // with AUDIO_CHANNEL_TELEPHONY channel.
+  if (NS_FAILED(TestOneVideoNormalChannel())) {
+    return 1;
+  }
+
   if (NS_FAILED(TestPriorities())) {
     return 1;
   }
 
   return 0;
 }