使用exoPlayer来播放音频

pull/1295/head
gedoor 3 years ago
parent fbfc263ef9
commit 1b3dfd6c9f
  1. 9
      app/build.gradle
  2. 31
      app/src/main/java/io/legado/app/help/ExoPlayerHelper.kt
  3. 2
      app/src/main/java/io/legado/app/help/MediaHelp.kt
  4. 105
      app/src/main/java/io/legado/app/service/AudioPlayService.kt

@ -152,8 +152,13 @@ dependencies {
def media2_version = "1.1.3" def media2_version = "1.1.3"
implementation("androidx.media2:media2-session:$media2_version") implementation("androidx.media2:media2-session:$media2_version")
implementation("androidx.media:media:1.4.0") implementation("androidx.media:media:1.4.0")
//implementation "androidx.media2:media2-player:$media2_version" def exoplayer_version = '2.15.0'
//implementation 'com.google.android.exoplayer:exoplayer:2.13.0' implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:extension-okhttp:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-rtsp:$exoplayer_version"
//Splitties //Splitties
def splitties_version = '2.1.1' def splitties_version = '2.1.1'

@ -0,0 +1,31 @@
package io.legado.app.help
import android.net.Uri
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.dash.DashMediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource
import com.google.android.exoplayer2.util.Util.inferContentType
import io.legado.app.help.http.okHttpClient
object ExoPlayerHelper {
fun createMediaSource(uri: Uri, overrideExtension: String? = null): MediaSource {
val mediaItem = MediaItem.fromUri(uri)
val dataSourceFactory = OkHttpDataSource.Factory(okHttpClient)
val mediaSourceFactory = when (inferContentType(uri, overrideExtension)) {
C.TYPE_SS -> SsMediaSource.Factory(dataSourceFactory)
C.TYPE_DASH -> DashMediaSource.Factory(dataSourceFactory)
C.TYPE_HLS -> HlsMediaSource.Factory(dataSourceFactory)
else -> ProgressiveMediaSource.Factory(dataSourceFactory)
}
return mediaSourceFactory.createMediaSource(mediaItem)
}
}

@ -32,7 +32,7 @@ object MediaHelp {
or PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE or PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
or PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED) or PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED)
fun getFocusRequest(audioFocusChangeListener: AudioManager.OnAudioFocusChangeListener): AudioFocusRequestCompat? { fun getFocusRequest(audioFocusChangeListener: AudioManager.OnAudioFocusChangeListener): AudioFocusRequestCompat {
val mPlaybackAttributes = AudioAttributesCompat.Builder() val mPlaybackAttributes = AudioAttributesCompat.Builder()
.setUsage(AudioAttributesCompat.USAGE_MEDIA) .setUsage(AudioAttributesCompat.USAGE_MEDIA)
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC) .setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)

