diff --git a/app/build.gradle b/app/build.gradle index 499f569b5..2dd13c9ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -179,8 +179,15 @@ dependencies { //media implementation("androidx.media:media:1.6.0") - implementation("com.google.android.exoplayer:exoplayer-core:$exoplayer_version") - implementation("com.google.android.exoplayer:extension-okhttp:$exoplayer_version") + // For media playback using ExoPlayer + implementation "androidx.media3:media3-exoplayer:$media3_version" + // For loading data using the OkHttp network stack + implementation "androidx.media3:media3-datasource-okhttp:$media3_version" + // For exposing and controlling media sessions + //implementation "androidx.media3:media3-session:$media3_version" + +// implementation("com.google.android.exoplayer:exoplayer-core:$exoplayer_version") +// implementation("com.google.android.exoplayer:extension-okhttp:$exoplayer_version") //Splitties implementation("com.louiscad.splitties:splitties-appctx:$splitties_version") diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 36bb1052f..bd681356c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -239,7 +239,7 @@ } ## ExoPlayer 反射设置ua 保证该私有变量不被混淆 --keepclassmembers class com.google.android.exoplayer2.upstream.cache.CacheDataSource$Factory { +-keepclassmembers class androidx.media3.datasource.cache.CacheDataSource$Factory { *** upstreamDataSourceFactory; } ## ExoPlayer 如果还不能播放就取消注释这个 diff --git a/app/src/app/java/io/legado/app/lib/cronet/NewCallBack.kt b/app/src/app/java/io/legado/app/lib/cronet/NewCallBack.kt index abe02704c..e3159907f 100644 --- a/app/src/app/java/io/legado/app/lib/cronet/NewCallBack.kt +++ b/app/src/app/java/io/legado/app/lib/cronet/NewCallBack.kt @@ -1,9 +1,9 @@ package io.legado.app.lib.cronet +import android.annotation.SuppressLint import android.os.Build import androidx.annotation.Keep import androidx.annotation.RequiresApi -import io.legado.app.utils.DebugLog import okhttp3.Call import okhttp3.Request import okhttp3.Response @@ -12,6 +12,7 @@ import java.io.IOException import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit +@SuppressLint("ObsoleteSdkInt") @Keep @RequiresApi(api = Build.VERSION_CODES.N) class NewCallBack(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) { diff --git a/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt b/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt index af826d6b0..d8b0ea472 100644 --- a/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt +++ b/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt @@ -1,35 +1,79 @@ package io.legado.app.help.exoplayer +import android.annotation.SuppressLint +import android.content.Context import android.net.Uri -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.database.StandaloneDatabaseProvider -import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource -import com.google.android.exoplayer2.offline.DefaultDownloaderFactory -import com.google.android.exoplayer2.offline.DownloadRequest -import com.google.android.exoplayer2.offline.DownloaderFactory -import com.google.android.exoplayer2.source.MediaSource -import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.upstream.DataSource -import com.google.android.exoplayer2.upstream.FileDataSource -import com.google.android.exoplayer2.upstream.cache.* +import androidx.media3.common.MediaItem +import androidx.media3.database.StandaloneDatabaseProvider +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.FileDataSource +import androidx.media3.datasource.ResolvingDataSource +import androidx.media3.datasource.cache.Cache +import androidx.media3.datasource.cache.CacheDataSink +import androidx.media3.datasource.cache.CacheDataSource +import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor +import androidx.media3.datasource.cache.SimpleCache +import androidx.media3.datasource.okhttp.OkHttpDataSource +import androidx.media3.exoplayer.DefaultLoadControl +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.offline.DefaultDownloaderFactory +import androidx.media3.exoplayer.offline.DownloadRequest +import androidx.media3.exoplayer.offline.DownloaderFactory +import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken import io.legado.app.help.http.okHttpClient import okhttp3.CacheControl import splitties.init.appCtx import java.io.File +import java.lang.reflect.Type import java.util.concurrent.TimeUnit +@SuppressLint("UnsafeOptInUsageError") object ExoPlayerHelper { - fun createMediaSource( - uri: Uri, - defaultRequestProperties: Map - ): MediaSource { - val mediaItem = MediaItem.fromUri(uri) - val mediaSourceFactory = ProgressiveMediaSource.Factory( - cacheDataSourceFactory.setDefaultRequestProperties(defaultRequestProperties) + private const val SPLIT_TAG = "\uD83D\uDEA7" + + private val gson by lazy { + GsonBuilder().create() + } + private val mapType by lazy { + val type: Type = object : TypeToken?>() {}.type + type + } + +// fun createMediaSource( +// uri: Uri, +// defaultRequestProperties: Map +// ): MediaSource { +// val mediaItem = MediaItem.fromUri(uri) +// val mediaSourceFactory = ProgressiveMediaSource.Factory( +// cacheDataSourceFactory.setDefaultRequestProperties(defaultRequestProperties) +// ) +// return mediaSourceFactory.createMediaSource(mediaItem) +// } + + fun createMediaItem(url: String, headers: Map): MediaItem { + val formatUrl = url + SPLIT_TAG + gson.toJson(headers, mapType) + return MediaItem.Builder().setUri(formatUrl).build() + } + + fun createExoPlayer(context: Context): ExoPlayer { + return ExoPlayer.Builder(context).setLoadControl( + DefaultLoadControl.Builder().setBufferDurationsMs( + DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS / 10, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS / 10 + ).build() + ) - return mediaSourceFactory.createMediaSource(mediaItem) + .setMediaSourceFactory( + DefaultMediaSourceFactory(context) + .setDataSourceFactory(resolvingDataSource) + .setLiveTargetOffsetMs(5000) + ).build() } /** @@ -62,6 +106,31 @@ object ExoPlayerHelper { } + private val resolvingDataSource: ResolvingDataSource.Factory by lazy { + ResolvingDataSource.Factory( + cacheDataSourceFactory + ) { + var res = it + + if (it.uri.toString().contains(SPLIT_TAG)) { + val urls = it.uri.toString().split(SPLIT_TAG) + val url = urls[0] + res = res.withUri(Uri.parse(url)) + try { + val headers: Map = gson.fromJson(urls[1], mapType) + res = res.withAdditionalHeaders(headers) + } catch (_: Exception) { + } + } + + + + res + + } + } + + /** * 支持缓存的DataSource.Factory */ diff --git a/app/src/main/java/io/legado/app/service/AudioPlayService.kt b/app/src/main/java/io/legado/app/service/AudioPlayService.kt index a7c8f0ce9..5f8aaab80 100644 --- a/app/src/main/java/io/legado/app/service/AudioPlayService.kt +++ b/app/src/main/java/io/legado/app/service/AudioPlayService.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.content.IntentFilter import android.graphics.BitmapFactory import android.media.AudioManager -import android.net.Uri import android.os.Build import android.os.PowerManager import android.support.v4.media.MediaMetadataCompat @@ -15,10 +14,10 @@ import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.PlaybackStateCompat import androidx.core.app.NotificationCompat import androidx.media.AudioFocusRequestCompat -import com.google.android.exoplayer2.DefaultLoadControl -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.PlaybackException -import com.google.android.exoplayer2.Player +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlayer import io.legado.app.R import io.legado.app.base.BaseService import io.legado.app.constant.* @@ -40,6 +39,7 @@ import kotlinx.coroutines.Dispatchers.Main import splitties.systemservices.audioManager import splitties.systemservices.powerManager +@UnstableApi /** * 音频播放服务 */ @@ -74,14 +74,7 @@ class AudioPlayService : BaseService(), MediaHelp.buildAudioFocusRequestCompat(this) } private val exoPlayer: ExoPlayer by lazy { - ExoPlayer.Builder(this).setLoadControl( - DefaultLoadControl.Builder().setBufferDurationsMs( - DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, - DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, - DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS / 10, - DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS / 10 - ).build() - ).build() + ExoPlayerHelper.createExoPlayer(this) } private var mediaSessionCompat: MediaSessionCompat? = null private var broadcastReceiver: BroadcastReceiver? = null @@ -159,13 +152,14 @@ class AudioPlayService : BaseService(), chapter = AudioPlay.durChapter, headerMapF = AudioPlay.headers(true), ) - val uri = Uri.parse(analyzeUrl.url) + //val uri = Uri.parse(analyzeUrl.url) //ExoPlayerHelper.preDownload(uri, analyzeUrl.headerMap) //休息1秒钟,防止403 //delay(1000) - val mediaSource = ExoPlayerHelper - .createMediaSource(uri, analyzeUrl.headerMap) - exoPlayer.setMediaSource(mediaSource) +// val mediaSource = ExoPlayerHelper +// .createMediaSource(uri, analyzeUrl.headerMap) + //exoPlayer.setMediaSource(mediaSource) + exoPlayer.setMediaItem(ExoPlayerHelper.createMediaItem(analyzeUrl.url,analyzeUrl.headerMap)) exoPlayer.playWhenReady = true exoPlayer.prepare() }.onError { @@ -234,8 +228,10 @@ class AudioPlayService : BaseService(), /** * 调节速度 */ + private fun upSpeed(adjust: Float) { kotlin.runCatching { + @SuppressLint("ObsoleteSdkInt") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { playSpeed += adjust exoPlayer.setPlaybackSpeed(playSpeed) 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 bb0256b96..633407711 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -2,10 +2,10 @@ package io.legado.app.service import android.app.PendingIntent 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 androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.exoplayer.ExoPlayer import com.script.ScriptException import io.legado.app.R import io.legado.app.constant.AppLog @@ -16,6 +16,7 @@ import io.legado.app.exception.ConcurrentException import io.legado.app.exception.NoStackTraceException import io.legado.app.help.config.AppConfig import io.legado.app.help.coroutine.Coroutine +import io.legado.app.help.exoplayer.ExoPlayerHelper import io.legado.app.model.ReadAloud import io.legado.app.model.ReadBook import io.legado.app.model.analyzeRule.AnalyzeUrl @@ -38,7 +39,8 @@ class HttpReadAloudService : BaseReadAloudService(), Player.Listener { private val exoPlayer: ExoPlayer by lazy { - ExoPlayer.Builder(this).build() + //ExoPlayer.Builder(this).build() + ExoPlayerHelper.createExoPlayer(this) } private val ttsFolderPath: String by lazy { cacheDir.absolutePath + File.separator + "httpTTS" + File.separator diff --git a/build.gradle b/build.gradle index 4d6d6f07a..efb9bdc20 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,8 @@ buildscript { build_tool_version = '33.0.1' kotlin_version = '1.8.21' agp_version = '8.0.1' - exoplayer_version = '2.18.6' + //exoplayer_version = '2.18.6' + media3_version = "1.0.1" splitties_version = '3.0.0' room_version = '2.5.1' }