Skip to content

Commit

Permalink
Fix(android): restart issue react76 (#4302)
Browse files Browse the repository at this point in the history
* fix: upgrade to expo 54
* fix: more bufferConfig inside source
- restart issue on react 0.76
- fix constness
- deprecate bufferConfig in root props
- update documentation
  • Loading branch information
freeboub authored Nov 24, 2024
1 parent daaac97 commit 2b7c215
Show file tree
Hide file tree
Showing 20 changed files with 908 additions and 440 deletions.
7 changes: 4 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ def configStringPath = ExoplayerDependencies
.concat("buildFromSource:$media3_buildFromSource")
.md5()

if (isNewArchitectureEnabled()) {
apply plugin: "com.facebook.react"
}
// commented as new architecture not yet fully supported
// if (isNewArchitectureEnabled()) {
// apply plugin: "com.facebook.react"
// }

android {
if (supportsNamespace()) {
Expand Down
30 changes: 15 additions & 15 deletions android/src/main/java/com/brentvatne/common/api/BufferConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
11 changes: 10 additions & 1 deletion android/src/main/java/com/brentvatne/common/api/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ class Source {
*/
var adsProps: AdsProps? = null

/*
* buffering configuration
*/
var bufferConfig = BufferConfig()

/**
* The list of sideLoaded text tracks
*/
Expand All @@ -95,7 +100,8 @@ class Source {
cmcdProps == other.cmcdProps &&
sideLoadedTextTracks == other.sideLoadedTextTracks &&
adsProps == other.adsProps &&
minLoadRetryCount == other.minLoadRetryCount
minLoadRetryCount == other.minLoadRetryCount &&
bufferConfig == other.bufferConfig
)
}

Expand Down Expand Up @@ -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? {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
53 changes: 53 additions & 0 deletions docs/pages/component/props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ A Boolean value that indicates whether the player should automatically delay pla

### `bufferConfig`

> [!WARNING]
> Deprecated, use source.bufferConfig instead
<PlatformsList types={['Android']} />

Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
Expand Down Expand Up @@ -907,6 +910,56 @@ source={{
}}
```

### `bufferConfig`

<PlatformsList types={['Android']} />

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`
<PlatformsList types={['Android']} />
Expand Down
3 changes: 1 addition & 2 deletions examples/bare/src/BasicExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ const BasicExample = () => {
};

useEffect(() => {
videoRef.current?.setSource(currentSrc);
videoRef.current?.setSource({...currentSrc, bufferConfig: _bufferConfig });
}, [currentSrc]);

return (
Expand Down Expand Up @@ -284,7 +284,6 @@ const BasicExample = () => {
selectedAudioTrack={selectedAudioTrack}
selectedVideoTrack={selectedVideoTrack}
playInBackground={false}
bufferConfig={_bufferConfig}
preventsDisplaySleepDuringVideoPlayback={true}
renderLoader={_renderLoader}
onPlaybackRateChange={onPlaybackRateChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader

import expo.modules.ApplicationLifecycleDispatcher
Expand Down Expand Up @@ -42,7 +43,7 @@ class MainApplication : Application(), ReactApplication {

override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
Expand Down
10 changes: 5 additions & 5 deletions examples/expo/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

buildscript {
ext {
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0'
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23')
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.23'
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0'
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24')
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '35')
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.24'

ndkVersion = "26.1.10909125"
}
Expand Down
Loading

0 comments on commit 2b7c215

Please sign in to comment.