Bug 894249 - Add ability to associate video with a channel so that it can interrupt the background content channel. r=mchen, r=kinetik, r=bent
authorJW Wang <jwwang@mozilla.com>
Wed, 18 Sep 2013 11:46:22 +0800
changeset 161862 bb4af3cda509cd7ae210f6ab290b23186f030ed0
parent 161861 1078a52212babb201ff1441985683c11ce3e1d3b
child 161863 a156e6a7757c801514c1a73c233968ad969e3147
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchen, kinetik, bent
bugs894249
milestone27.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 894249 - Add ability to associate video with a channel so that it can interrupt the background content channel. r=mchen, r=kinetik, r=bent
content/html/content/src/HTMLMediaElement.cpp
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelAgent.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/AudioChannelServiceChild.cpp
dom/audiochannel/AudioChannelServiceChild.h
dom/audiochannel/nsIAudioChannelAgent.idl
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3806,18 +3806,23 @@ void HTMLMediaElement::UpdateAudioChanne
     mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
+      nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
       // Use a weak ref so the audio channel agent can't leak |this|.
-      mAudioChannelAgent->InitWithWeakCallback(mAudioChannelType, this);
+      if (AUDIO_CHANNEL_NORMAL == mAudioChannelType && video) {
+        mAudioChannelAgent->InitWithVideo(mAudioChannelType, this, true);
+      } else {
+        mAudioChannelAgent->InitWithWeakCallback(mAudioChannelType, this);
+      }
       mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
     }
 
     if (mPlayingThroughTheAudioChannel) {
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
       CanPlayChanged(canPlay);
     } else {
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -17,16 +17,17 @@ NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
 
 AudioChannelAgent::AudioChannelAgent()
   : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
   , mIsRegToService(false)
   , mVisible(true)
+  , mWithVideo(false)
 {
 }
 
 AudioChannelAgent::~AudioChannelAgent()
 {
   if (mIsRegToService) {
     StopPlaying();
   }
@@ -49,20 +50,28 @@ NS_IMETHODIMP AudioChannelAgent::Init(in
  *                               in nsIAudioChannelAgentCallback callback); */
 NS_IMETHODIMP
 AudioChannelAgent::InitWithWeakCallback(int32_t channelType,
                                         nsIAudioChannelAgentCallback *callback)
 {
   return InitInternal(channelType, callback, /* useWeakRef = */ true);
 }
 
+NS_IMETHODIMP
+AudioChannelAgent::InitWithVideo(int32_t channelType,
+                                 nsIAudioChannelAgentCallback *callback,
+                                 bool aUseWeakRef)
+{
+  return InitInternal(channelType, callback, aUseWeakRef, true);
+}
+
 nsresult
 AudioChannelAgent::InitInternal(int32_t aChannelType,
                                 nsIAudioChannelAgentCallback *aCallback,
-                                bool aUseWeakRef)
+                                bool aUseWeakRef, bool aWithVideo)
 {
   // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
   // AudioChannelCommon.h the same.
   static_assert(static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_NORMAL) ==
                 AUDIO_CHANNEL_NORMAL &&
                 static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_CONTENT) ==
                 AUDIO_CHANNEL_CONTENT &&
                 static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_NOTIFICATION) ==
@@ -86,30 +95,32 @@ AudioChannelAgent::InitInternal(int32_t 
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
   }
 
+  mWithVideo = aWithVideo;
+
   return NS_OK;
 }
 
 /* boolean startPlaying (); */
 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));
+    static_cast<AudioChannelType>(mAudioChannelType), mWithVideo);
   *_retval = service->GetState(this, !mVisible);
   mIsRegToService = true;
   return NS_OK;
 }
 
 /* void stopPlaying (); */
 NS_IMETHODIMP AudioChannelAgent::StopPlaying(void)
 {
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -36,21 +36,22 @@ private:
   virtual ~AudioChannelAgent();
 
   // Returns mCallback if that's non-null, or otherwise tries to get an
   // nsIAudioChannelAgentCallback out of mWeakCallback.
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
-                        bool aUseWeakRef);
+                        bool aUseWeakRef, bool aWithVideo=false);
 
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
   nsWeakPtr mWeakCallback;
   int32_t mAudioChannelType;
   bool mIsRegToService;
   bool mVisible;
