Bug 976172 - Part 1: Changes in MediaOmxDecoder to enable audio offloading. r=roc, a=1.4+
authorVasanthakumar Pandurangan <vasanth@codeaurora.org>
Mon, 24 Mar 2014 19:10:30 +0530
changeset 191715 91e8b45c5f5e9f58d1395af3a836b5ec4f0fa0f7
parent 191714 86a35aa4500da40d506895e3b69a13cf02572d6e
child 191716 f989ff4e46cde969d30b0adef3e71c39f7f6dc14
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, 1
bugs976172
milestone30.0a2
Bug 976172 - Part 1: Changes in MediaOmxDecoder to enable audio offloading. r=roc, a=1.4+ This adds support in MediaOmxDecoder to dynamically switch between MediaDecoderStateMachine and AudioOffloadPlayer.
configure.in
content/html/content/public/HTMLMediaElement.h
content/html/content/src/HTMLMediaElement.cpp
content/media/AbstractMediaDecoder.h
content/media/MediaDecoder.h
content/media/omx/AudioOffloadPlayerBase.h
content/media/omx/MediaOmxDecoder.cpp
content/media/omx/MediaOmxDecoder.h
content/media/omx/MediaOmxReader.cpp
content/media/omx/MediaOmxReader.h
content/media/omx/OmxDecoder.cpp
content/media/omx/OmxDecoder.h
content/media/omx/moz.build
--- a/configure.in
+++ b/configure.in
@@ -223,17 +223,17 @@ if test -n "$gonkdir" ; then
         ;;
     i?86)
         ARCH_DIR=arch-x86
         ;;
     esac
 
     case "$ANDROID_VERSION" in
     15)
-        GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
+        GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/ -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
         MOZ_B2G_BT=1
         MOZ_B2G_BT_BLUEZ=1
         MOZ_NFC=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_SUBST(MOZ_OMX_DECODER)
         MOZ_RTSP=1
         ;;
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -45,16 +45,19 @@ class WakeLock;
 
 class nsITimer;
 class nsRange;
 class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
+// Number of milliseconds between timeupdate events as defined by spec
+#define TIMEUPDATE_MS 250
+
 class MediaError;
 class MediaSource;
 class TextTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -104,19 +104,16 @@ static PRLogModuleInfo* gMediaElementEve
 #include "nsContentTypeParser.h"
 
 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;
