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 156896 9a6c8fab8072bb10108f9f51ff1886231067d8f9
parent 156895 d965e4f200f493ce8bf5d1bdc3db59b1b93d964a
child 156897 df2dfd91f397f85d300f69a0967ed63306359b6d
push id373
push userryanvm@gmail.com
push dateTue, 17 Dec 2013 17:35:26 +0000
reviewersmchen, koi
bugs947856
milestone26.0
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
@@ -120,27 +120,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();
   }
 }
 
@@ -223,28 +227,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;
 }