+  bool mWithVideo;
 };
 
 } // namespace dom
 } // namespace mozilla
 #endif
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -88,86 +88,95 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                               AudioChannelType aType)
+                                               AudioChannelType aType,
+                                               bool aWithVideo)
 {
   MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
 
   AudioChannelAgentData* data = new AudioChannelAgentData(aType,
                                 true /* aElementHidden */,
-                                AUDIO_CHANNEL_STATE_MUTED /* aState */);
+                                AUDIO_CHANNEL_STATE_MUTED /* aState */,
+                                aWithVideo);
   mAgents.Put(aAgent, data);
-  RegisterType(aType, CONTENT_PROCESS_ID_MAIN);
+  RegisterType(aType, CONTENT_PROCESS_ID_MAIN, aWithVideo);
 }
 
 void
-AudioChannelService::RegisterType(AudioChannelType aType, uint64_t aChildID)
+AudioChannelService::RegisterType(AudioChannelType aType, uint64_t aChildID, bool aWithVideo)
 {
   AudioChannelInternalType type = GetInternalType(aType, true);
   mChannelCounters[type].AppendElement(aChildID);
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // Since there is another telephony registered, we can unregister old one
     // immediately.
     if (mDeferTelChannelTimer && aType == AUDIO_CHANNEL_TELEPHONY) {
       mDeferTelChannelTimer->Cancel();
       mDeferTelChannelTimer = nullptr;
-      UnregisterTypeInternal(aType, mTimerElementHidden, mTimerChildID);
+      UnregisterTypeInternal(aType, mTimerElementHidden, mTimerChildID, false);
+    }
+
+    if (aWithVideo) {
+      mWithVideoChildIDs.AppendElement(aChildID);
     }
 
     // In order to avoid race conditions, it's safer to notify any existing
     // agent any time a new one is registered.
     SendAudioChannelChangedNotification(aChildID);
     Notify();
   }
 }
 
 void
 AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
 {
   nsAutoPtr<AudioChannelAgentData> data;
   mAgents.RemoveAndForget(aAgent, data);
 
   if (data) {
-    UnregisterType(data->mType, data->mElementHidden, CONTENT_PROCESS_ID_MAIN);
+    UnregisterType(data->mType, data->mElementHidden,
+                   CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
   }
 }
 
 void
 AudioChannelService::UnregisterType(AudioChannelType aType,
                                     bool aElementHidden,
-                                    uint64_t aChildID)
+                                    uint64_t aChildID,
+                                    bool aWithVideo)
 {
   // There are two reasons to defer the decrease of telephony channel.
   // 1. User can have time to remove device from his ear before music resuming.
   // 2. Give BT SCO to be disconnected before starting to connect A2DP.
   if (XRE_GetProcessType() == GeckoProcessType_Default &&
       aType == AUDIO_CHANNEL_TELEPHONY &&
       (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
        mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
     mTimerElementHidden = aElementHidden;
     mTimerChildID = aChildID;
     mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
     mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
     return;
   }
 
-  UnregisterTypeInternal(aType, aElementHidden, aChildID);
+  UnregisterTypeInternal(aType, aElementHidden, aChildID, aWithVideo);
 }
 
 void
 AudioChannelService::UnregisterTypeInternal(AudioChannelType aType,
                                             bool aElementHidden,
-                                            uint64_t aChildID)
+                                            uint64_t aChildID,
+                                            bool aWithVideo)
 {
   // The array may contain multiple occurrence of this appId but
   // this should remove only the first one.
   AudioChannelInternalType type = GetInternalType(aType, aElementHidden);
   MOZ_ASSERT(mChannelCounters[type].Contains(aChildID));
   mChannelCounters[type].RemoveElement(aChildID);
 
   // In order to avoid race conditions, it's safer to notify any existing
@@ -176,16 +185,22 @@ AudioChannelService::UnregisterTypeInter
     // We only remove ChildID when it is in the foreground.
     // If in the background, we kept ChildID for allowing it to play next song.
     if (aType == AUDIO_CHANNEL_CONTENT &&
         mActiveContentChildIDs.Contains(aChildID) &&
         !aElementHidden &&
         !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
       mActiveContentChildIDs.RemoveElement(aChildID);
     }
+
+    if (aWithVideo) {
+      MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID));
+      mWithVideoChildIDs.RemoveElement(aChildID);
+    }
+
     SendAudioChannelChangedNotification(aChildID);
     Notify();
   }
 }
 
 void
 AudioChannelService::UpdateChannelType(AudioChannelType aType,
                                        uint64_t aChildID,
@@ -253,18 +268,29 @@ AudioChannelService::GetStateInternal(Au
     // will be in list.
     if (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
       mActiveContentChildIDsFrozen = true;
     } else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
       MOZ_ASSERT(mActiveContentChildIDs.Contains(aChildID));
       mActiveContentChildIDs.RemoveElement(aChildID);
     }
   }