@@ -3417,16 +3414,17 @@ void HTMLMediaElement::SuspendOrResumeEl
   }
 }
 
 void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
 {
   nsIDocument* ownerDoc = OwnerDoc();
 
   if (mDecoder) {
+    mDecoder->SetElementVisibility(!ownerDoc->Hidden());
     mDecoder->SetDormantIfNecessary(ownerDoc->Hidden());
   }
 
   // SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
   // CanPlayChanged callback.
   if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&
       mAudioChannelAgent) {
     mAudioChannelAgent->SetVisibilityState(!ownerDoc->Hidden());
--- a/content/media/AbstractMediaDecoder.h
+++ b/content/media/AbstractMediaDecoder.h
@@ -111,16 +111,22 @@ public:
   // Returns the owner of this media decoder. The owner should only be used
   // on the main thread.
   virtual MediaDecoderOwner* GetOwner() = 0;
 
   // May be called by the reader to notify the decoder that the resources
   // required to begin playback have been acquired. Can be called on any thread.
   virtual void NotifyWaitingForResourcesStatusChanged() = 0;
 
+  // Called by Reader if the current audio track can be offloaded
+  virtual void SetCanOffloadAudio(bool aCanOffloadAudio) {}
+
+  // Called from HTMLMediaElement when owner document activity changes
+  virtual void SetElementVisibility(bool aIsVisible) {}
+
   // Stack based class to assist in notifying the frame statistics of
   // parsed and decoded frames. Use inside video demux & decode functions
   // to ensure all parsed and decoded frames are reported on all return paths.
   class AutoNotifyDecoded {
   public:
     AutoNotifyDecoded(AbstractMediaDecoder* aDecoder, uint32_t& aParsed, uint32_t& aDecoded)
       : mDecoder(aDecoder), mParsed(aParsed), mDecoded(aDecoded) {}
     ~AutoNotifyDecoded() {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -338,17 +338,17 @@ public:
   // Adjust the speed of the playback, optionally with pitch correction,
   virtual void SetVolume(double aVolume);
   // Sets whether audio is being captured. If it is, we won't play any
   // of our audio.
   virtual void SetAudioCaptured(bool aCaptured);
 
   virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
 
-  void SetPlaybackRate(double aPlaybackRate);
+  virtual void SetPlaybackRate(double aPlaybackRate);
   void SetPreservesPitch(bool aPreservesPitch);
 
   // All MediaStream-related data is protected by mReentrantMonitor.
   // We have at most one DecodedStreamData per MediaDecoder. Its stream
   // is used as the input for each ProcessedMediaStream created by calls to
   // captureStream(UntilEnded). Seeking creates a new source stream, as does
   // replaying after the input as ended. In the latter case, the new source is
   // not connected to streams created by captureStreamUntilEnded.
@@ -735,29 +735,33 @@ public:
   /******
    * The following methods must only be called on the main
    * thread.
    ******/
 
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
-  void ChangeState(PlayState aState);
+  virtual void ChangeState(PlayState aState);
 
   // Called by |ChangeState|, to update the state machine.
   // Call on the main thread only and the lock must be obtained.
   virtual void ApplyStateToStateMachine(PlayState aState);
 
   // May be called by the reader to notify this decoder that the metadata from
   // the media file has been read. Call on the decode thread only.
   void OnReadMetadataCompleted() MOZ_OVERRIDE { }
 
   // Called when the metadata from the media file has been loaded by the
   // state machine. Call on the main thread only.
-  void MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags);
+  virtual void MetadataLoaded(int aChannels,
+                              int aRate,
+                              bool aHasAudio,
+                              bool aHasVideo,
+                              MetadataTags* aTags);
 
   // Called when the first frame has been loaded.
   // Call on the main thread only.
   void FirstFrameLoaded();
 
   // Returns true if the resource has been loaded. Acquires the monitor.
   // Call from any thread.
   virtual bool IsDataCachedToEndOfResource();
@@ -780,17 +784,17 @@ public:
 
   // Called when the backend has changed the current playback
   // position. It dispatches a timeupdate event and invalidates the frame.
   // This must be called on the main thread only.
   void PlaybackPositionChanged();
 
   // Calls mElement->UpdateReadyStateForData, telling it whether we have
   // data for the next frame and if we're buffering. Main thread only.
-  void UpdateReadyStateForData();
+  virtual void UpdateReadyStateForData();
 
   // Find the end of the cached data starting at the current decoder
   // position.
   int64_t GetDownloadPosition();
 
   // Updates the approximate byte offset which playback has reached. This is
   // used to calculate the readyState transitions.
   void UpdatePlaybackOffset(int64_t aOffset);
new file mode 100644
--- /dev/null
+++ b/content/media/omx/AudioOffloadPlayerBase.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_OFFLOAD_PLAYER_BASE_H_
+#define AUDIO_OFFLOAD_PLAYER_BASE_H_
+
+#include "MediaDecoderOwner.h"
+#include "MediaOmxDecoder.h"
+
+namespace mozilla {
+
+class MediaOmxDecoder;
+
+/**
+ * AudioOffloadPlayer interface class which has funtions used by MediaOmxDecoder
+ * This is to reduce the dependency of AudioOffloadPlayer in MediaOmxDecoder
+ */
+class AudioOffloadPlayerBase
+{
+  typedef android::status_t status_t;
+  typedef android::MediaSource MediaSource;
+
+public:
+  virtual ~AudioOffloadPlayerBase() {};
+
+  // Caller retains ownership of "aSource".
+  virtual void SetSource(const android::sp<MediaSource> &aSource) {}
+
+  // Start the source if it's not already started and open the AudioSink to
+  // create an offloaded audio track
+  virtual status_t Start(bool aSourceAlreadyStarted = false)
+  {
+    return android::NO_INIT;
+  }
+
+  virtual void ChangeState(MediaDecoder::PlayState aState) {}
+
+  virtual void SetVolume(double aVolume) {}
+
+  virtual double GetMediaTimeSecs() { return 0; }
+
+  // To update progress bar when the element is visible
+  virtual void SetElementVisibility(bool aIsVisible) {}
+
+  // Update ready state based on current play state. Not checking data
+  // availability since offloading is currently done only when whole compressed
+  // data is available
+  virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus()
+  {
+    return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
+  }
+};
+
+} // namespace mozilla
+
+#endif // AUDIO_OFFLOAD_PLAYER_BASE_H_
--- a/content/media/omx/MediaOmxDecoder.cpp
+++ b/content/media/omx/MediaOmxDecoder.cpp
@@ -3,31 +3,246 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaOmxDecoder.h"
 #include "MediaOmxReader.h"
 #include "MediaDecoderStateMachine.h"
 
+#include "OmxDecoder.h"
+
+#ifdef MOZ_AUDIO_OFFLOAD
+#include "AudioOffloadPlayer.h"
+#endif
+
+using namespace android;
+
 namespace mozilla {
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaDecoderLog;
+#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
+#else
+#define DECODER_LOG(type, msg)
+#endif
+
 MediaOmxDecoder::MediaOmxDecoder() :
-  MediaDecoder()
+  MediaDecoder(),
+  mCanOffloadAudio(false),
+  mFallbackToStateMachine(false)
 {
+#ifdef PR_LOGGING
+  if (!gMediaDecoderLog) {
+    gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
+  }
+#endif
 }
 
 MediaDecoder* MediaOmxDecoder::Clone()
 {
   return new MediaOmxDecoder();
 }
 
 MediaDecoderStateMachine* MediaOmxDecoder::CreateStateMachine()
 {
-  return new MediaDecoderStateMachine(this, new MediaOmxReader(this));
+  mReader = new MediaOmxReader(this);
+  mReader->SetAudioChannelType(GetAudioChannelType());
+  return new MediaDecoderStateMachine(this, mReader);
+}
+
+void MediaOmxDecoder::SetCanOffloadAudio(bool aCanOffloadAudio)
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mCanOffloadAudio = aCanOffloadAudio;
+}
+
+void MediaOmxDecoder::MetadataLoaded(int aChannels,
+                                     int aRate,
+                                     bool aHasAudio,
+                                     bool aHasVideo,
+                                     MetadataTags* aTags)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MediaDecoder::MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags);
+
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  if (!mCanOffloadAudio || mFallbackToStateMachine || mOutputStreams.Length() ||
+      mInitialPlaybackRate != 1.0) {
+    DECODER_LOG(PR_LOG_DEBUG, ("In %s Offload Audio check failed",
+        __PRETTY_FUNCTION__));
+    return;
+  }
+
+#ifdef MOZ_AUDIO_OFFLOAD
+  mAudioOffloadPlayer = new AudioOffloadPlayer(this);
+#endif
+  mAudioOffloadPlayer->SetSource(mReader->GetAudioOffloadTrack());
+  status_t err = mAudioOffloadPlayer->Start(false);
+  if (err == OK) {
+    PauseStateMachine();
+    // Call ChangeState() to run AudioOffloadPlayer since offload state enabled
+    ChangeState(mPlayState);
+    return;
+  }
+
+  mAudioOffloadPlayer = nullptr;
+  DECODER_LOG(PR_LOG_DEBUG, ("In %s Unable to start offload audio %d."
+      "Switching to normal mode", __PRETTY_FUNCTION__, err));
+}
+
+void MediaOmxDecoder::PauseStateMachine()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
+
+  if (!mDecoderStateMachine) {
+    return;
+  }
+  StopProgress();
+  mDecoderStateMachine->SetDormant(true);
+}
+
+void MediaOmxDecoder::ResumeStateMachine()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  DECODER_LOG(PR_LOG_DEBUG, ("%s current time %f", __PRETTY_FUNCTION__,
+      mCurrentTime));
+
+  if (!mDecoderStateMachine) {
+    return;
+  }
+
+  mFallbackToStateMachine = true;
+  mAudioOffloadPlayer = nullptr;
+  mRequestedSeekTime = mCurrentTime;
+
+  mNextState = mPlayState;
+  ChangeState(PLAY_STATE_LOADING);
+  mDecoderStateMachine->SetDormant(false);
+}
+
+void MediaOmxDecoder::AudioOffloadTearDown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  PlaybackPositionChanged();
+  DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
+  {
+    // Audio offload player sent tear down event. Fallback to state machine
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    ResumeStateMachine();
+  }
 }
 