@ -8,13 +8,16 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.AudioManager import android.media.AudioManager
import android.media.MediaPlayer
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat import android.support.v4.media.session.PlaybackStateCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.media.AudioFocusRequestCompat import androidx.media.AudioFocusRequestCompat
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
@ -23,6 +26,7 @@ import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.ExoPlayerHelper
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.help.MediaHelp import io.legado.app.help.MediaHelp
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -39,9 +43,7 @@ import kotlinx.coroutines.Dispatchers.Main
class AudioPlayService : BaseService(), class AudioPlayService : BaseService(),
AudioManager.OnAudioFocusChangeListener, AudioManager.OnAudioFocusChangeListener,
MediaPlayer.OnPreparedListener, Player.Listener {
MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
companion object { companion object {
var isRun = false var isRun = false
@ -50,10 +52,10 @@ class AudioPlayService : BaseService(),
} }
private lateinit var audioManager: AudioManager private lateinit var audioManager: AudioManager
private var mFocusRequest: AudioFocusRequestCompat? = null private lateinit var mFocusRequest: AudioFocusRequestCompat
private lateinit var exoPlayer: ExoPlayer
private var title: String = "" private var title: String = ""
private var subtitle: String = "" private var subtitle: String = ""
private val mediaPlayer = MediaPlayer()
private var mediaSessionCompat: MediaSessionCompat? = null private var mediaSessionCompat: MediaSessionCompat? = null
private var broadcastReceiver: BroadcastReceiver? = null private var broadcastReceiver: BroadcastReceiver? = null
private var url: String = "" private var url: String = ""
@ -68,9 +70,8 @@ class AudioPlayService : BaseService(),
upNotification() upNotification()
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
mFocusRequest = MediaHelp.getFocusRequest(this) mFocusRequest = MediaHelp.getFocusRequest(this)
mediaPlayer.setOnErrorListener(this) exoPlayer = SimpleExoPlayer.Builder(this).build()
mediaPlayer.setOnPreparedListener(this) exoPlayer.addListener(this)
mediaPlayer.setOnCompletionListener(this)
initMediaSession() initMediaSession()
initBroadcastReceiver() initBroadcastReceiver()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING) upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
@ -106,7 +107,7 @@ class AudioPlayService : BaseService(),
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
isRun = false isRun = false
mediaPlayer.release() exoPlayer.release()
mediaSessionCompat?.release() mediaSessionCompat?.release()
unregisterReceiver(broadcastReceiver) unregisterReceiver(broadcastReceiver)
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_STOPPED) upMediaSessionPlaybackState(PlaybackStateCompat.STATE_STOPPED)
@ -120,22 +121,22 @@ class AudioPlayService : BaseService(),
kotlin.runCatching { kotlin.runCatching {
AudioPlay.status = Status.STOP AudioPlay.status = Status.STOP
postEvent(EventBus.AUDIO_STATE, Status.STOP) postEvent(EventBus.AUDIO_STATE, Status.STOP)
mediaPlayer.reset() upPlayProgressJob?.cancel()
exoPlayer.clearMediaItems()
val analyzeUrl = val analyzeUrl =
AnalyzeUrl(url, headerMapF = AudioPlay.headers(), useWebView = true) AnalyzeUrl(url, headerMapF = AudioPlay.headers(), useWebView = true)
val uri = Uri.parse(analyzeUrl.url) val uri = Uri.parse(analyzeUrl.url)
mediaPlayer.setDataSource(this, uri, analyzeUrl.headerMap) val mediaSource = ExoPlayerHelper.createMediaSource(uri)
mediaPlayer.prepareAsync() exoPlayer.setMediaSource(mediaSource)
upPlayProgressJob?.cancel() exoPlayer.playWhenReady = true
exoPlayer.prepare()
}.onFailure { }.onFailure {
it.printStackTrace() it.printStackTrace()
launch {
toastOnUi("$url ${it.localizedMessage}") toastOnUi("$url ${it.localizedMessage}")
stopSelf() stopSelf()
} }
} }
} }
}
private fun pause(pause: Boolean) { private fun pause(pause: Boolean) {
if (url.contains(".m3u8", false)) { if (url.contains(".m3u8", false)) {
@ -144,8 +145,8 @@ class AudioPlayService : BaseService(),
try { try {
AudioPlayService.pause = pause AudioPlayService.pause = pause
upPlayProgressJob?.cancel() upPlayProgressJob?.cancel()
position = mediaPlayer.currentPosition position = exoPlayer.currentPosition.toInt()
if (mediaPlayer.isPlaying) mediaPlayer.pause() if (exoPlayer.isPlaying) exoPlayer.pause()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PAUSED) upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PAUSED)
AudioPlay.status = Status.PAUSE AudioPlay.status = Status.PAUSE
postEvent(EventBus.AUDIO_STATE, Status.PAUSE) postEvent(EventBus.AUDIO_STATE, Status.PAUSE)
@ -159,9 +160,9 @@ class AudioPlayService : BaseService(),
private fun resume() { private fun resume() {
try { try {
pause = false pause = false
if (!mediaPlayer.isPlaying) { if (!exoPlayer.isPlaying) {
mediaPlayer.start() exoPlayer.play()
mediaPlayer.seekTo(position) exoPlayer.seekTo(position.toLong())
} }
upPlayProgress() upPlayProgress()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING) upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
@ -175,8 +176,8 @@ class AudioPlayService : BaseService(),
} }
private fun adjustProgress(position: Int) { private fun adjustProgress(position: Int) {
if (mediaPlayer.isPlaying) { if (exoPlayer.isPlaying) {
mediaPlayer.seekTo(position) exoPlayer.seekTo(position.toLong())
} else { } else {
this.position = position this.position = position
} }
@ -186,50 +187,42 @@ class AudioPlayService : BaseService(),
kotlin.runCatching { kotlin.runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
playSpeed += adjust playSpeed += adjust
if (mediaPlayer.isPlaying) { exoPlayer.setPlaybackSpeed(playSpeed)
mediaPlayer.playbackParams =
mediaPlayer.playbackParams.apply { speed = playSpeed }
}
postEvent(EventBus.AUDIO_SPEED, playSpeed) postEvent(EventBus.AUDIO_SPEED, playSpeed)
} }
} }
} }
/** override fun onPlaybackStateChanged(playbackState: Int) {
* 加载完成 super.onPlaybackStateChanged(playbackState)
*/ when (playbackState) {
override fun onPrepared(mp: MediaPlayer) { Player.STATE_IDLE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 空闲
mediaPlayer.playbackParams = mediaPlayer.playbackParams.apply { speed = playSpeed } }
} else { Player.STATE_BUFFERING -> {
mediaPlayer.start() // 缓冲中
} }
mediaPlayer.seekTo(position) Player.STATE_READY -> {
// 准备好
AudioPlay.status = Status.PLAY AudioPlay.status = Status.PLAY
postEvent(EventBus.AUDIO_STATE, Status.PLAY) postEvent(EventBus.AUDIO_STATE, Status.PLAY)
postEvent(EventBus.AUDIO_SIZE, mediaPlayer.duration) postEvent(EventBus.AUDIO_SIZE, exoPlayer.duration)
upPlayProgress() upPlayProgress()
AudioPlay.saveDurChapter(mediaPlayer.duration.toLong()) AudioPlay.saveDurChapter(exoPlayer.duration)
}
Player.STATE_ENDED -> {
// 结束
upPlayProgressJob?.cancel()
AudioPlay.next(this)
} }
/**
* 播放出错
*/
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
if (!mediaPlayer.isPlaying) {
AudioPlay.status = Status.STOP
postEvent(EventBus.AUDIO_STATE, Status.STOP)
launch { toastOnUi("error: $what $extra $url") }
} }
return true
} }
/** override fun onPlayerError(error: PlaybackException) {
* 播放结束 super.onPlayerError(error)
*/ AudioPlay.status = Status.STOP
override fun onCompletion(mp: MediaPlayer) { postEvent(EventBus.AUDIO_STATE, Status.STOP)
upPlayProgressJob?.cancel() toastOnUi("error: ${error.errorCode} ${error.errorCodeName} $url")
AudioPlay.next(this)
} }
private fun setTimer(minute: Int) { private fun setTimer(minute: Int) {
@ -279,7 +272,7 @@ class AudioPlayService : BaseService(),
upPlayProgressJob = launch { upPlayProgressJob = launch {
while (isActive) { while (isActive) {
saveProgress() saveProgress()
postEvent(EventBus.AUDIO_PROGRESS, mediaPlayer.currentPosition) postEvent(EventBus.AUDIO_PROGRESS, exoPlayer.currentPosition)
delay(1000) delay(1000)
} }
} }
@ -341,7 +334,7 @@ class AudioPlayService : BaseService(),
private fun saveProgress() { private fun saveProgress() {
execute { execute {
AudioPlay.book?.let { AudioPlay.book?.let {
AudioPlay.durChapterPos = mediaPlayer.currentPosition AudioPlay.durChapterPos = exoPlayer.currentPosition.toInt()
appDb.bookDao.upProgress(it.bookUrl, AudioPlay.durChapterPos) appDb.bookDao.upProgress(it.bookUrl, AudioPlay.durChapterPos)
} }
} }

Loading…
Cancel
Save