chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size (#3739)

This commit is contained in:
Olivier Bouillet 2024-05-06 22:04:40 +02:00 committed by GitHub
parent e05da4e9fe
commit 8eb31e82c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 104 additions and 70 deletions

View File

@ -0,0 +1,66 @@
package com.brentvatne.common.api
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetDouble
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt
import com.facebook.react.bridge.ReadableMap
/**
* Class representing bufferConfig for host.
* Only generic code here, no reference to the player.
* By default, if application don't provide input field, -1 is set instead
*/
class BufferConfig {
var cacheSize = BufferConfigPropUnsetInt
var minBufferMs = BufferConfigPropUnsetInt
var maxBufferMs = BufferConfigPropUnsetInt
var bufferForPlaybackMs = BufferConfigPropUnsetInt
var bufferForPlaybackAfterRebufferMs = BufferConfigPropUnsetInt
var backBufferDurationMs = BufferConfigPropUnsetInt
var maxHeapAllocationPercent = BufferConfigPropUnsetDouble
var minBackBufferMemoryReservePercent = BufferConfigPropUnsetDouble
var minBufferMemoryReservePercent = BufferConfigPropUnsetDouble
companion object {
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"
@JvmStatic
fun parse(src: ReadableMap?): BufferConfig {
val bufferConfig = BufferConfig()
if (src != null) {
bufferConfig.cacheSize = safeGetInt(src, PROP_BUFFER_CONFIG_CACHE_SIZE, BufferConfigPropUnsetInt)
bufferConfig.minBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MIN_BUFFER_MS, BufferConfigPropUnsetInt)
bufferConfig.maxBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MAX_BUFFER_MS, BufferConfigPropUnsetInt)
bufferConfig.bufferForPlaybackMs = safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS, BufferConfigPropUnsetInt)
bufferConfig.bufferForPlaybackAfterRebufferMs =
safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, BufferConfigPropUnsetInt)
bufferConfig.maxHeapAllocationPercent =
safeGetDouble(src, PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT, BufferConfigPropUnsetDouble)
bufferConfig.minBackBufferMemoryReservePercent = safeGetDouble(
src,
PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT,
BufferConfigPropUnsetDouble
)
bufferConfig.minBufferMemoryReservePercent =
safeGetDouble(
src,
PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT,
BufferConfigPropUnsetDouble
)
bufferConfig.backBufferDurationMs = safeGetInt(src, PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS, BufferConfigPropUnsetInt)
}
return bufferConfig
}
}
}

View File

