安卓开发,目前需要实现一个在安卓5.0系统下实现变速播放的功能。我们知道安卓播放器mediaPlayer实现变速播放功能的API是在安卓系统6.0以上推出的,所以在安卓5.0系统,安卓源码是没有实现变速播放的API,如果我们开发的时候,没有对系统进行判断,在6.0以上安卓系统开发出来的变速播放功能,安装到5.0系统时,调用没有的系统API,程序是会直接崩溃的。通过查看官网,我们了解到android官网的ExoPlayer所以我们有了如下解决方案:
// 设置音乐播放速度
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以前,需要另想办法处理,后续查到好的方法再补充
}
}
那么问题来了:在安卓5.0系统,我们该如何实现变速播放的功能呢?
Google为了适配低版本推出了ExoPlayer播放器
具体如何使用网络上也有不少很好的教程:ExoPlayer简单使用。这里的变速播放具体实现代码如下:
override fun setPlaySpeed(speed: Float) {
val playbackParameters = PlaybackParameters(speed)
player?.playbackParameters = playbackParameters
}
如果需要实现一个播放器功能,我们可以抽取出一个播放器的接口,然后通过实现这个接口的方法来统一包装成一个播放器,接口如下:
/**
* 播放器包装接口
* @author guotianhui
*/
public interface IPlayerWapper {
void prepareDataSource(String mediaSourece);
void setPlaySpeed(float speed);
void startPlay();
void pause();
void stop();
void resumePlay();
boolean isPlaying();
long getPlayerPosition();
void seekToPlayerPosition(long progress);
void releasePlayer();
}
然后我们首先只要在App的build.grade文件里面配置如下代码:
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.6'
因为我们只是需要实现变速播放功能,所以只需要导入核心代码包就行。
播放器具体实现代码如下:
/**
* 兼容安卓6.0以下系统的Exoplayer
* @author guotianhui
*/
class DifferentSpeedPlayer: IPlayerWapper {
private var player: ExoPlayer? = null
private constructor()
companion object {
private var INSTANCE: DifferentSpeedPlayer? = null
fun getSpeedPlayerInstance(): DifferentSpeedPlayer? {
if(INSTANCE == null){
synchronized(DifferentSpeedPlayer::class.java){
if(INSTANCE == null){
INSTANCE = DifferentSpeedPlayer()
}
}
}
return INSTANCE
}
}
override fun initPlayer(context: Context) {
if (player == null) {
player = ExoPlayerFactory.newSimpleInstance(context, DefaultRenderersFactory(context),
DefaultTrackSelector(), DefaultLoadControl())
}
}
override fun prepareDataSource(mediaPath: String?,sourceDuration: Long) {
if(ObjectUtils.isNotEmpty(mediaPath)) {
val uri = Uri.parse(mediaPath)
val mediaSource = buildMediaSource(uri,sourceDuration)
player?.prepare(mediaSource, false, true)
}
}
private fun buildMediaSource(uri: Uri,sourceDuration: Long): MediaSource {
val sourceFactory = DefaultHttpDataSourceFactory("exoplayer-codelab")
val mediaSource = ExtractorMediaSource.Factory(sourceFactory)
val createMediaSource = mediaSource.createMediaSource(uri)
createMediaSource.onSourceInfoRefreshed(sourceDuration,true)
return createMediaSource
}
override fun setPlaySpeed(speed: Float) {
val playbackParameters = PlaybackParameters(speed)
player?.playbackParameters = playbackParameters
}
override fun startPlay() {
player?.playWhenReady = true
Log.e(">>>>>>>>>>>>>>"," player?.duration:"+ player?.duration)
}
override fun pause() {
player?.playWhenReady = false
}
override fun resumePlay() {
}
override fun stop() {
player?.playWhenReady = false
}
override fun isPlaying(): Boolean {
return if(ObjectUtils.isNotEmpty(player)) {
player?.isPlayingAd!!
}else{
false
}
}
override fun isResumePlaying(): Boolean {
return false
}
override fun getPlayerPosition(): Long {
return (player?.contentPosition!! /1000)
}
override fun seekToPlayerPosition(progress: Long) {
player?.seekTo(progress * 1000)
}
override fun releasePlayer() {
if (player != null) {
player?.release()
player = null
}
}
}
查看Api发现,播放器的时间获取返回的都是毫秒值,需要自己除以1000得到的正确的播放时间。ExoPlayer的开源官网