最近接觸到的一個項目, 有音頻播放、切換播放速率和拖動進度到某處播放的需求 ,由於之前只是見過並沒有嘗試過切換播放速率 , 於是開始調研並最終實現,下面簡單記錄一下這次的調研過程。
- MediaPlayer
播放音頻最先想到的就是MediaPlayer這個Android提供的原生API了,在Android 6.0+(23+)MediaPlayer可以通過setSpeed來改變播放速率
在代碼中,我們需要:
// 設置音樂播放速度 public static void changeplayerSpeed(float speed) { if (mPlayer == null) { return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23 (6.0)以上 ,通過設置Speed改變音樂的播放速率 if (mPlayer.isPlaying()) { // 判斷是否正在播放,未播放時,要在設置Speed后,暫停音樂播放 mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(speed)); } else { mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed(speed)); mPlayer.pause(); } } else { // 在Android6.0以前,需要另想辦法處理,后續查到好的方法再補充 } }
實際實現過程中 ,我發現手上的測試機Honor V9執行該操作后 ,播放靜默了 ,不僅沒有實現播放速率的切換,播放也不能恢復。無奈,只好另尋他法。
- PLMediaPlayer
PLDroidPlayer是七牛SDK提供的一套API, PLMediaPlayer
實現了一個媒體播放器的各種基礎功能和接口,與 Android 官方的 MediaPlayer
的設計基本保持一致。
關鍵代碼就一行
private PLOnPreparedListener mOnPreparedListener = new PLOnPreparedListener() { @Override public void onPrepared(int preparedTime) { Log.i(TAG, "On Prepared !"); mMediaPlayer.start();
//設置播放速率為2x mMediaPlayer.setPlaySpeed(2.0f); mIsStopped = false; } };
實際實現過程中 ,播放速率切換正常,但seekTo操作大概率失效,於是去github上查探究竟,發現仍存在該問題的ISSUE,遂放棄。
- ijkPlayer
ijkplayer是b站基於ffplay的輕量級Android/iOS視頻播放器,實現了跨平台的功能,API易於集成;編譯配置可裁剪,方便控制安裝包大小。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1);
ijkMediaPlayer.setSpeed(2.0f);
api和用法類似mediaplayer,關鍵代碼也是只有一行。
實際實現過程中 ,seekTo正常,播放速率切換也正常(只是在切換到慢速0.5x的時候存在重音的情況),但是播放不了https開頭url的音頻文件,搜索了一下需要自己編譯ijkplayer源碼以支持https,遂放棄。
- ExoPlayer
最終選擇的是google的exoPlayer來實現,api類似MediaPlayer,但也有些差異,下面貼出關鍵播放控制部分的代碼。
package com.weex.app.media; import android.content.Context; import android.net.Uri; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Util; import java.io.File; public class AudioPlayerManager { private static final String TAG = "AudioPlayerManager"; public static Float[] speedArray = new Float[]{1.0f, 1.25f, 1.75f, 0.5f, 0.75f}; private static AudioPlayerManager instance; private Context context; private SimpleExoPlayer mediaPlayer; private DataSource.Factory dataSourceFactory; private AudioPlayerManager(Context context) { this.context = context; createPlayer(); } public static AudioPlayerManager getInstance(Context context) { synchronized (AudioPlayerManager.class) { if (instance == null) { instance = new AudioPlayerManager(context); } } return instance; } public ExoPlayer getMediaPlayer() { return mediaPlayer; } //設置播放url public void setAudioUrl(String audioUrl) { try { //這是一個代表將要被播放的媒體的MediaSource MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.parse(audioUrl)); mediaPlayer.prepare(mediaSource); mediaPlayer.setPlayWhenReady(false); } catch (Exception e) { e.printStackTrace(); } } //設置播放file public void setAudioFile(File file) { try { //這是一個代表將要被播放的媒體的MediaSource MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.fromFile(file)); mediaPlayer.prepare(mediaSource); mediaPlayer.setPlayWhenReady(false); } catch (Exception e) { e.printStackTrace(); } } //開始播放 public void start() { mediaPlayer.setPlayWhenReady(true); } //判斷是否是播放狀態 public boolean isPlaying() { int playbackState = mediaPlayer.getPlaybackState(); if (playbackState == SimpleExoPlayer.STATE_READY && mediaPlayer.getPlayWhenReady()) { return true; } return false; } //播放,帶回調事件 public void playWithCompletionListener(String url, Player.EventListener listener) { if (listener != null) { mediaPlayer.addListener(listener); } if (url.startsWith("http")) { setAudioUrl(url); } else { setAudioFile(new File(url)); } mediaPlayer.setPlayWhenReady(true); } //播放or暫停 public void playOrPause() { if (isPlaying()) { mediaPlayer.setPlayWhenReady(false); } else { mediaPlayer.setPlayWhenReady(true); } } //切換播放速率 public void switchSpeed(int speedIndex) { // API 23 (6.0)以上 ,通過設置Speed改變音樂的播放速率 if (isPlaying()) { // 判斷是否正在播放,未播放時,要在設置Speed后,暫停音樂播放 getMediaPlayer().setPlaybackParameters(new PlaybackParameters(speedArray[speedIndex])); } else { getMediaPlayer().setPlaybackParameters(new PlaybackParameters(speedArray[speedIndex])); getMediaPlayer().setPlayWhenReady(false); } } //停止播放 public void stop(boolean reset) { if (mediaPlayer != null) { mediaPlayer.stop(reset); } } //釋放資源 public void release() { if (mediaPlayer != null) { mediaPlayer.release(); } } //創建一個新的player private void createPlayer() { // 創建帶寬 BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); // 創建軌道選擇工廠 TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); // 創建軌道選擇器實例 TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); //step2.創建播放器 mediaPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector); //創建一個DataSource對象,通過它來下載多媒體數據 dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, "loader")); } }
實際自測過程中,表現正常,而且切換播放速率時,沒有重(chong)音的情況,但沒有在6.0以下的設備上測試過。