@ -15,7 +15,7 @@ object RNVSimpleCache {
var cacheDataSourceFactory: DataSource.Factory? = null var cacheDataSourceFactory: DataSource.Factory? = null
fun setSimpleCache(context: Context, cacheSize: Int, factory: HttpDataSource.Factory) { fun setSimpleCache(context: Context, cacheSize: Int, factory: HttpDataSource.Factory) {
if (cacheDataSourceFactory != null || cacheSize == 0) return if (cacheDataSourceFactory != null || cacheSize <= 0) return
simpleCache = SimpleCache( simpleCache = SimpleCache(
File(context.cacheDir, "RNVCache"), File(context.cacheDir, "RNVCache"),
LeastRecentlyUsedCacheEvictor( LeastRecentlyUsedCacheEvictor(

View File

@ -96,6 +96,7 @@ import androidx.media3.extractor.metadata.id3.Id3Frame;
import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.extractor.metadata.id3.TextInformationFrame;
import androidx.media3.ui.LegacyPlayerControlView; import androidx.media3.ui.LegacyPlayerControlView;
import com.brentvatne.common.api.BufferConfig;
import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.api.TimedMetadata; import com.brentvatne.common.api.TimedMetadata;
@ -185,18 +186,11 @@ public class ReactExoplayerView extends FrameLayout implements
private AudioOutput audioOutput = AudioOutput.SPEAKER; private AudioOutput audioOutput = AudioOutput.SPEAKER;
private float audioVolume = 1f; private float audioVolume = 1f;
private int minLoadRetryCount = 3; private int minLoadRetryCount = 3;
private BufferConfig bufferConfig = new BufferConfig();
private int maxBitRate = 0; private int maxBitRate = 0;
private boolean hasDrmFailed = false; private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false; private boolean isUsingContentResolution = false;
private boolean selectTrackWhenReady = false; private boolean selectTrackWhenReady = false;
private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
private Handler mainHandler; private Handler mainHandler;
private Runnable mainRunnable; private Runnable mainRunnable;
private DataSource.Factory cacheDataSourceFactory; private DataSource.Factory cacheDataSourceFactory;
@ -497,19 +491,32 @@ public class ReactExoplayerView extends FrameLayout implements
private class RNVLoadControl extends DefaultLoadControl { private class RNVLoadControl extends DefaultLoadControl {
private final int availableHeapInBytes; private final int availableHeapInBytes;
private final Runtime runtime; private final Runtime runtime;
public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) {
super(allocator, super(allocator,
minBufferMs, config.getMinBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
maxBufferMs, ? config.getMinBufferMs()
bufferForPlaybackMs, : DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
bufferForPlaybackAfterRebufferMs, config.getMaxBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
targetBufferBytes, ? config.getMaxBufferMs()
prioritizeTimeOverSizeThresholds, : DefaultLoadControl.DEFAULT_MAX_BUFFER_MS,
backBufferDurationMs, config.getBufferForPlaybackMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
retainBackBufferFromKeyframe); ? config.getBufferForPlaybackMs()
: DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS ,
config.getBufferForPlaybackAfterRebufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBufferForPlaybackAfterRebufferMs()
: DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
-1,
true,
config.getBackBufferDurationMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBackBufferDurationMs()
: DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME);
runtime = Runtime.getRuntime(); runtime = Runtime.getRuntime();
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE); ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024); double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
? bufferConfig.getMaxHeapAllocationPercent()
: DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024);
} }
@Override @Override
@ -524,6 +531,9 @@ public class ReactExoplayerView extends FrameLayout implements
} }
long usedMemory = runtime.totalMemory() - runtime.freeMemory(); long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory; long freeMemory = runtime.maxMemory() - usedMemory;
double minBufferMemoryReservePercent = bufferConfig.getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
? bufferConfig.getMinBufferMemoryReservePercent()
: ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory(); long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory();
long bufferedMs = bufferedDurationUs / (long)1000; long bufferedMs = bufferedDurationUs / (long)1000;
if (reserveMemory > freeMemory && bufferedMs > 2000) { if (reserveMemory > freeMemory && bufferedMs > 2000) {
@ -604,14 +614,7 @@ public class ReactExoplayerView extends FrameLayout implements
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl( RNVLoadControl loadControl = new RNVLoadControl(
allocator, allocator,
minBufferMs, bufferConfig
maxBufferMs,
bufferForPlaybackMs,
bufferForPlaybackAfterRebufferMs,
-1,
true,
backBufferDurationMs,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
); );
DefaultRenderersFactory renderersFactory = DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext()) new DefaultRenderersFactory(getContext())
@ -2034,26 +2037,18 @@ public class ReactExoplayerView extends FrameLayout implements
exoPlayerView.setHideShutterView(hideShutterView); exoPlayerView.setHideShutterView(hideShutterView);
} }
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent, int newBackBufferDurationMs, int cacheSize) { public void setBufferConfig(BufferConfig config) {
minBufferMs = newMinBufferMs; bufferConfig = config;
maxBufferMs = newMaxBufferMs; if (bufferConfig.getCacheSize() > 0) {
bufferForPlaybackMs = newBufferForPlaybackMs;
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
maxHeapAllocationPercent = newMaxHeapAllocationPercent;
minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent;
minBufferMemoryReservePercent = newMinBufferMemoryReservePercent;
if (cacheSize > 0) {
RNVSimpleCache.INSTANCE.setSimpleCache( RNVSimpleCache.INSTANCE.setSimpleCache(
this.getContext(), this.getContext(),
cacheSize, bufferConfig.getCacheSize(),
buildHttpDataSourceFactory(false) buildHttpDataSourceFactory(false)
); );
cacheDataSourceFactory = RNVSimpleCache.INSTANCE.getCacheDataSourceFactory(); cacheDataSourceFactory = RNVSimpleCache.INSTANCE.getCacheDataSourceFactory();
} else { } else {
cacheDataSourceFactory = null; cacheDataSourceFactory = null;
} }
backBufferDurationMs = newBackBufferDurationMs;
releasePlayer(); releasePlayer();
initializePlayer(); initializePlayer();
} }

View File

@ -11,6 +11,7 @@ import androidx.media3.common.util.Util;
import androidx.media3.datasource.RawResourceDataSource; import androidx.media3.datasource.RawResourceDataSource;
import androidx.media3.exoplayer.DefaultLoadControl; import androidx.media3.exoplayer.DefaultLoadControl;
import com.brentvatne.common.api.BufferConfig;
import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.react.VideoEventEmitter; import com.brentvatne.common.react.VideoEventEmitter;
@ -59,15 +60,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_AUDIO_OUTPUT = "audioOutput"; private static final String PROP_AUDIO_OUTPUT = "audioOutput";
private static final String PROP_VOLUME = "volume"; private static final String PROP_VOLUME = "volume";
private static final String PROP_BUFFER_CONFIG = "bufferConfig"; private static final String PROP_BUFFER_CONFIG = "bufferConfig";
private static final String PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB";
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent";
private static final String PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent";
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent";
private static final String PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs";
private static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback"; private static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback";
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval"; private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth"; private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
@ -401,30 +393,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
videoView.setShutterColor(color == null ? Color.BLACK : color); videoView.setShutterColor(color == null ? Color.BLACK : color);
} }
@ReactProp(name = PROP_BUFFER_CONFIG) @ReactProp(name = PROP_BUFFER_CONFIG)
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) { public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
int cacheSize = 0; BufferConfig config = BufferConfig.parse(bufferConfig);
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; videoView.setBufferConfig(config);
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
if (bufferConfig != null) {
cacheSize = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_CACHE_SIZE, 0);
minBufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_MIN_BUFFER_MS, minBufferMs);
maxBufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_MAX_BUFFER_MS, maxBufferMs);
bufferForPlaybackMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS, bufferForPlaybackMs);
bufferForPlaybackAfterRebufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, bufferForPlaybackAfterRebufferMs);
maxHeapAllocationPercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT, maxHeapAllocationPercent);
minBackBufferMemoryReservePercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT, minBackBufferMemoryReservePercent);
minBufferMemoryReservePercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT, minBufferMemoryReservePercent);
backBufferDurationMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS, backBufferDurationMs);
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, maxHeapAllocationPercent, minBackBufferMemoryReservePercent, minBufferMemoryReservePercent, backBufferDurationMs, cacheSize);
}
} }
@ReactProp(name = PROP_DEBUG, defaultBoolean = false) @ReactProp(name = PROP_DEBUG, defaultBoolean = false)