expo-video 3.0.5 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/android/build.gradle +3 -3
- package/android/src/main/java/expo/modules/video/player/DefaultLoadControl.java +552 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayer.kt +1 -1
- package/android/src/main/java/expo/modules/video/player/VideoPlayerLoadControl.kt +18 -485
- package/android/src/main/java/expo/modules/video/records/BufferOptions.kt +4 -3
- package/expo-module.config.json +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.5/expo.modules.video-3.0.5-sources.jar → 3.0.6/expo.modules.video-3.0.6-sources.jar} +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.5/expo.modules.video-3.0.5.aar → 3.0.6/expo.modules.video-3.0.6.aar} +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.5/expo.modules.video-3.0.5.module → 3.0.6/expo.modules.video-3.0.6.module} +28 -28
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.5/expo.modules.video-3.0.5.pom → 3.0.6/expo.modules.video-3.0.6.pom} +7 -7
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml +4 -4
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha512 +1 -1
- package/package.json +3 -3
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 3.0.6 — 2025-08-27
|
|
14
|
+
|
|
15
|
+
### 💡 Others
|
|
16
|
+
|
|
17
|
+
- [Android] Bump media3 version to 1.8.0. ([#39184](https://github.com/expo/expo/pull/39184) by [@behenate](https://github.com/behenate))
|
|
18
|
+
|
|
13
19
|
## 3.0.5 — 2025-08-25
|
|
14
20
|
|
|
15
21
|
### 🛠 Breaking changes
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '3.0.
|
|
7
|
+
version = '3.0.6'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.video"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 1
|
|
13
|
-
versionName '3.0.
|
|
13
|
+
versionName '3.0.6'
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -18,7 +18,7 @@ dependencies {
|
|
|
18
18
|
implementation 'com.facebook.react:react-android'
|
|
19
19
|
|
|
20
20
|
// Remember to keep this in sync with the version in `expo-audio`
|
|
21
|
-
def androidxMedia3Version = "1.
|
|
21
|
+
def androidxMedia3Version = "1.8.0"
|
|
22
22
|
implementation "androidx.media3:media3-session:${androidxMedia3Version}"
|
|
23
23
|
implementation "androidx.media3:media3-exoplayer:${androidxMedia3Version}"
|
|
24
24
|
implementation "androidx.media3:media3-exoplayer-dash:${androidxMedia3Version}"
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2016 The Android Open Source Project
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* NOTE FROM EXPO (@behenate):
|
|
19
|
+
* This is a copy of the DefaultLoadControl from ExoPlayer. The only difference being, that the buffer options such as
|
|
20
|
+
* long minBufferUs;
|
|
21
|
+
* long maxBufferUs;rSizeThresholds
|
|
22
|
+
* long bufferForPlaybackUs;Ms
|
|
23
|
+
* long bufferForPlaybackAfterRebufferUs;AfterRebufferMs
|
|
24
|
+
* int targetBufferBytesOverwrite;
|
|
25
|
+
* and some others
|
|
26
|
+
*
|
|
27
|
+
* Are protected, instead of private, so that we can inherit from it and modify those at runtime
|
|
28
|
+
* also the PlayerLoadingState is made internal
|
|
29
|
+
*
|
|
30
|
+
* STEPS FOR UPDATING TO A NEWER VERSION:
|
|
31
|
+
* - Copy the implementation for the new version from the media3 repo
|
|
32
|
+
* - Make the following variables protected in DefaultLoadControl class (not the builder!):
|
|
33
|
+
* long minBufferUs;
|
|
34
|
+
* long maxBufferUs;
|
|
35
|
+
* long bufferForPlaybackUs;
|
|
36
|
+
* long bufferForPlaybackAfterRebufferUs;
|
|
37
|
+
* int targetBufferBytesOverwrite;
|
|
38
|
+
* boolean prioritizeTimeOverSizeThresholds;
|
|
39
|
+
* long backBufferDurationUs;
|
|
40
|
+
* final HashMap<PlayerId, PlayerLoadingState> loadingStates;
|
|
41
|
+
* - Make PlayerLoadingState class protected instead of private
|
|
42
|
+
* - Make the updateAllocator method protected
|
|
43
|
+
* - Remove non-exported annotations from google
|
|
44
|
+
* - In our class inheriting from DefaultLoadControl make sure that `applyBufferOptions` still does everything necessary to update the buffer preferences
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
package expo.modules.video.player;
|
|
48
|
+
|
|
49
|
+
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
50
|
+
import static androidx.media3.common.util.Assertions.checkState;
|
|
51
|
+
import static java.lang.Math.max;
|
|
52
|
+
import static java.lang.Math.min;
|
|
53
|
+
|
|
54
|
+
import androidx.annotation.Nullable;
|
|
55
|
+
import androidx.annotation.VisibleForTesting;
|
|
56
|
+
import androidx.media3.common.C;
|
|
57
|
+
import androidx.media3.common.Timeline;
|
|
58
|
+
import androidx.media3.common.util.Assertions;
|
|
59
|
+
import androidx.media3.common.util.Log;
|
|
60
|
+
import androidx.media3.common.util.NullableType;
|
|
61
|
+
import androidx.media3.common.util.UnstableApi;
|
|
62
|
+
import androidx.media3.common.util.Util;
|
|
63
|
+
import androidx.media3.exoplayer.Renderer;
|
|
64
|
+
import androidx.media3.exoplayer.analytics.PlayerId;
|
|
65
|
+
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
|
66
|
+
import androidx.media3.exoplayer.source.TrackGroupArray;
|
|
67
|
+
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
|
68
|
+
import androidx.media3.exoplayer.upstream.Allocator;
|
|
69
|
+
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
|
70
|
+
import androidx.media3.exoplayer.LoadControl;
|
|
71
|
+
import java.util.HashMap;
|
|
72
|
+
|
|
73
|
+
/** The default {@link LoadControl} implementation. */
|
|
74
|
+
@UnstableApi
|
|
75
|
+
public class DefaultLoadControl implements LoadControl {
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The default minimum duration of media that the player will attempt to ensure is buffered at all
|
|
79
|
+
* times, in milliseconds.
|
|
80
|
+
*/
|
|
81
|
+
public static final int DEFAULT_MIN_BUFFER_MS = 50_000;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The default maximum duration of media that the player will attempt to buffer, in milliseconds.
|
|
85
|
+
*/
|
|
86
|
+
public static final int DEFAULT_MAX_BUFFER_MS = 50_000;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The default duration of media that must be buffered for playback to start or resume following a
|
|
90
|
+
* user action such as a seek, in milliseconds.
|
|
91
|
+
*/
|
|
92
|
+
public static final int DEFAULT_BUFFER_FOR_PLAYBACK_MS = 1000;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The default duration of media that must be buffered for playback to resume after a rebuffer, in
|
|
96
|
+
* milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action.
|
|
97
|
+
*/
|
|
98
|
+
public static final int DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = 2000;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The default target buffer size in bytes. The value ({@link C#LENGTH_UNSET}) means that the load
|
|
102
|
+
* control will calculate the target buffer size based on the selected tracks.
|
|
103
|
+
*/
|
|
104
|
+
public static final int DEFAULT_TARGET_BUFFER_BYTES = C.LENGTH_UNSET;
|
|
105
|
+
|
|
106
|
+
/** The default prioritization of buffer time constraints over size constraints. */
|
|
107
|
+
public static final boolean DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS = false;
|
|
108
|
+
|
|
109
|
+
/** The default back buffer duration in milliseconds. */
|
|
110
|
+
public static final int DEFAULT_BACK_BUFFER_DURATION_MS = 0;
|
|
111
|
+
|
|
112
|
+
/** The default for whether the back buffer is retained from the previous keyframe. */
|
|
113
|
+
public static final boolean DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME = false;
|
|
114
|
+
|
|
115
|
+
/** A default size in bytes for a video buffer. */
|
|
116
|
+
public static final int DEFAULT_VIDEO_BUFFER_SIZE = 2000 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
117
|
+
|
|
118
|
+
/** A default size in bytes for an audio buffer. */
|
|
119
|
+
public static final int DEFAULT_AUDIO_BUFFER_SIZE = 200 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
120
|
+
|
|
121
|
+
/** A default size in bytes for a text buffer. */
|
|
122
|
+
public static final int DEFAULT_TEXT_BUFFER_SIZE = 2 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
123
|
+
|
|
124
|
+
/** A default size in bytes for a metadata buffer. */
|
|
125
|
+
public static final int DEFAULT_METADATA_BUFFER_SIZE = 2 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
126
|
+
|
|
127
|
+
/** A default size in bytes for a camera motion buffer. */
|
|
128
|
+
public static final int DEFAULT_CAMERA_MOTION_BUFFER_SIZE = 2 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
129
|
+
|
|
130
|
+
/** A default size in bytes for an image buffer. */
|
|
131
|
+
public static final int DEFAULT_IMAGE_BUFFER_SIZE = 400 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
132
|
+
|
|
133
|
+
/** A default size in bytes for a muxed buffer (e.g. containing video, audio and text). */
|
|
134
|
+
public static final int DEFAULT_MUXED_BUFFER_SIZE =
|
|
135
|
+
DEFAULT_VIDEO_BUFFER_SIZE + DEFAULT_AUDIO_BUFFER_SIZE + DEFAULT_TEXT_BUFFER_SIZE;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The buffer size in bytes that will be used as a minimum target buffer in all cases. This is
|
|
139
|
+
* also the default target buffer before tracks are selected.
|
|
140
|
+
*/
|
|
141
|
+
public static final int DEFAULT_MIN_BUFFER_SIZE = 200 * C.DEFAULT_BUFFER_SEGMENT_SIZE;
|
|
142
|
+
|
|
143
|
+
/** Builder for {@link DefaultLoadControl}. */
|
|
144
|
+
public static final class Builder {
|
|
145
|
+
|
|
146
|
+
@Nullable private DefaultAllocator allocator;
|
|
147
|
+
private int minBufferMs;
|
|
148
|
+
private int maxBufferMs;
|
|
149
|
+
private int bufferForPlaybackMs;
|
|
150
|
+
private int bufferForPlaybackAfterRebufferMs;
|
|
151
|
+
private int targetBufferBytes;
|
|
152
|
+
private boolean prioritizeTimeOverSizeThresholds;
|
|
153
|
+
private int backBufferDurationMs;
|
|
154
|
+
private boolean retainBackBufferFromKeyframe;
|
|
155
|
+
private boolean buildCalled;
|
|
156
|
+
|
|
157
|
+
/** Constructs a new instance. */
|
|
158
|
+
public Builder() {
|
|
159
|
+
minBufferMs = DEFAULT_MIN_BUFFER_MS;
|
|
160
|
+
maxBufferMs = DEFAULT_MAX_BUFFER_MS;
|
|
161
|
+
bufferForPlaybackMs = DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
|
162
|
+
bufferForPlaybackAfterRebufferMs = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
|
163
|
+
targetBufferBytes = DEFAULT_TARGET_BUFFER_BYTES;
|
|
164
|
+
prioritizeTimeOverSizeThresholds = DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS;
|
|
165
|
+
backBufferDurationMs = DEFAULT_BACK_BUFFER_DURATION_MS;
|
|
166
|
+
retainBackBufferFromKeyframe = DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Sets the {@link DefaultAllocator} used by the loader.
|
|
171
|
+
*
|
|
172
|
+
* @param allocator The {@link DefaultAllocator}.
|
|
173
|
+
* @return This builder, for convenience.
|
|
174
|
+
* @throws IllegalStateException If {@link #build()} has already been called.
|
|
175
|
+
*/
|
|
176
|
+
public Builder setAllocator(DefaultAllocator allocator) {
|
|
177
|
+
checkState(!buildCalled);
|
|
178
|
+
this.allocator = allocator;
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Sets the buffer duration parameters.
|
|
184
|
+
*
|
|
185
|
+
* @param minBufferMs The minimum duration of media that the player will attempt to ensure is
|
|
186
|
+
* buffered at all times, in milliseconds.
|
|
187
|
+
* @param maxBufferMs The maximum duration of media that the player will attempt to buffer, in
|
|
188
|
+
* milliseconds.
|
|
189
|
+
* @param bufferForPlaybackMs The duration of media that must be buffered for playback to start
|
|
190
|
+
* or resume following a user action such as a seek, in milliseconds.
|
|
191
|
+
* @param bufferForPlaybackAfterRebufferMs The default duration of media that must be buffered
|
|
192
|
+
* for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be
|
|
193
|
+
* caused by buffer depletion rather than a user action.
|
|
194
|
+
* @return This builder, for convenience.
|
|
195
|
+
* @throws IllegalStateException If {@link #build()} has already been called.
|
|
196
|
+
*/
|
|
197
|
+
public Builder setBufferDurationsMs(
|
|
198
|
+
int minBufferMs,
|
|
199
|
+
int maxBufferMs,
|
|
200
|
+
int bufferForPlaybackMs,
|
|
201
|
+
int bufferForPlaybackAfterRebufferMs) {
|
|
202
|
+
checkState(!buildCalled);
|
|
203
|
+
assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0");
|
|
204
|
+
assertGreaterOrEqual(
|
|
205
|
+
bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0");
|
|
206
|
+
assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs");
|
|
207
|
+
assertGreaterOrEqual(
|
|
208
|
+
minBufferMs,
|
|
209
|
+
bufferForPlaybackAfterRebufferMs,
|
|
210
|
+
"minBufferMs",
|
|
211
|
+
"bufferForPlaybackAfterRebufferMs");
|
|
212
|
+
assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs");
|
|
213
|
+
this.minBufferMs = minBufferMs;
|
|
214
|
+
this.maxBufferMs = maxBufferMs;
|
|
215
|
+
this.bufferForPlaybackMs = bufferForPlaybackMs;
|
|
216
|
+
this.bufferForPlaybackAfterRebufferMs = bufferForPlaybackAfterRebufferMs;
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Sets the target buffer size in bytes for each player. The actual overall target buffer size
|
|
222
|
+
* is this value multiplied by the number of players that use the load control simultaneously.
|
|
223
|
+
* If set to {@link C#LENGTH_UNSET}, the target buffer size of a player will be calculated based
|
|
224
|
+
* on the selected tracks of the player.
|
|
225
|
+
*
|
|
226
|
+
* @param targetBufferBytes The target buffer size in bytes.
|
|
227
|
+
* @return This builder, for convenience.
|
|
228
|
+
* @throws IllegalStateException If {@link #build()} has already been called.
|
|
229
|
+
*/
|
|
230
|
+
public Builder setTargetBufferBytes(int targetBufferBytes) {
|
|
231
|
+
checkState(!buildCalled);
|
|
232
|
+
this.targetBufferBytes = targetBufferBytes;
|
|
233
|
+
return this;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Sets whether the load control prioritizes buffer time constraints over buffer size
|
|
238
|
+
* constraints.
|
|
239
|
+
*
|
|
240
|
+
* @param prioritizeTimeOverSizeThresholds Whether the load control prioritizes buffer time
|
|
241
|
+
* constraints over buffer size constraints.
|
|
242
|
+
* @return This builder, for convenience.
|
|
243
|
+
* @throws IllegalStateException If {@link #build()} has already been called.
|
|
244
|
+
*/
|
|
245
|
+
public Builder setPrioritizeTimeOverSizeThresholds(boolean prioritizeTimeOverSizeThresholds) {
|
|
246
|
+
checkState(!buildCalled);
|
|
247
|
+
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Sets the back buffer duration, and whether the back buffer is retained from the previous
|
|
253
|
+
* keyframe.
|
|
254
|
+
*
|
|
255
|
+
* @param backBufferDurationMs The back buffer duration in milliseconds.
|
|
256
|
+
* @param retainBackBufferFromKeyframe Whether the back buffer is retained from the previous
|
|
257
|
+
* keyframe.
|
|
258
|
+
* @return This builder, for convenience.
|
|
259
|
+
* @throws IllegalStateException If {@link #build()} has already been called.
|
|
260
|
+
*/
|
|
261
|
+
public Builder setBackBuffer(int backBufferDurationMs, boolean retainBackBufferFromKeyframe) {
|
|
262
|
+
checkState(!buildCalled);
|
|
263
|
+
assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0");
|
|
264
|
+
this.backBufferDurationMs = backBufferDurationMs;
|
|
265
|
+
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Creates a {@link DefaultLoadControl}. */
|
|
270
|
+
public DefaultLoadControl build() {
|
|
271
|
+
checkState(!buildCalled);
|
|
272
|
+
buildCalled = true;
|
|
273
|
+
if (allocator == null) {
|
|
274
|
+
allocator = new DefaultAllocator(/* trimOnReset= */ true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
|
275
|
+
}
|
|
276
|
+
return new DefaultLoadControl(
|
|
277
|
+
allocator,
|
|
278
|
+
minBufferMs,
|
|
279
|
+
maxBufferMs,
|
|
280
|
+
bufferForPlaybackMs,
|
|
281
|
+
bufferForPlaybackAfterRebufferMs,
|
|
282
|
+
targetBufferBytes,
|
|
283
|
+
prioritizeTimeOverSizeThresholds,
|
|
284
|
+
backBufferDurationMs,
|
|
285
|
+
retainBackBufferFromKeyframe);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private final DefaultAllocator allocator;
|
|
290
|
+
|
|
291
|
+
protected long minBufferUs;
|
|
292
|
+
protected long maxBufferUs;
|
|
293
|
+
protected long bufferForPlaybackUs;
|
|
294
|
+
protected long bufferForPlaybackAfterRebufferUs;
|
|
295
|
+
protected int targetBufferBytesOverwrite;
|
|
296
|
+
protected boolean prioritizeTimeOverSizeThresholds;
|
|
297
|
+
protected long backBufferDurationUs;
|
|
298
|
+
private final boolean retainBackBufferFromKeyframe;
|
|
299
|
+
protected final HashMap<PlayerId, PlayerLoadingState> loadingStates;
|
|
300
|
+
|
|
301
|
+
private long threadId;
|
|
302
|
+
|
|
303
|
+
/** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */
|
|
304
|
+
public DefaultLoadControl() {
|
|
305
|
+
this(
|
|
306
|
+
new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
|
|
307
|
+
DEFAULT_MIN_BUFFER_MS,
|
|
308
|
+
DEFAULT_MAX_BUFFER_MS,
|
|
309
|
+
DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
|
310
|
+
DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
|
|
311
|
+
DEFAULT_TARGET_BUFFER_BYTES,
|
|
312
|
+
DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS,
|
|
313
|
+
DEFAULT_BACK_BUFFER_DURATION_MS,
|
|
314
|
+
DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
protected DefaultLoadControl(
|
|
318
|
+
DefaultAllocator allocator,
|
|
319
|
+
int minBufferMs,
|
|
320
|
+
int maxBufferMs,
|
|
321
|
+
int bufferForPlaybackMs,
|
|
322
|
+
int bufferForPlaybackAfterRebufferMs,
|
|
323
|
+
int targetBufferBytes,
|
|
324
|
+
boolean prioritizeTimeOverSizeThresholds,
|
|
325
|
+
int backBufferDurationMs,
|
|
326
|
+
boolean retainBackBufferFromKeyframe) {
|
|
327
|
+
assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0");
|
|
328
|
+
assertGreaterOrEqual(
|
|
329
|
+
bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0");
|
|
330
|
+
assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs");
|
|
331
|
+
assertGreaterOrEqual(
|
|
332
|
+
minBufferMs,
|
|
333
|
+
bufferForPlaybackAfterRebufferMs,
|
|
334
|
+
"minBufferMs",
|
|
335
|
+
"bufferForPlaybackAfterRebufferMs");
|
|
336
|
+
assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs");
|
|
337
|
+
assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0");
|
|
338
|
+
|
|
339
|
+
this.allocator = allocator;
|
|
340
|
+
this.minBufferUs = Util.msToUs(minBufferMs);
|
|
341
|
+
this.maxBufferUs = Util.msToUs(maxBufferMs);
|
|
342
|
+
this.bufferForPlaybackUs = Util.msToUs(bufferForPlaybackMs);
|
|
343
|
+
this.bufferForPlaybackAfterRebufferUs = Util.msToUs(bufferForPlaybackAfterRebufferMs);
|
|
344
|
+
this.targetBufferBytesOverwrite = targetBufferBytes;
|
|
345
|
+
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
|
|
346
|
+
this.backBufferDurationUs = Util.msToUs(backBufferDurationMs);
|
|
347
|
+
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
|
|
348
|
+
loadingStates = new HashMap<>();
|
|
349
|
+
threadId = C.INDEX_UNSET;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@Override
|
|
353
|
+
public void onPrepared(PlayerId playerId) {
|
|
354
|
+
long currentThreadId = Thread.currentThread().getId();
|
|
355
|
+
checkState(
|
|
356
|
+
threadId == C.INDEX_UNSET || threadId == currentThreadId,
|
|
357
|
+
"Players that share the same LoadControl must share the same playback thread. See"
|
|
358
|
+
+ " ExoPlayer.Builder.setPlaybackLooper(Looper).");
|
|
359
|
+
threadId = currentThreadId;
|
|
360
|
+
if (!loadingStates.containsKey(playerId)) {
|
|
361
|
+
loadingStates.put(playerId, new PlayerLoadingState());
|
|
362
|
+
}
|
|
363
|
+
resetPlayerLoadingState(playerId);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@Override
|
|
367
|
+
public void onTracksSelected(
|
|
368
|
+
LoadControl.Parameters parameters,
|
|
369
|
+
TrackGroupArray trackGroups,
|
|
370
|
+
@NullableType ExoTrackSelection[] trackSelections) {
|
|
371
|
+
checkNotNull(loadingStates.get(parameters.playerId)).targetBufferBytes =
|
|
372
|
+
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
|
373
|
+
? calculateTargetBufferBytes(trackSelections)
|
|
374
|
+
: targetBufferBytesOverwrite;
|
|
375
|
+
updateAllocator();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@Override
|
|
379
|
+
public void onStopped(PlayerId playerId) {
|
|
380
|
+
removePlayer(playerId);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@Override
|
|
384
|
+
public void onReleased(PlayerId playerId) {
|
|
385
|
+
removePlayer(playerId);
|
|
386
|
+
if (loadingStates.isEmpty()) {
|
|
387
|
+
threadId = C.INDEX_UNSET;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@Override
|
|
392
|
+
public Allocator getAllocator() {
|
|
393
|
+
return allocator;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@Override
|
|
397
|
+
public long getBackBufferDurationUs(PlayerId playerId) {
|
|
398
|
+
return backBufferDurationUs;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@Override
|
|
402
|
+
public boolean retainBackBufferFromKeyframe(PlayerId playerId) {
|
|
403
|
+
return retainBackBufferFromKeyframe;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@Override
|
|
407
|
+
public boolean shouldContinueLoading(Parameters parameters) {
|
|
408
|
+
PlayerLoadingState playerLoadingState = checkNotNull(loadingStates.get(parameters.playerId));
|
|
409
|
+
boolean targetBufferSizeReached =
|
|
410
|
+
allocator.getTotalBytesAllocated() >= calculateTotalTargetBufferBytes();
|
|
411
|
+
long minBufferUs = this.minBufferUs;
|
|
412
|
+
if (parameters.playbackSpeed > 1) {
|
|
413
|
+
// The playback speed is faster than real time, so scale up the minimum required media
|
|
414
|
+
// duration to keep enough media buffered for a playout duration of minBufferUs.
|
|
415
|
+
long mediaDurationMinBufferUs =
|
|
416
|
+
Util.getMediaDurationForPlayoutDuration(minBufferUs, parameters.playbackSpeed);
|
|
417
|
+
minBufferUs = min(mediaDurationMinBufferUs, maxBufferUs);
|
|
418
|
+
}
|
|
419
|
+
// Prevent playback from getting stuck if minBufferUs is too small.
|
|
420
|
+
minBufferUs = max(minBufferUs, 500_000);
|
|
421
|
+
if (parameters.bufferedDurationUs < minBufferUs) {
|
|
422
|
+
playerLoadingState.isLoading = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
|
|
423
|
+
if (!playerLoadingState.isLoading && parameters.bufferedDurationUs < 500_000) {
|
|
424
|
+
Log.w(
|
|
425
|
+
"DefaultLoadControl",
|
|
426
|
+
"Target buffer size reached with less than 500ms of buffered media data.");
|
|
427
|
+
}
|
|
428
|
+
} else if (parameters.bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
|
|
429
|
+
playerLoadingState.isLoading = false;
|
|
430
|
+
} // Else don't change the loading state.
|
|
431
|
+
return playerLoadingState.isLoading;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@Override
|
|
435
|
+
public boolean shouldStartPlayback(Parameters parameters) {
|
|
436
|
+
long bufferedDurationUs =
|
|
437
|
+
Util.getPlayoutDurationForMediaDuration(
|
|
438
|
+
parameters.bufferedDurationUs, parameters.playbackSpeed);
|
|
439
|
+
long minBufferDurationUs =
|
|
440
|
+
parameters.rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
|
|
441
|
+
if (parameters.targetLiveOffsetUs != C.TIME_UNSET) {
|
|
442
|
+
minBufferDurationUs = min(parameters.targetLiveOffsetUs / 2, minBufferDurationUs);
|
|
443
|
+
}
|
|
444
|
+
return minBufferDurationUs <= 0
|
|
445
|
+
|| bufferedDurationUs >= minBufferDurationUs
|
|
446
|
+
|| (!prioritizeTimeOverSizeThresholds
|
|
447
|
+
&& allocator.getTotalBytesAllocated() >= calculateTotalTargetBufferBytes());
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
@Override
|
|
451
|
+
public boolean shouldContinuePreloading(
|
|
452
|
+
Timeline timeline, MediaPeriodId mediaPeriodId, long bufferedDurationUs) {
|
|
453
|
+
for (PlayerLoadingState playerLoadingState : loadingStates.values()) {
|
|
454
|
+
if (playerLoadingState.isLoading) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Calculate target buffer size in bytes based on the selected tracks. The player will try not to
|
|
463
|
+
* exceed this target buffer. Only used when {@code targetBufferBytes} is {@link C#LENGTH_UNSET}.
|
|
464
|
+
*
|
|
465
|
+
* @param trackSelectionArray The selected tracks.
|
|
466
|
+
* @return The target buffer size in bytes.
|
|
467
|
+
*/
|
|
468
|
+
protected int calculateTargetBufferBytes(@NullableType ExoTrackSelection[] trackSelectionArray) {
|
|
469
|
+
int targetBufferSize = 0;
|
|
470
|
+
for (ExoTrackSelection exoTrackSelection : trackSelectionArray) {
|
|
471
|
+
if (exoTrackSelection != null) {
|
|
472
|
+
targetBufferSize += getDefaultBufferSize(exoTrackSelection.getTrackGroup().type);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return max(DEFAULT_MIN_BUFFER_SIZE, targetBufferSize);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @deprecated Use {@link #calculateTargetBufferBytes(ExoTrackSelection[])} instead.
|
|
480
|
+
*/
|
|
481
|
+
@Deprecated
|
|
482
|
+
protected final int calculateTargetBufferBytes(
|
|
483
|
+
Renderer[] renderers, ExoTrackSelection[] trackSelectionArray) {
|
|
484
|
+
return calculateTargetBufferBytes(trackSelectionArray);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
@VisibleForTesting
|
|
488
|
+
/* package */ int calculateTotalTargetBufferBytes() {
|
|
489
|
+
int totalTargetBufferBytes = 0;
|
|
490
|
+
for (PlayerLoadingState state : loadingStates.values()) {
|
|
491
|
+
totalTargetBufferBytes += state.targetBufferBytes;
|
|
492
|
+
}
|
|
493
|
+
return totalTargetBufferBytes;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
private void resetPlayerLoadingState(PlayerId playerId) {
|
|
497
|
+
PlayerLoadingState playerLoadingState = checkNotNull(loadingStates.get(playerId));
|
|
498
|
+
playerLoadingState.targetBufferBytes =
|
|
499
|
+
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
|
500
|
+
? DEFAULT_MIN_BUFFER_SIZE
|
|
501
|
+
: targetBufferBytesOverwrite;
|
|
502
|
+
playerLoadingState.isLoading = false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
private void removePlayer(PlayerId playerId) {
|
|
506
|
+
if (loadingStates.remove(playerId) != null) {
|
|
507
|
+
updateAllocator();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
protected void updateAllocator() {
|
|
512
|
+
if (loadingStates.isEmpty()) {
|
|
513
|
+
allocator.reset();
|
|
514
|
+
} else {
|
|
515
|
+
allocator.setTargetBufferSize(calculateTotalTargetBufferBytes());
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private static int getDefaultBufferSize(@C.TrackType int trackType) {
|
|
520
|
+
switch (trackType) {
|
|
521
|
+
case C.TRACK_TYPE_DEFAULT:
|
|
522
|
+
return DEFAULT_MUXED_BUFFER_SIZE;
|
|
523
|
+
case C.TRACK_TYPE_AUDIO:
|
|
524
|
+
return DEFAULT_AUDIO_BUFFER_SIZE;
|
|
525
|
+
case C.TRACK_TYPE_VIDEO:
|
|
526
|
+
return DEFAULT_VIDEO_BUFFER_SIZE;
|
|
527
|
+
case C.TRACK_TYPE_TEXT:
|
|
528
|
+
return DEFAULT_TEXT_BUFFER_SIZE;
|
|
529
|
+
case C.TRACK_TYPE_METADATA:
|
|
530
|
+
return DEFAULT_METADATA_BUFFER_SIZE;
|
|
531
|
+
case C.TRACK_TYPE_CAMERA_MOTION:
|
|
532
|
+
return DEFAULT_CAMERA_MOTION_BUFFER_SIZE;
|
|
533
|
+
case C.TRACK_TYPE_IMAGE:
|
|
534
|
+
return DEFAULT_IMAGE_BUFFER_SIZE;
|
|
535
|
+
case C.TRACK_TYPE_NONE:
|
|
536
|
+
return 0;
|
|
537
|
+
case C.TRACK_TYPE_UNKNOWN:
|
|
538
|
+
return DEFAULT_MIN_BUFFER_SIZE;
|
|
539
|
+
default:
|
|
540
|
+
throw new IllegalArgumentException();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private static void assertGreaterOrEqual(int value1, int value2, String name1, String name2) {
|
|
545
|
+
Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
protected static class PlayerLoadingState {
|
|
549
|
+
public boolean isLoading;
|
|
550
|
+
public int targetBufferBytes;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
@@ -52,7 +52,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
52
52
|
.setEnableDecoderFallback(true)
|
|
53
53
|
private var listeners: MutableList<WeakReference<VideoPlayerListener>> = mutableListOf()
|
|
54
54
|
private var currentPlayerView = MutableWeakReference<PlayerView?>(null)
|
|
55
|
-
val loadControl: VideoPlayerLoadControl = VideoPlayerLoadControl
|
|
55
|
+
val loadControl: VideoPlayerLoadControl = VideoPlayerLoadControl()
|
|
56
56
|
val subtitles: VideoPlayerSubtitles = VideoPlayerSubtitles(this)
|
|
57
57
|
val audioTracks: VideoPlayerAudioTracks = VideoPlayerAudioTracks(this)
|
|
58
58
|
val trackSelector = DefaultTrackSelector(context)
|