音频界面显示缓冲进度
This commit is contained in:
@@ -16,6 +16,7 @@ object EventBus {
|
|||||||
const val AUDIO_SUB_TITLE = "audioSubTitle"
|
const val AUDIO_SUB_TITLE = "audioSubTitle"
|
||||||
const val AUDIO_STATE = "audioState"
|
const val AUDIO_STATE = "audioState"
|
||||||
const val AUDIO_PROGRESS = "audioProgress"
|
const val AUDIO_PROGRESS = "audioProgress"
|
||||||
|
const val AUDIO_BUFFER_PROGRESS = "audioBufferProgress"
|
||||||
const val AUDIO_SIZE = "audioSize"
|
const val AUDIO_SIZE = "audioSize"
|
||||||
const val AUDIO_SPEED = "audioSpeed"
|
const val AUDIO_SPEED = "audioSpeed"
|
||||||
const val NOTIFY_MAIN = "notifyMain"
|
const val NOTIFY_MAIN = "notifyMain"
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
|||||||
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
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.Cache
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSink
|
||||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
|
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
@@ -36,6 +38,12 @@ object ExoPlayerHelper {
|
|||||||
return@lazy OkhttpCacheDataSource.Factory()
|
return@lazy OkhttpCacheDataSource.Factory()
|
||||||
.setCache(cache)
|
.setCache(cache)
|
||||||
.setUpstreamDataSourceFactory(okhttpDataFactory)
|
.setUpstreamDataSourceFactory(okhttpDataFactory)
|
||||||
|
.setCacheReadDataSourceFactory(FileDataSource.Factory())
|
||||||
|
.setCacheWriteDataSinkFactory(
|
||||||
|
CacheDataSink.Factory()
|
||||||
|
.setCache(cache)
|
||||||
|
.setFragmentSize(CacheDataSink.DEFAULT_FRAGMENT_SIZE)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
package io.legado.app.help.exoplayer;
|
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.Assertions.checkNotNull;
|
||||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
import static java.lang.Math.min;
|
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;
|
import android.net.Uri;
|
||||||
|
|
||||||
@@ -37,10 +58,12 @@ import java.io.InterruptedIOException;
|
|||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache
|
* 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
|
* 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
|
@Nullable
|
||||||
private PriorityTaskManager upstreamPriorityTaskManager;
|
private PriorityTaskManager upstreamPriorityTaskManager;
|
||||||
private int upstreamPriority;
|
private int upstreamPriority;
|
||||||
@OkhttpCacheDataSource.Flags
|
private @OkhttpCacheDataSource.Flags int flags;
|
||||||
private int flags;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private OkhttpCacheDataSource.EventListener eventListener;
|
private OkhttpCacheDataSource.EventListener eventListener;
|
||||||
|
|
||||||
@@ -351,6 +373,7 @@ public final class OkhttpCacheDataSource implements DataSource {
|
|||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target(TYPE_USE)
|
||||||
@IntDef(
|
@IntDef(
|
||||||
flag = true,
|
flag = true,
|
||||||
value = {
|
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
|
* Reasons the cache may be ignored. One of {@link #CACHE_IGNORED_REASON_ERROR} or {@link
|
||||||
* #CACHE_IGNORED_REASON_UNSET_LENGTH}.
|
* #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
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
|
||||||
@IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH})
|
@IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH})
|
||||||
public @interface CacheIgnoredReason {
|
public @interface CacheIgnoredReason {
|
||||||
}
|
}
|
||||||
@@ -641,15 +667,15 @@ public final class OkhttpCacheDataSource implements DataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(@NonNull byte[] buffer, int offset, int length) throws IOException {
|
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||||||
DataSpec requestDataSpec = checkNotNull(this.requestDataSpec);
|
|
||||||
DataSpec currentDataSpec = checkNotNull(this.currentDataSpec);
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (bytesRemaining == 0) {
|
if (bytesRemaining == 0) {
|
||||||
return C.RESULT_END_OF_INPUT;
|
return C.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
|
DataSpec requestDataSpec = checkNotNull(this.requestDataSpec);
|
||||||
|
DataSpec currentDataSpec = checkNotNull(this.currentDataSpec);
|
||||||
try {
|
try {
|
||||||
if (readPosition >= checkCachePosition) {
|
if (readPosition >= checkCachePosition) {
|
||||||
openNextSource(requestDataSpec, true);
|
openNextSource(requestDataSpec, true);
|
||||||
@@ -683,9 +709,8 @@ public final class OkhttpCacheDataSource implements DataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return actualUri;
|
return actualUri;
|
||||||
}
|
}
|
||||||
@@ -693,6 +718,7 @@ public final class OkhttpCacheDataSource implements DataSource {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getResponseHeaders() {
|
public Map<String, List<String>> getResponseHeaders() {
|
||||||
|
// TODO: Implement.
|
||||||
return isReadingFromUpstream()
|
return isReadingFromUpstream()
|
||||||
? upstreamDataSource.getResponseHeaders()
|
? upstreamDataSource.getResponseHeaders()
|
||||||
: Collections.emptyMap();
|
: Collections.emptyMap();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import android.support.v4.media.session.MediaSessionCompat
|
|||||||
import android.support.v4.media.session.PlaybackStateCompat
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.media.AudioFocusRequestCompat
|
import androidx.media.AudioFocusRequestCompat
|
||||||
|
import com.google.android.exoplayer2.DefaultLoadControl
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.google.android.exoplayer2.PlaybackException
|
import com.google.android.exoplayer2.PlaybackException
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
@@ -57,7 +58,14 @@ class AudioPlayService : BaseService(),
|
|||||||
MediaHelp.getFocusRequest(this)
|
MediaHelp.getFocusRequest(this)
|
||||||
}
|
}
|
||||||
private val exoPlayer: ExoPlayer by lazy {
|
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 mediaSessionCompat: MediaSessionCompat? = null
|
||||||
private var broadcastReceiver: BroadcastReceiver? = null
|
private var broadcastReceiver: BroadcastReceiver? = null
|
||||||
@@ -302,6 +310,8 @@ class AudioPlayService : BaseService(),
|
|||||||
upPlayProgressJob = launch {
|
upPlayProgressJob = launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
AudioPlay.book?.let {
|
AudioPlay.book?.let {
|
||||||
|
//更新buffer位置
|
||||||
|
postEvent(EventBus.AUDIO_BUFFER_PROGRESS, exoPlayer.bufferedPosition.toInt())
|
||||||
it.durChapterPos = exoPlayer.currentPosition.toInt()
|
it.durChapterPos = exoPlayer.currentPosition.toInt()
|
||||||
postEvent(EventBus.AUDIO_PROGRESS, it.durChapterPos)
|
postEvent(EventBus.AUDIO_PROGRESS, it.durChapterPos)
|
||||||
saveProgress(it)
|
saveProgress(it)
|
||||||
|
|||||||
@@ -273,6 +273,10 @@ class AudioPlayActivity :
|
|||||||
if (!adjustProgress) binding.playerProgress.progress = it
|
if (!adjustProgress) binding.playerProgress.progress = it
|
||||||
binding.tvDurTime.text = progressTimeFormat.format(it.toLong())
|
binding.tvDurTime.text = progressTimeFormat.format(it.toLong())
|
||||||
}
|
}
|
||||||
|
observeEventSticky<Int>(EventBus.AUDIO_BUFFER_PROGRESS) {
|
||||||
|
binding.playerProgress.secondaryProgress = it
|
||||||
|
|
||||||
|
}
|
||||||
observeEventSticky<Float>(EventBus.AUDIO_SPEED) {
|
observeEventSticky<Float>(EventBus.AUDIO_SPEED) {
|
||||||
binding.tvSpeed.text = String.format("%.1fX", it)
|
binding.tvSpeed.text = String.format("%.1fX", it)
|
||||||
binding.tvSpeed.visible()
|
binding.tvSpeed.visible()
|
||||||
|
|||||||
Reference in New Issue
Block a user