diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt index 4d3d84648..248530263 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -1,7 +1,11 @@ package io.legado.app.service import android.app.PendingIntent -import android.media.MediaPlayer +import android.net.Uri +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.MediaItem +import com.google.android.exoplayer2.PlaybackException +import com.google.android.exoplayer2.Player import io.legado.app.R import io.legado.app.constant.AppLog import io.legado.app.constant.AppPattern @@ -19,21 +23,21 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import okhttp3.Response import org.mozilla.javascript.WrappedException - import java.io.File -import java.io.FileDescriptor -import java.io.FileInputStream import java.net.ConnectException import java.net.SocketTimeoutException import java.util.* import javax.script.ScriptException +/** + * 在线朗读 + */ class HttpReadAloudService : BaseReadAloudService(), - MediaPlayer.OnPreparedListener, - MediaPlayer.OnErrorListener, - MediaPlayer.OnCompletionListener { + Player.Listener { - private val mediaPlayer = MediaPlayer() + private val exoPlayer: ExoPlayer by lazy { + ExoPlayer.Builder(this).build() + } private val ttsFolderPath: String by lazy { cacheDir.absolutePath + File.separator + "httpTTS" + File.separator } @@ -45,19 +49,16 @@ class HttpReadAloudService : BaseReadAloudService(), override fun onCreate() { super.onCreate() - mediaPlayer.setOnErrorListener(this) - mediaPlayer.setOnPreparedListener(this) - mediaPlayer.setOnCompletionListener(this) + exoPlayer.addListener(this) } override fun onDestroy() { super.onDestroy() task?.cancel() - mediaPlayer.release() + exoPlayer.release() } override fun newReadAloud(play: Boolean) { - mediaPlayer.reset() playingIndex = -1 super.newReadAloud(play) } @@ -77,7 +78,7 @@ class HttpReadAloudService : BaseReadAloudService(), } else { val file = getSpeakFileAsMd5(fileName) if (file.exists()) { - playAudio(FileInputStream(file).fd) + playAudio(file) } else { downloadAudio() } @@ -89,7 +90,7 @@ class HttpReadAloudService : BaseReadAloudService(), } override fun playStop() { - mediaPlayer.stop() + exoPlayer.stop() } private fun playNext() { @@ -118,8 +119,7 @@ class HttpReadAloudService : BaseReadAloudService(), if (hasSpeakFile(fileName)) { //已经下载好的语音缓存 if (index == nowSpeak) { val file = getSpeakFileAsMd5(fileName) - val fis = FileInputStream(file) - playAudio(fis.fd) + playAudio(file) } } else if (hasSpeakCache(fileName)) { //缓存文件还在,可能还没下载完 return@forEachIndexed @@ -160,9 +160,8 @@ class HttpReadAloudService : BaseReadAloudService(), val file = createSpeakFileAsMd5IfNotExist(fileName) file.writeBytes(bytes) removeSpeakCache(fileName) - val fis = FileInputStream(file) if (index == nowSpeak) { - playAudio(fis.fd) + playAudio(file) } } downloadErrorNo = 0 @@ -213,16 +212,17 @@ class HttpReadAloudService : BaseReadAloudService(), } @Synchronized - private fun playAudio(fd: FileDescriptor) { + private fun playAudio(file: File) { if (playingIndex != nowSpeak && requestFocus()) { - try { - mediaPlayer.reset() - mediaPlayer.setDataSource(fd) - mediaPlayer.prepareAsync() - playingIndex = nowSpeak - postEvent(EventBus.TTS_PROGRESS, readAloudNumber + 1) - } catch (e: Exception) { - e.printOnDebug() + launch { + kotlin.runCatching { + val mediaItem = MediaItem.fromUri(Uri.fromFile(file)) + exoPlayer.setMediaItem(mediaItem) + exoPlayer.playWhenReady = true + exoPlayer.prepare() + }.onFailure { + it.printOnDebug() + } } } } @@ -281,7 +281,7 @@ class HttpReadAloudService : BaseReadAloudService(), super.pauseReadAloud(pause) kotlin.runCatching { playIndexJob?.cancel() - mediaPlayer.pause() + exoPlayer.pause() } } @@ -291,7 +291,7 @@ class HttpReadAloudService : BaseReadAloudService(), if (playingIndex == -1) { play() } else { - mediaPlayer.start() + exoPlayer.play() upPlayPos() } } @@ -302,22 +302,22 @@ class HttpReadAloudService : BaseReadAloudService(), val textChapter = textChapter ?: return playIndexJob = launch { postEvent(EventBus.TTS_PROGRESS, readAloudNumber + 1) - if (mediaPlayer.duration <= 0) { + if (exoPlayer.duration <= 0) { return@launch } val speakTextLength = contentList[nowSpeak].length if (speakTextLength <= 0) { return@launch } - val sleep = mediaPlayer.duration / speakTextLength - val start = speakTextLength * mediaPlayer.currentPosition / mediaPlayer.duration + val sleep = exoPlayer.duration / speakTextLength + val start = speakTextLength * exoPlayer.currentPosition / exoPlayer.duration for (i in start..contentList[nowSpeak].length) { if (readAloudNumber + i > textChapter.getReadLength(pageIndex + 1)) { pageIndex++ ReadBook.moveToNextPage() postEvent(EventBus.TTS_PROGRESS, readAloudNumber + i) } - delay(sleep.toLong()) + delay(sleep) } } } @@ -327,39 +327,46 @@ class HttpReadAloudService : BaseReadAloudService(), */ override fun upSpeechRate(reset: Boolean) { task?.cancel() - mediaPlayer.stop() + exoPlayer.stop() playingIndex = -1 downloadAudio() } - override fun onPrepared(mp: MediaPlayer?) { - super.play() - if (pause) return - mediaPlayer.start() - upPlayPos() + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + when (playbackState) { + Player.STATE_IDLE -> { + // 空闲 + } + Player.STATE_BUFFERING -> { + // 缓冲中 + } + Player.STATE_READY -> { + // 准备好 + if (pause) return + exoPlayer.play() + upPlayPos() + } + Player.STATE_ENDED -> { + // 结束 + playErrorNo = 0 + playNext() + } + } } private var playErrorNo = 0 - override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean { - if (what == -38 && extra == 0) { - play() - return true - } - AppLog.put("朗读错误($what, $extra)\n${contentList[nowSpeak]}") + override fun onPlayerError(error: PlaybackException) { + super.onPlayerError(error) + AppLog.put("朗读错误\n${contentList[nowSpeak]}", error) playErrorNo++ if (playErrorNo >= 5) { - toastOnUi("朗读连续5次错误, 最后一次错误代码($what, $extra)") + toastOnUi("朗读连续5次错误, 最后一次错误代码(${error.localizedMessage})") ReadAloud.pause(this) } else { playNext() } - return true - } - - override fun onCompletion(mp: MediaPlayer?) { - playErrorNo = 0 - playNext() } override fun aloudServicePendingIntent(actionStr: String): PendingIntent? {