diff --git a/app/src/main/java/io/legado/app/constant/EventBus.kt b/app/src/main/java/io/legado/app/constant/EventBus.kt index f5e9cee25..18be386c5 100644 --- a/app/src/main/java/io/legado/app/constant/EventBus.kt +++ b/app/src/main/java/io/legado/app/constant/EventBus.kt @@ -16,6 +16,7 @@ object EventBus { const val AUDIO_SUB_TITLE = "audioSubTitle" const val AUDIO_STATE = "audioState" const val AUDIO_PROGRESS = "audioProgress" + const val AUDIO_BUFFER_PROGRESS = "audioBufferProgress" const val AUDIO_SIZE = "audioSize" const val AUDIO_SPEED = "audioSpeed" const val NOTIFY_MAIN = "notifyMain" 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 fe684de62..8579edde3 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 @@ -6,7 +6,9 @@ import com.google.android.exoplayer2.database.StandaloneDatabaseProvider 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.upstream.FileDataSource import com.google.android.exoplayer2.upstream.cache.Cache +import com.google.android.exoplayer2.upstream.cache.CacheDataSink import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache import io.legado.app.help.http.okHttpClient @@ -36,6 +38,12 @@ object ExoPlayerHelper { return@lazy OkhttpCacheDataSource.Factory() .setCache(cache) .setUpstreamDataSourceFactory(okhttpDataFactory) + .setCacheReadDataSourceFactory(FileDataSource.Factory()) + .setCacheWriteDataSinkFactory( + CacheDataSink.Factory() + .setCache(cache) + .setFragmentSize(CacheDataSink.DEFAULT_FRAGMENT_SIZE) + ) } /** diff --git a/app/src/main/java/io/legado/app/help/exoplayer/OkhttpCacheDataSource.java b/app/src/main/java/io/legado/app/help/exoplayer/OkhttpCacheDataSource.java index abf8e8c71..479bb3b70 100644 --- a/app/src/main/java/io/legado/app/help/exoplayer/OkhttpCacheDataSource.java +++ b/app/src/main/java/io/legado/app/help/exoplayer/OkhttpCacheDataSource.java @@ -1,8 +1,29 @@ package io.legado.app.help.exoplayer; +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Util.castNonNull; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; @@ -37,10 +58,12 @@ import java.io.InterruptedIOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.List; import java.util.Map; + /** * A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache * when possible. When data is not cached it is requested from an upstream {@link DataSource} and @@ -66,8 +89,7 @@ public final class OkhttpCacheDataSource implements DataSource { @Nullable private PriorityTaskManager upstreamPriorityTaskManager; private int upstreamPriority; - @OkhttpCacheDataSource.Flags - private int flags; + private @OkhttpCacheDataSource.Flags int flags; @Nullable private OkhttpCacheDataSource.EventListener eventListener; @@ -351,6 +373,7 @@ public final class OkhttpCacheDataSource implements DataSource { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { @@ -384,8 +407,11 @@ public final class OkhttpCacheDataSource implements DataSource { * Reasons the cache may be ignored. One of {@link #CACHE_IGNORED_REASON_ERROR} or {@link * #CACHE_IGNORED_REASON_UNSET_LENGTH}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH}) public @interface CacheIgnoredReason { } @@ -641,15 +667,15 @@ public final class OkhttpCacheDataSource implements DataSource { } @Override - public int read(@NonNull byte[] buffer, int offset, int length) throws IOException { - DataSpec requestDataSpec = checkNotNull(this.requestDataSpec); - DataSpec currentDataSpec = checkNotNull(this.currentDataSpec); + public int read(byte[] buffer, int offset, int length) throws IOException { if (length == 0) { return 0; } if (bytesRemaining == 0) { return C.RESULT_END_OF_INPUT; } + DataSpec requestDataSpec = checkNotNull(this.requestDataSpec); + DataSpec currentDataSpec = checkNotNull(this.currentDataSpec); try { if (readPosition >= checkCachePosition) { openNextSource(requestDataSpec, true); @@ -683,9 +709,8 @@ public final class OkhttpCacheDataSource implements DataSource { } } - - @Nullable @Override + @Nullable public Uri getUri() { return actualUri; } @@ -693,6 +718,7 @@ public final class OkhttpCacheDataSource implements DataSource { @NonNull @Override public Map> getResponseHeaders() { + // TODO: Implement. return isReadingFromUpstream() ? upstreamDataSource.getResponseHeaders() : Collections.emptyMap(); 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 87e9b1a82..93905843f 100644 --- a/app/src/main/java/io/legado/app/service/AudioPlayService.kt +++ b/app/src/main/java/io/legado/app/service/AudioPlayService.kt @@ -13,6 +13,7 @@ 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 @@ -57,7 +58,14 @@ class AudioPlayService : BaseService(), MediaHelp.getFocusRequest(this) } private val exoPlayer: ExoPlayer by lazy { - ExoPlayer.Builder(this).build() + 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() } private var mediaSessionCompat: MediaSessionCompat? = null private var broadcastReceiver: BroadcastReceiver? = null @@ -302,6 +310,8 @@ class AudioPlayService : BaseService(), upPlayProgressJob = launch { while (isActive) { AudioPlay.book?.let { + //更新buffer位置 + postEvent(EventBus.AUDIO_BUFFER_PROGRESS, exoPlayer.bufferedPosition.toInt()) it.durChapterPos = exoPlayer.currentPosition.toInt() postEvent(EventBus.AUDIO_PROGRESS, it.durChapterPos) saveProgress(it) diff --git a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt index 778aef4d2..acb05e218 100644 --- a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt @@ -273,6 +273,10 @@ class AudioPlayActivity : if (!adjustProgress) binding.playerProgress.progress = it binding.tvDurTime.text = progressTimeFormat.format(it.toLong()) } + observeEventSticky(EventBus.AUDIO_BUFFER_PROGRESS) { + binding.playerProgress.secondaryProgress = it + + } observeEventSticky(EventBus.AUDIO_SPEED) { binding.tvSpeed.text = String.format("%.1fX", it) binding.tvSpeed.visible()