+  else if (newType == AUDIO_CHANNEL_INT_NORMAL &&
+           oldType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN &&
+           mWithVideoChildIDs.Contains(aChildID)) {
+    if (mActiveContentChildIDsFrozen) {
+      mActiveContentChildIDsFrozen = false;
+      mActiveContentChildIDs.Clear();
+    }
+  }
 
-  if (newType != oldType && aType == AUDIO_CHANNEL_CONTENT) {
+  if (newType != oldType &&
+      (aType == AUDIO_CHANNEL_CONTENT ||
+       (aType == AUDIO_CHANNEL_NORMAL &&
+        mWithVideoChildIDs.Contains(aChildID)))) {
     Notify();
   }
 
   SendAudioChannelChangedNotification(aChildID);
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
     if (CheckVolumeFadedCondition(newType, aElementHidden)) {
@@ -503,17 +529,17 @@ AudioChannelService::Notify()
   for (uint32_t i = 0; i < children.Length(); i++) {
     unused << children[i]->SendAudioChannelNotify();
   }
 }
 
 NS_IMETHODIMP
 AudioChannelService::Notify(nsITimer* aTimer)
 {
-  UnregisterTypeInternal(AUDIO_CHANNEL_TELEPHONY, mTimerElementHidden, mTimerChildID);
+  UnregisterTypeInternal(AUDIO_CHANNEL_TELEPHONY, mTimerElementHidden, mTimerChildID, false);
   mDeferTelChannelTimer = nullptr;
   return NS_OK;
 }
 
 bool
 AudioChannelService::ChannelsActiveWithHigherPriorityThan(
   AudioChannelInternalType aType)
 {
@@ -564,31 +590,35 @@ AudioChannelService::Observe(nsISupports
 {
   if (!strcmp(aTopic, "ipc:content-shutdown")) {
     nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
     if (!props) {
       NS_WARNING("ipc:content-shutdown message without property bag as subject");
       return NS_OK;
     }
 
+    int32_t index;
     uint64_t childID = 0;
     nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
                                              &childID);
     if (NS_SUCCEEDED(rv)) {
       for (int32_t type = AUDIO_CHANNEL_INT_NORMAL;
            type < AUDIO_CHANNEL_INT_LAST;
            ++type) {
-        int32_t index;
+
         while ((index = mChannelCounters[type].IndexOf(childID)) != -1) {
           mChannelCounters[type].RemoveElementAt(index);
         }
+      }
 
-        if ((index = mActiveContentChildIDs.IndexOf(childID)) != -1) {
-          mActiveContentChildIDs.RemoveElementAt(index);
-        }
+      while ((index = mActiveContentChildIDs.IndexOf(childID)) != -1) {
+        mActiveContentChildIDs.RemoveElementAt(index);
+      }
+      while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) {
+        mWithVideoChildIDs.RemoveElementAt(index);
       }
 
       // We don't have to remove the agents from the mAgents hashtable because if
       // that table contains only agents running on the same process.
 
       SendAudioChannelChangedNotification(childID);
       Notify();
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -41,17 +41,18 @@ public:
    */
   static void Shutdown();
 
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannelType.
    */
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannelType aType);
+                                         AudioChannelType aType,
+                                         bool aWithVideo);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
@@ -85,21 +86,21 @@ protected:
 
   /**
    * Send the audio-channel-changed notification for the given process ID if
    * needed.
    */
   void SendAudioChannelChangedNotification(uint64_t aChildID);
 
   /* Register/Unregister IPC types: */
-  void RegisterType(AudioChannelType aType, uint64_t aChildID);
+  void RegisterType(AudioChannelType aType, uint64_t aChildID, bool aWithVideo);
   void UnregisterType(AudioChannelType aType, bool aElementHidden,
-                      uint64_t aChildID);
+                      uint64_t aChildID, bool aWithVideo);
   void UnregisterTypeInternal(AudioChannelType aType, bool aElementHidden,
-                              uint64_t aChildID);
+                              uint64_t aChildID, bool aWithVideo);
 
   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);
