michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* michael@0: * Copyright (c) 2014 The Linux Foundation. All rights reserved. michael@0: * Copyright (C) 2009 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #ifndef AUDIO_OFFLOAD_PLAYER_H_ michael@0: #define AUDIO_OFFLOAD_PLAYER_H_ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "AudioOutput.h" michael@0: michael@0: #include "MediaDecoderOwner.h" michael@0: #include "MediaOmxDecoder.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: class MediaOmxDecoder; michael@0: michael@0: /** michael@0: * AudioOffloadPlayer adds support for audio tunneling to a digital signal michael@0: * processor (DSP) in the device chipset. With tunneling, audio decoding is michael@0: * off-loaded to the DSP, waking the application processor less often and using michael@0: * less battery michael@0: * michael@0: * This depends on offloading capability provided by Android KK AudioTrack class michael@0: * michael@0: * Audio playback is based on pull mechanism, whenever audio sink needs michael@0: * data, FillBuffer() will read data from compressed audio source and provide michael@0: * it to the sink michael@0: * michael@0: * Also this class passes state changes (play/pause/seek) from MediaOmxDecoder michael@0: * to AudioSink as well as provide AudioSink status (position changed, michael@0: * playback ended, seek complete, audio tear down) back to MediaOmxDecoder michael@0: * michael@0: * It acts as a bridge between MediaOmxDecoder and AudioSink during michael@0: * offload playback michael@0: */ michael@0: michael@0: class AudioOffloadPlayer : public AudioOffloadPlayerBase michael@0: { michael@0: typedef android::Mutex Mutex; michael@0: typedef android::MetaData MetaData; michael@0: typedef android::status_t status_t; michael@0: typedef android::AudioTrack AudioTrack; michael@0: typedef android::MediaBuffer MediaBuffer; michael@0: typedef android::MediaSource MediaSource; michael@0: michael@0: public: michael@0: enum { michael@0: REACHED_EOS, michael@0: SEEK_COMPLETE michael@0: }; michael@0: michael@0: AudioOffloadPlayer(MediaOmxDecoder* aDecoder = nullptr); michael@0: michael@0: ~AudioOffloadPlayer(); michael@0: michael@0: // Caller retains ownership of "aSource". michael@0: void SetSource(const android::sp &aSource); michael@0: michael@0: // Start the source if it's not already started and open the AudioSink to michael@0: // create an offloaded audio track michael@0: status_t Start(bool aSourceAlreadyStarted = false); michael@0: michael@0: double GetMediaTimeSecs(); michael@0: michael@0: // To update progress bar when the element is visible michael@0: void SetElementVisibility(bool aIsVisible); michael@0: michael@0: status_t ChangeState(MediaDecoder::PlayState aState); michael@0: michael@0: void SetVolume(double aVolume); michael@0: michael@0: // Update ready state based on current play state. Not checking data michael@0: // availability since offloading is currently done only when whole compressed michael@0: // data is available michael@0: MediaDecoderOwner::NextFrameStatus GetNextFrameStatus(); michael@0: michael@0: void TimeUpdate(); michael@0: michael@0: // Close the audio sink, stop time updates, frees the input buffers michael@0: void Reset(); michael@0: michael@0: private: michael@0: // Set when audio source is started and audioSink is initialized michael@0: // Used only in main thread michael@0: bool mStarted; michael@0: michael@0: // Set when audio sink is started. i.e. playback started michael@0: // Used only in main thread michael@0: bool mPlaying; michael@0: michael@0: // Set when playstate is seeking and reset when FillBUffer() acknowledged michael@0: // seeking by seeking audio source. Used in main thread and offload michael@0: // callback thread, protected by Mutex mLock michael@0: bool mSeeking; michael@0: michael@0: // Once playback reached end of stream (last ~100ms), position provided by DSP michael@0: // may be reset/corrupted. This bool is used to avoid that. michael@0: // Used in main thread and offload callback thread, protected by Mutex michael@0: // mLock michael@0: bool mReachedEOS; michael@0: michael@0: // Set when there is a seek request during pause. michael@0: // Used in main thread and offload callback thread, protected by Mutex michael@0: // mLock michael@0: bool mSeekDuringPause; michael@0: michael@0: // Seek can be triggered internally or by MediaDecoder. This bool is to michael@0: // to track seek triggered by MediaDecoder so that we can send back michael@0: // SeekingStarted and SeekingStopped events. michael@0: // Used in main thread and offload callback thread, protected by Mutex mLock michael@0: bool mDispatchSeekEvents; michael@0: michael@0: // Set when the HTML Audio Element is visible to the user. michael@0: // Used only in main thread michael@0: bool mIsElementVisible; michael@0: michael@0: // Session id given by Android::AudioSystem and used while creating audio sink michael@0: // Used only in main thread michael@0: int mSessionId; michael@0: michael@0: // Sample rate of current audio track. Used only in main thread michael@0: int mSampleRate; michael@0: michael@0: // After seeking, positions returned by offlaoded tracks (DSP) will be michael@0: // relative to the seeked position. And seeked position may be slightly michael@0: // different than given mSeekTimeUs, if audio source cannot find a frame at michael@0: // that position. Store seeked position in mStartPosUs and provide michael@0: // mStartPosUs + GetPosition() (i.e. absolute position) to MediaOmxDecoder michael@0: // Used in main thread and offload callback thread, protected by Mutex michael@0: // mLock michael@0: int64_t mStartPosUs; michael@0: michael@0: // Given seek time when there is a request to seek michael@0: // Used in main thread and offload callback thread, protected by Mutex michael@0: // mLock michael@0: int64_t mSeekTimeUs; michael@0: michael@0: // Positions obtained from offlaoded tracks (DSP) michael@0: // Used in main thread and offload callback thread, protected by Mutex michael@0: // mLock michael@0: int64_t mPositionTimeMediaUs; michael@0: michael@0: // State obtained from MediaOmxDecoder. Used only in main thread michael@0: MediaDecoder::PlayState mPlayState; michael@0: michael@0: // Protect accessing audio position related variables between main thread and michael@0: // offload callback thread michael@0: Mutex mLock; michael@0: michael@0: // Compressed audio source. michael@0: // Used in main thread first and later in offload callback thread michael@0: android::sp mSource; michael@0: michael@0: // Audio sink wrapper to access offloaded audio tracks michael@0: // Used in main thread and offload callback thread michael@0: // Race conditions are protected in underlying Android::AudioTrack class michael@0: android::sp mAudioSink; michael@0: michael@0: // Buffer used to get date from audio source. Used in offload callback thread michael@0: MediaBuffer* mInputBuffer; michael@0: michael@0: // MediaOmxDecoder object used mainly to notify the audio sink status michael@0: MediaOmxDecoder* mObserver; michael@0: michael@0: TimeStamp mLastFireUpdateTime; michael@0: michael@0: // Timer to trigger position changed events michael@0: nsCOMPtr mTimeUpdateTimer; michael@0: michael@0: // Timer to reset AudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS. michael@0: // It is triggered in Pause() and canceled when there is a Play() within michael@0: // OFFLOAD_PAUSE_MAX_USECS. Used only from main thread so no lock is needed. michael@0: nsCOMPtr mResetTimer; michael@0: michael@0: int64_t GetMediaTimeUs(); michael@0: michael@0: // Provide the playback position in microseconds from total number of michael@0: // frames played by audio track michael@0: int64_t GetOutputPlayPositionUs_l() const; michael@0: michael@0: // Fill the buffer given by audio sink with data from compressed audio michael@0: // source. Also handles the seek by seeking audio source and stop the sink in michael@0: // case of error michael@0: size_t FillBuffer(void *aData, size_t aSize); michael@0: michael@0: // Called by AudioSink when it needs data, to notify EOS or tear down event michael@0: static size_t AudioSinkCallback(AudioSink *aAudioSink, michael@0: void *aData, michael@0: size_t aSize, michael@0: void *aMe, michael@0: AudioSink::cb_event_t aEvent); michael@0: michael@0: bool IsSeeking(); michael@0: michael@0: // Set mSeekTime to the given position and restart the sink. Actual seek michael@0: // happens in FillBuffer(). If aDispatchSeekEvents is true, send michael@0: // SeekingStarted event always and SeekingStopped event when the play state is michael@0: // paused to MediaDecoder. michael@0: // When decoding and playing happens separately, if there is a seek during michael@0: // pause, we can decode and keep data ready. michael@0: // In case of offload player, no way to seek during pause. So just fake that michael@0: // seek is done. michael@0: status_t SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents = false); michael@0: michael@0: // Start/Resume the audio sink so that callback will start being called to get michael@0: // compressed data michael@0: status_t Play(); michael@0: michael@0: // Stop the audio sink if we need to play till we drain the current buffer. michael@0: // or Pause the sink in case we should stop playing immediately michael@0: void Pause(bool aPlayPendingSamples = false); michael@0: michael@0: // When audio is offloaded, application processor wakes up less frequently michael@0: // (>1sec) But when Player UI is visible we need to update progress bar michael@0: // atleast once in 250ms. Start a timer when player UI becomes visible or michael@0: // audio starts playing to send PlaybackPositionChanged events once in 250ms. michael@0: // Stop the timer when UI goes invisible or play state is not playing. michael@0: // Also make sure timer functions are always called from main thread michael@0: nsresult StartTimeUpdate(); michael@0: nsresult StopTimeUpdate(); michael@0: michael@0: // Notify end of stream by sending PlaybackEnded event to observer michael@0: // (i.e.MediaDecoder) michael@0: void NotifyAudioEOS(); michael@0: michael@0: // Notify position changed event by sending PlaybackPositionChanged event to michael@0: // observer michael@0: void NotifyPositionChanged(); michael@0: michael@0: // Offloaded audio track is invalidated due to usecase change. Notify michael@0: // MediaDecoder to re-evaluate offloading options michael@0: void NotifyAudioTearDown(); michael@0: michael@0: // Send information from MetaData to the HAL via AudioSink michael@0: void SendMetaDataToHal(android::sp& aSink, michael@0: const android::sp& aMeta); michael@0: michael@0: AudioOffloadPlayer(const AudioOffloadPlayer &); michael@0: AudioOffloadPlayer &operator=(const AudioOffloadPlayer &); michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif // AUDIO_OFFLOAD_PLAYER_H_