/* -*- 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_H_ #define AUDIO_OFFLOAD_PLAYER_H_ #include #include #include #include #include #include "AudioOutput.h" #include "AudioOffloadPlayerBase.h" #include "MediaDecoderOwner.h" #include "MediaOmxCommonDecoder.h" namespace mozilla { namespace dom { class WakeLock; } /** * AudioOffloadPlayer adds support for audio tunneling to a digital signal * processor (DSP) in the device chipset. With tunneling, audio decoding is * off-loaded to the DSP, waking the application processor less often and using * less battery * * This depends on offloading capability provided by Android KK AudioTrack class * * Audio playback is based on pull mechanism, whenever audio sink needs * data, FillBuffer() will read data from compressed audio source and provide * it to the sink * * Also this class passes state changes (play/pause/seek) from * MediaOmxCommonDecoder to GonkAudioSink as well as provide GonkAudioSink status * (position changed, playback ended, seek complete, audio tear down) back to * MediaOmxCommonDecoder * * It acts as a bridge between MediaOmxCommonDecoder and GonkAudioSink during * offload playback */ class AudioOffloadPlayer : public AudioOffloadPlayerBase { typedef android::Mutex Mutex; typedef android::MetaData MetaData; typedef android::status_t status_t; typedef android::AudioTrack AudioTrack; typedef android::MediaBuffer MediaBuffer; typedef android::MediaSource MediaSource; public: enum { REACHED_EOS, SEEK_COMPLETE }; AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr); ~AudioOffloadPlayer(); // Caller retains ownership of "aSource". virtual void SetSource(const android::sp &aSource) override; // Start the source if it's not already started and open the GonkAudioSink to // create an offloaded audio track virtual status_t Start(bool aSourceAlreadyStarted = false) override; virtual status_t ChangeState(MediaDecoder::PlayState aState) override; virtual void SetVolume(double aVolume) override; virtual int64_t GetMediaTimeUs() override; // To update progress bar when the element is visible virtual void SetElementVisibility(bool aIsVisible) override;; // 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() override; virtual RefPtr Seek(SeekTarget aTarget) override; void TimeUpdate(); // Close the audio sink, stop time updates, frees the input buffers void Reset(); private: // Set when audio source is started and GonkAudioSink is initialized // Used only in main thread bool mStarted; // Set when audio sink is started. i.e. playback started // Used only in main thread bool mPlaying; // Once playback reached end of stream (last ~100ms), position provided by DSP // may be reset/corrupted. This bool is used to avoid that. // Used in main thread and offload callback thread, protected by Mutex // mLock bool mReachedEOS; // Set when the HTML Audio Element is visible to the user. // Used only in main thread bool mIsElementVisible; // Session id given by Android::AudioSystem and used while creating audio sink // Used only in main thread int mSessionId; // Sample rate of current audio track. Used only in main thread int mSampleRate; // After seeking, positions returned by offlaoded tracks (DSP) will be // relative to the seeked position. And seeked position may be slightly // different than given mSeekTimeUs, if audio source cannot find a frame at // that position. Store seeked position in mStartPosUs and provide // mStartPosUs + GetPosition() (i.e. absolute position) to // MediaOmxCommonDecoder // Used in main thread and offload callback thread, protected by Mutex // mLock int64_t mStartPosUs; // The target of current seek when there is a request to seek // Used in main thread and offload callback thread, protected by Mutex // mLock SeekTarget mSeekTarget; // MozPromise of current seek. // Used in main thread and offload callback thread, protected by Mutex // mLock MozPromiseHolder mSeekPromise; // Positions obtained from offlaoded tracks (DSP) // Used in main thread and offload callback thread, protected by Mutex // mLock int64_t mPositionTimeMediaUs; // State obtained from MediaOmxCommonDecoder. Used only in main thread MediaDecoder::PlayState mPlayState; // Protect accessing audio position related variables between main thread and // offload callback thread Mutex mLock; // Compressed audio source. // Used in main thread first and later in offload callback thread android::sp mSource; // Audio sink wrapper to access offloaded audio tracks // Used in main thread and offload callback thread // Race conditions are protected in underlying Android::AudioTrack class android::sp mAudioSink; // Buffer used to get date from audio source. Used in offload callback thread MediaBuffer* mInputBuffer; // MediaOmxCommonDecoder object used mainly to notify the audio sink status MediaOmxCommonDecoder* mObserver; TimeStamp mLastFireUpdateTime; // Timer to trigger position changed events nsCOMPtr mTimeUpdateTimer; // Timer to reset GonkAudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS. // It is triggered in Pause() and canceled when there is a Play() within // OFFLOAD_PAUSE_MAX_USECS. Used only from main thread so no lock is needed. nsCOMPtr mResetTimer; // To avoid device suspend when mResetTimer is going to be triggered. // Used only from main thread so no lock is needed. RefPtr mWakeLock; // Provide the playback position in microseconds from total number of // frames played by audio track int64_t GetOutputPlayPositionUs_l() const; // Fill the buffer given by audio sink with data from compressed audio // source. Also handles the seek by seeking audio source and stop the sink in // case of error size_t FillBuffer(void *aData, size_t aSize); // Called by GonkAudioSink when it needs data, to notify EOS or tear down event static size_t AudioSinkCallback(GonkAudioSink *aAudioSink, void *aData, size_t aSize, void *aMe, GonkAudioSink::cb_event_t aEvent); bool IsSeeking(); // Set mSeekTarget to the given position and restart the sink. Actual seek // happens in FillBuffer(). If mSeekPromise is not empty, send // SeekingStarted event always and SeekingStopped event when the play state is // paused to MediaDecoder. // When decoding and playing happens separately, if there is a seek during // pause, we can decode and keep data ready. // In case of offload player, no way to seek during pause. So just fake that // seek is done. status_t DoSeek(); // Start/Resume the audio sink so that callback will start being called to get // compressed data status_t Play(); // Stop the audio sink if we need to play till we drain the current buffer. // or Pause the sink in case we should stop playing immediately void Pause(bool aPlayPendingSamples = false); // When audio is offloaded, application processor wakes up less frequently // (>1sec) But when Player UI is visible we need to update progress bar // atleast once in 250ms. Start a timer when player UI becomes visible or // audio starts playing to send UpdateLogicalPosition events once in 250ms. // Stop the timer when UI goes invisible or play state is not playing. // Also make sure timer functions are always called from main thread nsresult StartTimeUpdate(); nsresult StopTimeUpdate(); void WakeLockCreate(); void WakeLockRelease(); // Notify end of stream by sending PlaybackEnded event to observer // (i.e.MediaDecoder) void NotifyAudioEOS(); // Notify position changed event by sending UpdateLogicalPosition event to // observer void NotifyPositionChanged(); // Offloaded audio track is invalidated due to usecase change. Notify // MediaDecoder to re-evaluate offloading options void NotifyAudioTearDown(); // Send information from MetaData to the HAL via GonkAudioSink void SendMetaDataToHal(android::sp& aSink, const android::sp& aMeta); AudioOffloadPlayer(const AudioOffloadPlayer &); AudioOffloadPlayer &operator=(const AudioOffloadPlayer &); }; } // namespace mozilla #endif // AUDIO_OFFLOAD_PLAYER_H_