@@ -138,39 +139,43 @@ protected:
 
   AudioChannelInternalType GetInternalType(AudioChannelType aType,
                                            bool aElementHidden);
 
   class AudioChannelAgentData {
   public:
     AudioChannelAgentData(AudioChannelType aType,
                           bool aElementHidden,
-                          AudioChannelState aState)
+                          AudioChannelState aState,
+                          bool aWithVideo)
     : mType(aType)
     , mElementHidden(aElementHidden)
     , mState(aState)
+    , mWithVideo(aWithVideo)
     {}
 
     AudioChannelType mType;
     bool mElementHidden;
     AudioChannelState mState;
+    const bool mWithVideo;
   };
 
   static PLDHashOperator
   NotifyEnumerator(AudioChannelAgent* aAgent,
                    AudioChannelAgentData* aData, void *aUnused);
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
   AudioChannelType mCurrentHigherChannel;
   AudioChannelType mCurrentVisibleHigherChannel;
 
   nsTArray<uint64_t> mActiveContentChildIDs;
+  nsTArray<uint64_t> mWithVideoChildIDs;
   bool mActiveContentChildIDsFrozen;
 
   nsCOMPtr<nsITimer> mDeferTelChannelTimer;
   bool mTimerElementHidden;
   uint64_t mTimerChildID;
 
   uint64_t mDefChannelChildID;
 
--- a/dom/audiochannel/AudioChannelServiceChild.cpp
+++ b/dom/audiochannel/AudioChannelServiceChild.cpp
@@ -61,49 +61,42 @@ AudioChannelServiceChild::~AudioChannelS
 AudioChannelState
 AudioChannelServiceChild::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
 {
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
     return AUDIO_CHANNEL_STATE_MUTED;
   }
 
-  ContentChild *cc = ContentChild::GetSingleton();
   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->SendAudioChannelGetState(data->mType, aElementHidden, oldElementHidden, &state);
-  }
+  ContentChild* cc = ContentChild::GetSingleton();
+  cc->SendAudioChannelGetState(data->mType, aElementHidden, oldElementHidden, &state);
   data->mState = state;
-
-  if (cc) {
-    cc->SendAudioChannelChangedNotification();
-  }
+  cc->SendAudioChannelChangedNotification();
 
   return state;
 }
 
 void
 AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                                    AudioChannelType aType)
+                                                    AudioChannelType aType,
+                                                    bool aWithVideo)
 {
   MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
 
-  AudioChannelService::RegisterAudioChannelAgent(aAgent, aType);
+  AudioChannelService::RegisterAudioChannelAgent(aAgent, aType, aWithVideo);
 
-  ContentChild *cc = ContentChild::GetSingleton();
-  if (cc) {
-    cc->SendAudioChannelRegisterType(aType);
-  }
+  ContentChild::GetSingleton()->SendAudioChannelRegisterType(aType, aWithVideo);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
   }
 }
 
 void
@@ -115,20 +108,18 @@ AudioChannelServiceChild::UnregisterAudi
   }
 
   // We need to keep a copy because unregister will remove the
   // AudioChannelAgentData object from the hashtable.
   AudioChannelAgentData data(*pData);
 
   AudioChannelService::UnregisterAudioChannelAgent(aAgent);
 
-  ContentChild *cc = ContentChild::GetSingleton();
-  if (cc) {
-    cc->SendAudioChannelUnregisterType(data.mType, data.mElementHidden);
-  }
+  ContentChild::GetSingleton()->SendAudioChannelUnregisterType(
+      data.mType, data.mElementHidden, data.mWithVideo);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
   }
 }
 
 void
