朗读用exoPlayer

pull/1660/head
kunfei 3 years ago
parent 956810f3ff
commit a3424402d4
  1. 113
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt

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

Loading…
Cancel
Save