-MediaOmxDecoder::~MediaOmxDecoder()
+void MediaOmxDecoder::AddOutputStream(ProcessedMediaStream* aStream,
+                                      bool aFinishWhenEnded)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  PlaybackPositionChanged();
+
+  if (mAudioOffloadPlayer) {
+    // Offload player cannot handle MediaStream. Fallback
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    ResumeStateMachine();
+  }
+
+  MediaDecoder::AddOutputStream(aStream, aFinishWhenEnded);
+}
+
+void MediaOmxDecoder::SetPlaybackRate(double aPlaybackRate)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  PlaybackPositionChanged();
+
+  if (mAudioOffloadPlayer &&
+      ((aPlaybackRate != 0.0) || (aPlaybackRate != 1.0))) {
+    // Offload player cannot handle playback rate other than 1/0. Fallback
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    ResumeStateMachine();
+  }
+
+  MediaDecoder::SetPlaybackRate(aPlaybackRate);
+}
+
+void MediaOmxDecoder::ChangeState(PlayState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Keep MediaDecoder state in sync with MediaElement irrespective of offload
+  // playback so it will continue to work in normal mode when offloading fails
+  // in between
+  MediaDecoder::ChangeState(aState);
+
+  if (mAudioOffloadPlayer) {
+    mAudioOffloadPlayer->ChangeState(aState);
+  }
+}
+
+void MediaOmxDecoder::ApplyStateToStateMachine(PlayState aState)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // During offload playback, state machine should be in dormant state.
+  // ApplyStateToStateMachine() can change state machine state to
+  // something else or reset the seek time. So don't call this when audio is
+  // offloaded
+  if (!mAudioOffloadPlayer) {
+    MediaDecoder::ApplyStateToStateMachine(aState);
+  }
+}
+
+void MediaOmxDecoder::PlaybackPositionChanged()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mAudioOffloadPlayer) {
+    MediaDecoder::PlaybackPositionChanged();
+    return;
+  }
+
+  if (!mOwner || mShuttingDown) {
+    return;
+  }
+
+  double lastTime = mCurrentTime;
+  {
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    mCurrentTime = mAudioOffloadPlayer->GetMediaTimeSecs();
+  }
+  if (mOwner && lastTime != mCurrentTime) {
+    FireTimeUpdate();
+  }
+}
+
+void MediaOmxDecoder::SetElementVisibility(bool aIsVisible)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mAudioOffloadPlayer) {
+    mAudioOffloadPlayer->SetElementVisibility(aIsVisible);
+  }
+}
+
+void MediaOmxDecoder::UpdateReadyStateForData()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mAudioOffloadPlayer) {
+    MediaDecoder::UpdateReadyStateForData();
+    return;
+  }
+
+  if (!mOwner || mShuttingDown)
+    return;
+  mOwner->UpdateReadyStateForData(mAudioOffloadPlayer->GetNextFrameStatus());
+}
+
+void MediaOmxDecoder::SetVolume(double aVolume)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mAudioOffloadPlayer) {
+    MediaDecoder::SetVolume(aVolume);
+    return;
+  }
+  mAudioOffloadPlayer->SetVolume(aVolume);
 }
 
 } // namespace mozilla