--- a/dom/audiochannel/AudioChannelServiceChild.h
+++ b/dom/audiochannel/AudioChannelServiceChild.h
@@ -26,17 +26,18 @@ public:
    *
    * @return NS_OK on proper assignment, NS_ERROR_FAILURE otherwise.
    */
   static AudioChannelService* GetAudioChannelService();
 
   static void Shutdown();
 
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannelType aType);
+                                         AudioChannelType aType,
+                                         bool aWithVideo);
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
    * Return the state to indicate this agent should keep playing/
    * fading volume/muted.
    */
   virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
                                      bool aElementHidden);
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -27,17 +27,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.
  */
 
-[scriptable, uuid(7a4c0b06-63a4-11e2-8c1b-10bf48d64bd4)]
+[scriptable, uuid(86ef883d-9cec-4c04-994f-5de198286e7c)]
 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;
@@ -74,16 +74,24 @@ interface nsIAudioChannelAgent : nsISupp
    * weak reference to the callback object.
    *
    * In order for this to work, |callback| must implement
    * nsISupportsWeakReference.
    */
   void initWithWeakCallback(in long channelType, in nsIAudioChannelAgentCallback callback);
 
   /**
+   * This method is just like init(), and specify the channel is associated with video.
+   *
+   * @param weak
+   *    true if weak reference should be hold.
+   */
+  void initWithVideo(in long channelType, in nsIAudioChannelAgentCallback callback, in boolean weak);
+
+  /**
    * 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
    *    normal state: the agent has registered with audio channel service and
    *          the component should start playback.
@@ -105,11 +113,10 @@ interface nsIAudioChannelAgent : nsISupp
   void stopPlaying();
 
   /**
    * Notify the agent of the visibility state of the window using this agent.
    * @param visible
    *    True if the window associated with the agent is visible.
    */
   void setVisibilityState(in boolean visible);
-
 };
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1592,34 +1592,36 @@ ContentParent::RecvAudioChannelGetState(
     if (service) {
         *aState = service->GetStateInternal(aType, mChildID,
                                             aElementHidden, aElementWasHidden);
     }
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelRegisterType(const AudioChannelType& aType)
+ContentParent::RecvAudioChannelRegisterType(const AudioChannelType& aType,
+                                            const bool& aWithVideo)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
-        service->RegisterType(aType, mChildID);
+        service->RegisterType(aType, mChildID, aWithVideo);
     }
     return true;
 }
 
 bool
 ContentParent::RecvAudioChannelUnregisterType(const AudioChannelType& aType,
-                                              const bool& aElementHidden)
+                                              const bool& aElementHidden,
+                                              const bool& aWithVideo)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
-        service->UnregisterType(aType, aElementHidden, mChildID);
+        service->UnregisterType(aType, aElementHidden, mChildID, aWithVideo);
     }
     return true;
 }
 
 bool
 ContentParent::RecvAudioChannelChangedNotification()
 {
     nsRefPtr<AudioChannelService> service =
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -415,19 +415,21 @@ private:
 
     virtual bool RecvFirstIdle();
 
     virtual bool RecvAudioChannelGetState(const AudioChannelType& aType,
                                           const bool& aElementHidden,
                                           const bool& aElementWasHidden,
                                           AudioChannelState* aValue);
 
-    virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType);
+    virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType,
+                                              const bool& aWithVideo);
     virtual bool RecvAudioChannelUnregisterType(const AudioChannelType& aType,
-                                                const bool& aElementHidden);
+                                                const bool& aElementHidden,
+                                                const bool& aWithVideo);
 
     virtual bool RecvAudioChannelChangedNotification();
 
     virtual bool RecvAudioChannelChangeDefVolChannel(
       const AudioChannelType& aType, const bool& aHidden);
 
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -437,19 +437,20 @@ parent:
     // Tell the parent that the child has gone idle for the first time
     async FirstIdle();
 
     // Get Muted from the main AudioChannelService.
     sync AudioChannelGetState(AudioChannelType aType, bool aElementHidden,
                               bool aElementWasHidden)
         returns (AudioChannelState value);
 
-    sync AudioChannelRegisterType(AudioChannelType aType);
+    sync AudioChannelRegisterType(AudioChannelType aType, bool aWithVideo);
     sync AudioChannelUnregisterType(AudioChannelType aType,
-                                    bool aElementHidden);
+                                    bool aElementHidden,
+                                    bool aWithVideo);
 
     async AudioChannelChangedNotification();
     async AudioChannelChangeDefVolChannel(AudioChannelType aType,
                                           bool aHidden);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,