Android EasyPlayer聲音自動停止、恢復,一鍵靜音等功能
我們在開發播放器時,可能會需要靜音或者降低音量的功能。比如說某款音樂播放器,當在后臺播放時,如果此時有另外的系統通知聲音發出,可能播放器會把音量降低,系統聲音結束后,再調高;如果有來電了,播放器可能會把音樂暫停,等通話結束后再繼續播放。還有,比方說我們在某個場合放個視頻,不料音量很大,會引來很多目光(很尷尬),這時候可能我們需要一鍵靜音的功能。那這些功能我們應該如何實現呢?
Android播放聲音的類為AudioTrack,播放器會先把音頻流demux出來,再decode,之后,把音頻PCM數據通過AudioTrack類write到音頻設備中,從而通過話筒或者揚聲器發出聲音。
為了方便地實現聲音控制,我們需要從應用的最上層進行操作(因為底層可能已經被抽象成庫了),也就是要從AudioTrack來入手。讓我們看看AudioTrack的一些API吧。
int getPlayState () Returns the playback state of the AudioTrack instance. 獲取當前的播放狀態。這個接口會返回PLAYSTATE_STOPPED、PLAYSTATE_PAUSED、PLAYSTATE_PLAYING 三種狀態,分別表示未播放、暫停中、正在播放
void pause () Pauses the playback of the audio data. Data that has not been played back will not be discarded. Subsequent calls to play() will play this data back. See flush() to discard this data. 暫停播放音頻數據。已經在緩沖區中的未播放數據將不會被丟棄,在下次play的時候繼續播放。調用flush則會丟棄緩沖數據。
void play () Starts playing an AudioTrack. 開始播放
int setStereoVolume (float leftGain,
float rightGain)
Sets the specified left and right output gain values on the AudioTrack.
設置左右聲道的音量增益。
有了這幾個API,足以滿足我們的需求。實現起來就非常簡單了。
首先我們做一鍵靜音功能。我們可以做個切換的按鈕,這個按鈕初始狀態是要顯示當前的播放狀態:正在播放音頻或未在播放音頻。播放狀態可以調用getPlayState ()來獲取到;然后按鈕按下后,再根據播放狀態進行播放或暫停。
代碼如下:
mAudioEnable = mAudioTrack!=null && mAudioTrack.getPlayState()==PLAYSTATE_PLAYING;
public void setAudioEnable(boolean enable) {
mAudioEnable = enable;
AudioTrack at = mAudioTrack;
if (at != null) {
synchronized (at) {
if (!enable) {
at.pause();
at.flush();
} else {
at.flush();
at.play();
}
}
}
}
注意這里在pause之后,play之前都調用了flush接口。這樣可以確保在由暫停到播放切換時,不會把暫停時未播放的“舊數據”播放出來。
接下來我們實現音頻資源被其它進程占用(失去焦點)時,自動降低聲音或者停止聲音;在音頻資源又被釋放(重新獲取到焦點)時再恢復播放的功能。
我們需要通過AudioManager來判斷當前音頻資源的狀態,并且在音頻焦點更改時得到回調。其關鍵API接口有:
int requestAudioFocus (AudioManager.OnAudioFocusChangeListener l,
int streamType,
int durationHint)
Request audio focus. Send a request to obtain the audio focus
請求獲取音頻焦點。
第一個參數為音頻焦點更改時的回調;
第二個參數為音頻類型,在我們調節音量時可以看到有若干種音量,就對應的這里的streamType,這里我們基本用MUSIC,表示“媒體”。
第三個參數表示獲取焦點的“時長”,有如下幾種情況:
AUDIOFOCUS_GAIN_TRANSIENT
表示僅僅為臨時獲取焦點。比如播放導航語音、通知聲音等,屬于時間很短暫的情況;
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
表示為DUCK模式,表示當獲取焦點后,允許先前獲取過焦點的程序在降低輸出音量的前提下繼續播放。
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
痛第一種情況類似,只是不允許系統再播放其他聲音。通常應用在語音備忘、語音識別等情況;
AUDIOFOCUS_GAIN
表示要獲取焦點的時長未知。比如播放音樂等等。
當獲取到焦點時,函數放回AUDIOFOCUS_REQUEST_GRANTED,當獲取失敗時,返回AUDIOFOCUS_REQUEST_FAILED
結合上面的API說明,參考如下代碼以及解釋:
// 獲取AudioManager實例
final AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener l = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {// 焦點獲取到了,那繼續播放,并恢復音量。
AudioTrack audioTrack = mAudioTrack;
if (audioTrack != null) {
audioTrack.setStereoVolume(1.0f, 1.0f);
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
audioTrack.flush();
audioTrack.play();
}
}
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {// 焦點丟失了,暫停播放。
AudioTrack audioTrack = mAudioTrack;
if (audioTrack != null) {
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
audioTrack.pause();
}
}
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // 焦點丟失了,但是允許在降低音量的前提下繼續播放,那么降低聲音。
AudioTrack audioTrack = mAudioTrack;
if (audioTrack != null) {
audioTrack.setStereoVolume(0.5f, 0.5f);
}
}
}
};
// 因為這里要獲得的焦點無法預知時長,因此用AUDIOFOCUS_GAIN模式。
int requestCode = am.requestAudioFocus(l, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (requestCode == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 成功獲取到了焦點。那啟動播放
AudioTrack audioTrack = mAudioTrack;
if (audioTrack != null) {
audioTrack.setStereoVolume(1.0f, 1.0f);
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
audioTrack.flush();
audioTrack.play();
}
}
}else{ // 沒有獲取到音頻焦點。那不播放聲音
AudioTrack audioTrack = mAudioTrack;
if (audioTrack != null) {
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
audioTrack.pause();
}
}
}
至此,我們便實現了EasyPlayer的聲音自動停止、恢復,一鍵靜音的功能的實現??雌饋硗β闊??其實做一個app很容易,但是要想做的好,各種情況都兼顧了,卻是很不容易的。我們不防多看些系統APP的實現,或者Google官方的一些DEMO,它們往往都看似功能很簡單,會讓我們覺得:“如果是我做的話,幾行代碼即可搞定。?!?,但是它們的代碼量卻很大,因為它們兼顧了各種細節。而往往我們開發出來絕大多數app的都只能算是半成品,都有繼續優化的余地。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。