朗读用exoPlayer
This commit is contained in:
@@ -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? {
|
||||||
|
|||||||
Reference in New Issue
Block a user