-
--- a/content/media/omx/MediaOmxDecoder.h
+++ b/content/media/omx/MediaOmxDecoder.h
@@ -3,24 +3,63 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(MediaOmxDecoder_h_)
 #define MediaOmxDecoder_h_
 
 #include "base/basictypes.h"
 #include "MediaDecoder.h"
+#include "MediaOmxReader.h"
+#include "AudioOffloadPlayerBase.h"
 
 namespace mozilla {
 
 class MediaOmxDecoder : public MediaDecoder
 {
+  typedef android::MediaSource MediaSource;
 public:
   MediaOmxDecoder();
-  ~MediaOmxDecoder();
-
   virtual MediaDecoder* Clone();
   virtual MediaDecoderStateMachine* CreateStateMachine();
+
+  virtual void MetadataLoaded(int aChannels,
+                              int aRate,
+                              bool aHasAudio,
+                              bool aHasVideo,
+                              MetadataTags* aTags);
+  virtual void ChangeState(PlayState aState);
+  virtual void ApplyStateToStateMachine(PlayState aState);
+  virtual void SetVolume(double aVolume);
+  virtual void PlaybackPositionChanged();
+  virtual void UpdateReadyStateForData();
+  virtual void SetElementVisibility(bool aIsVisible);
+  virtual void SetCanOffloadAudio(bool aCanOffloadAudio);
+  virtual void AddOutputStream(ProcessedMediaStream* aStream,
+                               bool aFinishWhenEnded);
+  virtual void SetPlaybackRate(double aPlaybackRate);
+
+  void AudioOffloadTearDown();
+  double GetSeekTime() { return mRequestedSeekTime; }
+  void ResetSeekTime() { mRequestedSeekTime = -1.0; }
+
+private:
+  void PauseStateMachine();
+  void ResumeStateMachine();
+
+  MediaOmxReader* mReader;
+
+  // Offloaded audio track
+  android::sp<MediaSource> mAudioTrack;
+
+  nsAutoPtr<AudioOffloadPlayerBase> mAudioOffloadPlayer;
+
+  // Set by MediaOmxReader to denote current track can be offloaded
+  bool mCanOffloadAudio;
+
+  // Set when offload playback of current track fails in the middle and need to
+  // fallback to state machine
+  bool mFallbackToStateMachine;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -12,33 +12,52 @@
 #include "MediaResource.h"
 #include "VideoUtils.h"
 #include "MediaOmxDecoder.h"
 #include "AbstractMediaDecoder.h"
 #include "OmxDecoder.h"
 #include "MPAPI.h"
 #include "gfx2DGlue.h"
 
+#ifdef MOZ_AUDIO_OFFLOAD
+#include <stagefright/Utils.h>
+#include <cutils/properties.h>
+#include <stagefright/MetaData.h>
+#endif
+
 #define MAX_DROPPED_FRAMES 25
 // Try not to spend more than this much time in a single call to DecodeVideoFrame.
 #define MAX_VIDEO_DECODE_SECONDS 3.0
 
 using namespace mozilla::gfx;
 using namespace android;
 
 namespace mozilla {
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaDecoderLog;
+#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
+#else
+#define DECODER_LOG(type, msg)
+#endif
+
 MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder) :
   MediaDecoderReader(aDecoder),
   mHasVideo(false),
   mHasAudio(false),
   mVideoSeekTimeUs(-1),
   mAudioSeekTimeUs(-1),
-  mSkipCount(0)
+  mSkipCount(0),
+  mAudioChannelType(dom::AUDIO_CHANNEL_DEFAULT)
 {
+#ifdef PR_LOGGING
+  if (!gMediaDecoderLog) {
+    gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
+  }
+#endif
 }
 
 MediaOmxReader::~MediaOmxReader()
 {
   ReleaseMediaResources();
   ReleaseDecoder();
   mOmxDecoder.clear();
 }
@@ -119,16 +138,20 @@ nsresult MediaOmxReader::ReadMetadata(Me
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (!mOmxDecoder->TryLoad()) {
     return NS_ERROR_FAILURE;
   }
 
+#ifdef MOZ_AUDIO_OFFLOAD
+  CheckAudioOffload();
+#endif
+
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
 
   // Set the total duration (the max of the audio and video track).
   int64_t durationUs;
   mOmxDecoder->GetDuration(&durationUs);
   if (durationUs) {
@@ -371,10 +394,46 @@ void MediaOmxReader::SetIdle() {
 void MediaOmxReader::SetActive() {
   if (!mOmxDecoder.get()) {
     return;
   }
   DebugOnly<nsresult> result = mOmxDecoder->Play();
   NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
 }
 
+#ifdef MOZ_AUDIO_OFFLOAD
+void MediaOmxReader::CheckAudioOffload()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  char offloadProp[128];
+  property_get("audio.offload.disable", offloadProp, "0");
+  bool offloadDisable =  atoi(offloadProp) != 0;
+  if (offloadDisable) {
+    return;
+  }
+
+  mAudioOffloadTrack = mOmxDecoder->GetAudioOffloadTrack();
+  sp<MetaData> meta = (mAudioOffloadTrack.get()) ?
+      mAudioOffloadTrack->getFormat() : nullptr;
+
+  // Supporting audio offload only when there is no video, no streaming
+  bool hasNoVideo = !mOmxDecoder->HasVideo();
+  bool isNotStreaming
+      = mDecoder->GetResource()->IsDataCachedToEndOfResource(0);
+
+  // Not much benefit in trying to offload other channel types. Most of them
+  // aren't supported and also duration would be less than a minute
+  bool isTypeMusic = mAudioChannelType == dom::AUDIO_CHANNEL_CONTENT;
+
+  DECODER_LOG(PR_LOG_DEBUG, ("%s meta %p, no video %d, no streaming %d,"
+      " channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
+      isNotStreaming, mAudioChannelType));
+
+  if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
+      canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC)) {
+    DECODER_LOG(PR_LOG_DEBUG, ("Can offload this audio stream"));
+    mDecoder->SetCanOffloadAudio(true);
+  }
+}
+#endif
+
 } // namespace mozilla
-
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -4,17 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(MediaOmxReader_h_)
 #define MediaOmxReader_h_
 
 #include "MediaResource.h"
 #include "MediaDecoderReader.h"
 #include "nsRect.h"
+#include "AudioChannelCommon.h"
 #include <ui/GraphicBuffer.h>
+#include <stagefright/MediaSource.h>
 
 namespace android {
 class OmxDecoder;
 class MediaExtractor;
 }
 
 namespace mozilla {
 
@@ -29,20 +31,21 @@ class MediaOmxReader : public MediaDecod
   nsCString mType;
   bool mHasVideo;
   bool mHasAudio;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
   int64_t mVideoSeekTimeUs;
   int64_t mAudioSeekTimeUs;
   int32_t mSkipCount;
+  dom::AudioChannelType mAudioChannelType;
+  android::sp<android::MediaSource> mAudioOffloadTrack;
 
 protected:
   android::sp<android::OmxDecoder> mOmxDecoder;
-
   android::sp<android::MediaExtractor> mExtractor;
 
   // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
   // on decode thread. It create and initialize the OMX decoder including
   // setting up custom extractor. The extractor provide the essential
   // information used for creating OMX decoder such as video/audio codec.
   virtual nsresult InitOmxDecoder();
 
@@ -77,13 +80,28 @@ public:
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
   virtual void SetIdle() MOZ_OVERRIDE;
   virtual void SetActive() MOZ_OVERRIDE;
 
+  void SetAudioChannelType(dom::AudioChannelType aAudioChannelType) {
+    mAudioChannelType = aAudioChannelType;
+  }
+
+  android::sp<android::MediaSource> GetAudioOffloadTrack() {
+    return mAudioOffloadTrack;
+  }
+
+#ifdef MOZ_AUDIO_OFFLOAD
+  // Check whether it is possible to offload current audio track. This access
+  // canOffloadStream() from libStageFright Utils.cpp, which is not there in
+  // ANDROID_VERSION < 19
+  void CheckAudioOffload();
+#endif
+
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -389,16 +389,22 @@ bool OmxDecoder::Init(sp<MediaExtractor>
   mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
 
   if (videoTrackIndex != -1) {
     mVideoTrack = extractor->getTrack(videoTrackIndex);
   }
 
   if (audioTrackIndex != -1) {
     mAudioTrack = extractor->getTrack(audioTrackIndex);
+
+#ifdef MOZ_AUDIO_OFFLOAD
+    // mAudioTrack is be used by OMXCodec. For offloaded audio track, using same
+    // object gives undetermined behavior. So get a new track
+    mAudioOffloadTrack = extractor->getTrack(audioTrackIndex);
+#endif
   }
   return true;
 }
 
 bool OmxDecoder::TryLoad() {
 
   if (!AllocateMediaResources()) {
     return false;
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -98,16 +98,17 @@ class OmxDecoder : public OMXCodecProxy:
   };
 
   AbstractMediaDecoder *mDecoder;
   nsRefPtr<MediaResource> mResource;
   sp<GonkNativeWindow> mNativeWindow;
   sp<GonkNativeWindowClient> mNativeWindowClient;
   sp<MediaSource> mVideoTrack;
   sp<OMXCodecProxy> mVideoSource;
+  sp<MediaSource> mAudioOffloadTrack;
   sp<MediaSource> mAudioTrack;
   sp<MediaSource> mAudioSource;
   int32_t mVideoWidth;
   int32_t mVideoHeight;
   int32_t mVideoColorFormat;
   int32_t mVideoStride;
   int32_t mVideoSliceHeight;
   int32_t mVideoRotation;
@@ -253,12 +254,14 @@ public:
 
   // Post kNotifyPostReleaseVideoBuffer message to OmxDecoder via ALooper.
   void PostReleaseVideoBuffer(MediaBuffer *aBuffer, const FenceHandle& aReleaseFenceHandle);
   // Receive a message from AHandlerReflector.
   // Called on ALooper thread.
   void onMessageReceived(const sp<AMessage> &msg);
 
   int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
+
+  sp<MediaSource> GetAudioOffloadTrack() { return mAudioOffloadTrack; }
 };
 
 }
 
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -1,26 +1,38 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
+    'AudioOffloadPlayerBase.h',
     'MediaOmxDecoder.h',
     'MediaOmxReader.h',
 ]
 
 SOURCES += [
     'MediaOmxDecoder.cpp',
     'MediaOmxReader.cpp',
     'OMXCodecProxy.cpp',
     'OmxDecoder.cpp',
 ]
 
+if CONFIG['MOZ_AUDIO_OFFLOAD']:
+    EXPORTS += [
+        'AudioOffloadPlayer.h',
+        'AudioOutput.h',
+        'AudioSink.h',
+    ]
+    SOURCES += [
+        'AudioOffloadPlayer.cpp',
+        'AudioOutput.cpp',
+    ]
+
 if CONFIG['MOZ_OMX_ENCODER']:
     EXPORTS += [
         'OMXCodecWrapper.h',
     ]
     SOURCES += [
         'OMXCodecDescriptorUtil.cpp',
         'OMXCodecWrapper.cpp',
     ]
@@ -52,11 +64,12 @@ CXXFLAGS += [
         'frameworks/base/include',
         'frameworks/base/include/binder',
         'frameworks/base/include/utils',
         'frameworks/base/include/media',
         'frameworks/base/include/media/stagefright/openmax',
         'frameworks/base/media/libstagefright/include',
         'frameworks/native/opengl/include',
         'frameworks/native/include',
+        'hardware/libhardware/include/',
     ]
 ]