diff --git a/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt
index b3889888bd..4730e9f15a 100644
--- a/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt
+++ b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt
@@ -31,11 +31,11 @@ class BufferConfig {
var targetOffsetMs: Long = BufferConfigPropUnsetInt.toLong()
companion object {
- private val PROP_BUFFER_CONFIG_LIVE_MAX_PLAYBACK_SPEED = "maxPlaybackSpeed"
- private val PROP_BUFFER_CONFIG_LIVE_MIN_PLAYBACK_SPEED = "minPlaybackSpeed"
- private val PROP_BUFFER_CONFIG_LIVE_MAX_OFFSET_MS = "maxOffsetMs"
- private val PROP_BUFFER_CONFIG_LIVE_MIN_OFFSET_MS = "minOffsetMs"
- private val PROP_BUFFER_CONFIG_LIVE_TARGET_OFFSET_MS = "targetOffsetMs"
+ private const val PROP_BUFFER_CONFIG_LIVE_MAX_PLAYBACK_SPEED = "maxPlaybackSpeed"
+ private const val PROP_BUFFER_CONFIG_LIVE_MIN_PLAYBACK_SPEED = "minPlaybackSpeed"
+ private const val PROP_BUFFER_CONFIG_LIVE_MAX_OFFSET_MS = "maxOffsetMs"
+ private const val PROP_BUFFER_CONFIG_LIVE_MIN_OFFSET_MS = "minOffsetMs"
+ private const val PROP_BUFFER_CONFIG_LIVE_TARGET_OFFSET_MS = "targetOffsetMs"
@JvmStatic
fun parse(src: ReadableMap?): Live {
@@ -54,16 +54,16 @@ class BufferConfig {
val BufferConfigPropUnsetInt = -1
val BufferConfigPropUnsetDouble = -1.0
- private val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB"
- private val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs"
- private val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs"
- private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs"
- private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs"
- private val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent"
- private val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent"
- private val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent"
- private val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs"
- private val PROP_BUFFER_CONFIG_LIVE = "live"
+ private const val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB"
+ private const val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs"
+ private const val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs"
+ private const val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs"
+ private const val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs"
+ private const val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent"
+ private const val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent"
+ private const val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent"
+ private const val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs"
+ private const val PROP_BUFFER_CONFIG_LIVE = "live"
@JvmStatic
fun parse(src: ReadableMap?): BufferConfig {
diff --git a/android/src/main/java/com/brentvatne/common/api/Source.kt b/android/src/main/java/com/brentvatne/common/api/Source.kt
index 0f6a349103..2d5ede07d3 100644
--- a/android/src/main/java/com/brentvatne/common/api/Source.kt
+++ b/android/src/main/java/com/brentvatne/common/api/Source.kt
@@ -74,6 +74,11 @@ class Source {
*/
var adsProps: AdsProps? = null
+ /*
+ * buffering configuration
+ */
+ var bufferConfig = BufferConfig()
+
/**
* The list of sideLoaded text tracks
*/
@@ -95,7 +100,8 @@ class Source {
cmcdProps == other.cmcdProps &&
sideLoadedTextTracks == other.sideLoadedTextTracks &&
adsProps == other.adsProps &&
- minLoadRetryCount == other.minLoadRetryCount
+ minLoadRetryCount == other.minLoadRetryCount &&
+ bufferConfig == other.bufferConfig
)
}
@@ -164,6 +170,7 @@ class Source {
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"
private const val PROP_SRC_TEXT_TRACKS = "textTracks"
private const val PROP_SRC_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount"
+ private const val PROP_SRC_BUFFER_CONFIG = "bufferConfig"
@SuppressLint("DiscouragedApi")
private fun getUriFromAssetId(context: Context, uriString: String): Uri? {
@@ -229,6 +236,8 @@ class Source {
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)
source.sideLoadedTextTracks = SideLoadedTextTrackList.parse(safeGetArray(src, PROP_SRC_TEXT_TRACKS))
source.minLoadRetryCount = safeGetInt(src, PROP_SRC_MIN_LOAD_RETRY_COUNT, 3)
+ source.bufferConfig = BufferConfig.parse(safeGetMap(src, PROP_SRC_BUFFER_CONFIG))
+
val propSrcHeadersArray = safeGetArray(src, PROP_SRC_HEADERS)
if (propSrcHeadersArray != null) {
if (propSrcHeadersArray.size() > 0) {
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 06b1658c5f..c6d52c7c73 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -206,7 +206,6 @@ public class ReactExoplayerView extends FrameLayout implements
private float rate = 1f;
private AudioOutput audioOutput = AudioOutput.SPEAKER;
private float audioVolume = 1f;
- private BufferConfig bufferConfig = new BufferConfig();
private int maxBitRate = 0;
private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false;
@@ -691,7 +690,7 @@ public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) {
runtime = Runtime.getRuntime();
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
- ? bufferConfig.getMaxHeapAllocationPercent()
+ ? config.getMaxHeapAllocationPercent()
: DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024);
}
@@ -710,8 +709,8 @@ public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurat
}
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory;
- double minBufferMemoryReservePercent = bufferConfig.getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
- ? bufferConfig.getMinBufferMemoryReservePercent()
+ double minBufferMemoryReservePercent = source.getBufferConfig().getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
+ ? source.getBufferConfig().getMinBufferMemoryReservePercent()
: ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
long reserveMemory = (long) minBufferMemoryReservePercent * runtime.maxMemory();
long bufferedMs = bufferedDurationUs / (long) 1000;
@@ -743,10 +742,20 @@ private void initializePlayer() {
if (runningSource.getUri() == null) {
return;
}
+
if (player == null) {
// Initialize core configuration and listeners
initializePlayerCore(self);
}
+ if (source.getBufferConfig().getCacheSize() > 0) {
+ RNVSimpleCache.INSTANCE.setSimpleCache(
+ this.getContext(),
+ source.getBufferConfig().getCacheSize()
+ );
+ useCache = true;
+ } else {
+ useCache = false;
+ }
if (playerNeedsSource) {
// Will force display of shutter view if needed
exoPlayerView.updateShutterViewVisibility();
@@ -813,7 +822,7 @@ private void initializePlayerCore(ReactExoplayerView self) {
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl(
allocator,
- bufferConfig
+ source.getBufferConfig()
);
DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
@@ -1119,7 +1128,7 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi
}
}
- MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(bufferConfig);
+ MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(source.getBufferConfig());
mediaItemBuilder.setLiveConfiguration(liveConfiguration.build());
MediaSource.Factory mediaSourceFactory;
@@ -2368,21 +2377,6 @@ public void setHideShutterView(boolean hideShutterView) {
exoPlayerView.setHideShutterView(hideShutterView);
}
- public void setBufferConfig(BufferConfig config) {
- bufferConfig = config;
- if (bufferConfig.getCacheSize() > 0) {
- RNVSimpleCache.INSTANCE.setSimpleCache(
- this.getContext(),
- bufferConfig.getCacheSize()
- );
- useCache = true;
- } else {
- useCache = false;
- }
- releasePlayer();
- initializePlayer();
- }
-
@Override
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
DebugLog.d("DRM Info", "onDrmKeysLoaded");
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
index a2bebb8b01..2ac8dd1665 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
@@ -2,7 +2,6 @@ package com.brentvatne.exoplayer
import android.graphics.Color
import android.util.Log
-import com.brentvatne.common.api.BufferConfig
import com.brentvatne.common.api.BufferingStrategy
import com.brentvatne.common.api.ControlsConfig
import com.brentvatne.common.api.ResizeMode
@@ -36,7 +35,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
private const val PROP_MUTED = "muted"
private const val PROP_AUDIO_OUTPUT = "audioOutput"
private const val PROP_VOLUME = "volume"
- private const val PROP_BUFFER_CONFIG = "bufferConfig"
private const val PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK =
"preventsDisplaySleepDuringVideoPlayback"
private const val PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval"
@@ -242,12 +240,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
videoView.setShutterColor(color)
}
- @ReactProp(name = PROP_BUFFER_CONFIG)
- fun setBufferConfig(videoView: ReactExoplayerView, bufferConfig: ReadableMap?) {
- val config = BufferConfig.parse(bufferConfig)
- videoView.setBufferConfig(config)
- }
-
@ReactProp(name = PROP_SHOW_NOTIFICATION_CONTROLS)
fun setShowNotificationControls(videoView: ReactExoplayerView, showNotificationControls: Boolean) {
videoView.setShowNotificationControls(showNotificationControls)
diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx
index e205e6bad0..9854ed768b 100644
--- a/docs/pages/component/props.mdx
+++ b/docs/pages/component/props.mdx
@@ -52,6 +52,9 @@ A Boolean value that indicates whether the player should automatically delay pla
### `bufferConfig`
+> [!WARNING]
+> Deprecated, use source.bufferConfig instead
+
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
@@ -907,6 +910,56 @@ source={{
}}
```
+### `bufferConfig`
+
+
+
+Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
+
+| Property | Type | Description |
+| --------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| minBufferMs | number | The default minimum duration of media that the player will attempt to ensure is buffered at all times, in milliseconds. |
+| maxBufferMs | number | The default maximum duration of media that the player will attempt to buffer, in milliseconds. |
+| bufferForPlaybackMs | number | The default duration of media that must be buffered for playback to start or resume following a user action such as a seek, in milliseconds. |
+| bufferForPlaybackAfterRebufferMs | number | The default duration of media that must be buffered for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action. |
+| backBufferDurationMs | number | The number of milliseconds of buffer to keep before the current position. This allows rewinding without rebuffering within that duration. |
+| maxHeapAllocationPercent | number | The percentage of available heap that the video can use to buffer, between 0 and 1 |
+| minBackBufferMemoryReservePercent | number | The percentage of available app memory at which during startup the back buffer will be disabled, between 0 and 1 |
+| minBufferMemoryReservePercent | number | The percentage of available app memory to keep in reserve that prevents buffer from using it, between 0 and 1 |
+| cacheSizeMB | number | Cache size in MB, enabling this to prevent new src requests and save bandwidth while repeating videos, or 0 to disable. Android only. |
+| live | object | Object containing another config set for live playback configuration, see next table |
+
+
+Description of live object:
+
+| Property | Type | Description |
+| --------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| maxPlaybackSpeed | number | The maximum playback speed the player can use to catch up when trying to reach the target live offset. |
+| minPlaybackSpeed | number | The minimum playback speed the player can use to fall back when trying to reach the target live offset. |
+| maxOffsetMs | number | The maximum allowed live offset. Even when adjusting the offset to current network conditions, the player will not attempt to get above this offset during playback. |
+| minOffsetMs | number | The minimum allowed live offset. Even when adjusting the offset to current network conditions, the player will not attempt to get below this offset during playback. |
+| targetOffsetMs | number | The target live offset. The player will attempt to get close to this live offset during playback if possible. |
+
+For android, more informations about live configuration can be find [here](https://developer.android.com/media/media3/exoplayer/live-streaming?hl=en)
+
+Example with default values:
+
+```javascript
+bufferConfig={{
+ minBufferMs: 15000,
+ maxBufferMs: 50000,
+ bufferForPlaybackMs: 2500,
+ bufferForPlaybackAfterRebufferMs: 5000,
+ backBufferDurationMs: 120000,
+ cacheSizeMB: 0,
+ live: {
+ targetOffsetMs: 500,
+ },
+}}
+```
+
+Please note that the Android cache is a global cache that is shared among all components; individual components can still opt out of caching behavior by setting cacheSizeMB to 0, but multiple components with a positive cacheSizeMB will be sharing the same one, and the cache size will always be the first value set; it will not change during the app's lifecycle.
+
#### `minLoadRetryCount`
diff --git a/examples/expo/src/BasicExample.tsx b/examples/expo/src/BasicExample.tsx
index 2997cd58b2..af1aeede92 100644
--- a/examples/expo/src/BasicExample.tsx
+++ b/examples/expo/src/BasicExample.tsx
@@ -240,7 +240,7 @@ const BasicExample = () => {
};
useEffect(() => {
- videoRef.current?.setSource(currentSrc);
+ videoRef.current?.setSource({...currentSrc, bufferConfig: _bufferConfig });
}, [currentSrc]);
return (
@@ -284,7 +284,6 @@ const BasicExample = () => {
selectedAudioTrack={selectedAudioTrack}
selectedVideoTrack={selectedVideoTrack}
playInBackground={false}
- bufferConfig={_bufferConfig}
preventsDisplaySleepDuringVideoPlayback={true}
renderLoader={_renderLoader}
onPlaybackRateChange={onPlaybackRateChange}
diff --git a/src/Video.tsx b/src/Video.tsx
index 50cc352498..0aaa119904 100644
--- a/src/Video.tsx
+++ b/src/Video.tsx
@@ -108,6 +108,7 @@ const Video = forwardRef(
onAspectRatio,
localSourceEncryptionKeyScheme,
minLoadRetryCount,
+ bufferConfig,
...rest
},
ref,
@@ -219,6 +220,8 @@ const Video = forwardRef(
const _minLoadRetryCount =
_source.minLoadRetryCount || minLoadRetryCount;
+
+ const _bufferConfig = _source.bufferConfig || bufferConfig;
return {
uri,
isNetwork,
@@ -240,6 +243,7 @@ const Video = forwardRef(
textTracksAllowChunklessPreparation:
resolvedSource.textTracksAllowChunklessPreparation,
minLoadRetryCount: _minLoadRetryCount,
+ bufferConfig: _bufferConfig,
};
},
[
@@ -251,6 +255,7 @@ const Video = forwardRef(
minLoadRetryCount,
source?.cmcd,
textTracks,
+ bufferConfig,
],
);
diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts
index 907bfcb76c..d434d58534 100644
--- a/src/specs/VideoNativeComponent.ts
+++ b/src/specs/VideoNativeComponent.ts
@@ -51,6 +51,7 @@ export type VideoSrc = Readonly<{
textTracks?: TextTracks;
ad?: AdsConfig;
minLoadRetryCount?: Int32; // Android
+ bufferConfig?: BufferConfig; // Android
}>;
type DRMType = WithDefault;
@@ -358,7 +359,6 @@ export interface VideoNativeProps extends ViewProps {
restoreUserInterfaceForPIPStopCompletionHandler?: boolean;
debug?: DebugConfig;
showNotificationControls?: WithDefault; // Android, iOS
- bufferConfig?: BufferConfig; // Android
currentPlaybackTime?: Double; // Android
disableDisconnectError?: boolean; // Android
focusable?: boolean; // Android
diff --git a/src/types/video.ts b/src/types/video.ts
index f3118cfa71..6a213d1d4a 100644
--- a/src/types/video.ts
+++ b/src/types/video.ts
@@ -40,6 +40,7 @@ export type ReactVideoSourceProperties = {
textTracks?: TextTracks;
ad?: AdConfig;
minLoadRetryCount?: number; // Android
+ bufferConfig?: BufferConfig;
};
export type ReactVideoSource = Readonly<
@@ -289,6 +290,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
adLanguage?: ISO639_1;
audioOutput?: AudioOutput; // Mobile
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
+ /** @deprecated Use source.bufferConfig */
bufferConfig?: BufferConfig; // Android
bufferingStrategy?: BufferingStrategyType;
chapters?: Chapters[]; // iOS