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.
Files changed (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/android/build.gradle +3 -3
  3. package/android/src/main/java/expo/modules/video/player/DefaultLoadControl.java +552 -0
  4. package/android/src/main/java/expo/modules/video/player/VideoPlayer.kt +1 -1
  5. package/android/src/main/java/expo/modules/video/player/VideoPlayerLoadControl.kt +18 -485
  6. package/android/src/main/java/expo/modules/video/records/BufferOptions.kt +4 -3
  7. package/expo-module.config.json +1 -1
  8. 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
  9. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.md5 +1 -0
  10. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha1 +1 -0
  11. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha256 +1 -0
  12. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6-sources.jar.sha512 +1 -0
  13. 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
  14. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.md5 +1 -0
  15. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha1 +1 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha256 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.aar.sha512 +1 -0
  18. 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
  19. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.md5 +1 -0
  20. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha1 +1 -0
  21. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha256 +1 -0
  22. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.module.sha512 +1 -0
  23. 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
  24. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.md5 +1 -0
  25. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha1 +1 -0
  26. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha256 +1 -0
  27. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.6/expo.modules.video-3.0.6.pom.sha512 +1 -0
  28. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml +4 -4
  29. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.md5 +1 -1
  30. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha1 +1 -1
  31. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha256 +1 -1
  32. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha512 +1 -1
  33. package/package.json +3 -3
  34. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.md5 +0 -1
  35. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha1 +0 -1
  36. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha256 +0 -1
  37. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha512 +0 -1
  38. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.md5 +0 -1
  39. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha1 +0 -1
  40. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha256 +0 -1
  41. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha512 +0 -1
  42. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.md5 +0 -1
  43. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha1 +0 -1
  44. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha256 +0 -1
  45. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha512 +0 -1
  46. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.md5 +0 -1
  47. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha1 +0 -1
  48. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha256 +0 -1
  49. 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
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '3.0.5'
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.5'
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.4.0"
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.Builder().build()
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)