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 297a1bf65a..b3889888bd 100644 --- a/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt +++ b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt @@ -1,6 +1,7 @@ package com.brentvatne.common.api import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetDouble +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetFloat import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt import com.facebook.react.bridge.ReadableMap @@ -20,6 +21,35 @@ class BufferConfig { var minBackBufferMemoryReservePercent = BufferConfigPropUnsetDouble var minBufferMemoryReservePercent = BufferConfigPropUnsetDouble + var live: Live = Live() + + class Live { + var maxPlaybackSpeed: Float = BufferConfigPropUnsetDouble.toFloat() + var minPlaybackSpeed: Float = BufferConfigPropUnsetDouble.toFloat() + var maxOffsetMs: Long = BufferConfigPropUnsetInt.toLong() + var minOffsetMs: Long = BufferConfigPropUnsetInt.toLong() + 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" + + @JvmStatic + fun parse(src: ReadableMap?): Live { + val live = Live() + live.maxPlaybackSpeed = safeGetFloat(src, PROP_BUFFER_CONFIG_LIVE_MAX_PLAYBACK_SPEED, BufferConfigPropUnsetDouble.toFloat()) + live.minPlaybackSpeed = safeGetFloat(src, PROP_BUFFER_CONFIG_LIVE_MIN_PLAYBACK_SPEED, BufferConfigPropUnsetDouble.toFloat()) + live.maxOffsetMs = safeGetInt(src, PROP_BUFFER_CONFIG_LIVE_MAX_OFFSET_MS, BufferConfigPropUnsetInt).toLong() + live.minOffsetMs = safeGetInt(src, PROP_BUFFER_CONFIG_LIVE_MIN_OFFSET_MS, BufferConfigPropUnsetInt).toLong() + live.targetOffsetMs = safeGetInt(src, PROP_BUFFER_CONFIG_LIVE_TARGET_OFFSET_MS, BufferConfigPropUnsetInt).toLong() + return live + } + } + } + companion object { val BufferConfigPropUnsetInt = -1 val BufferConfigPropUnsetDouble = -1.0 @@ -33,6 +63,7 @@ class BufferConfig { 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" @JvmStatic fun parse(src: ReadableMap?): BufferConfig { @@ -59,6 +90,7 @@ class BufferConfig { BufferConfigPropUnsetDouble ) bufferConfig.backBufferDurationMs = safeGetInt(src, PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS, BufferConfigPropUnsetInt) + bufferConfig.live = Live.parse(src.getMap(PROP_BUFFER_CONFIG_LIVE)) } return bufferConfig } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ConfigurationUtils.kt b/android/src/main/java/com/brentvatne/exoplayer/ConfigurationUtils.kt new file mode 100644 index 0000000000..6900a037f6 --- /dev/null +++ b/android/src/main/java/com/brentvatne/exoplayer/ConfigurationUtils.kt @@ -0,0 +1,36 @@ +package com.brentvatne.exoplayer + +import androidx.media3.common.MediaItem.LiveConfiguration +import com.brentvatne.common.api.BufferConfig +import com.brentvatne.common.api.BufferConfig.Live + +/** + * Helper functions to create exoplayer configuration + */ +object ConfigurationUtils { + + /** + * Create a media3.LiveConfiguration.Builder from parsed BufferConfig + */ + @JvmStatic + fun getLiveConfiguration(bufferConfig: BufferConfig): LiveConfiguration.Builder { + val liveConfiguration = LiveConfiguration.Builder() + val live: Live = bufferConfig.live + if (bufferConfig.live.maxOffsetMs >= 0) { + liveConfiguration.setMaxOffsetMs(live.maxOffsetMs) + } + if (bufferConfig.live.maxPlaybackSpeed >= 0) { + liveConfiguration.setMaxPlaybackSpeed(live.maxPlaybackSpeed) + } + if (bufferConfig.live.targetOffsetMs >= 0) { + liveConfiguration.setTargetOffsetMs(live.targetOffsetMs) + } + if (bufferConfig.live.minOffsetMs >= 0) { + liveConfiguration.setMinOffsetMs(live.minOffsetMs) + } + if (bufferConfig.live.minPlaybackSpeed >= 0) { + liveConfiguration.setMinPlaybackSpeed(live.minPlaybackSpeed) + } + return liveConfiguration + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 64de3f1577..13f253664a 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -965,6 +965,9 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi ); } + MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(bufferConfig); + mediaItemBuilder.setLiveConfiguration(liveConfiguration.build()); + MediaSource.Factory mediaSourceFactory; DrmSessionManagerProvider drmProvider; List streamKeys = new ArrayList(); diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index 5bbbb643d0..f6d7fcaf0d 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -83,6 +83,20 @@ Adjust the buffer settings. This prop takes an object with one or more of the pr | 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: @@ -93,7 +107,10 @@ bufferConfig={{ bufferForPlaybackMs: 2500, bufferForPlaybackAfterRebufferMs: 5000, backBufferDurationMs: 120000, - cacheSizeMB: 0 + cacheSizeMB: 0, + live: { + targetOffsetMs: 500, + }, }} ``` diff --git a/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index d9a6a8638d..37378baef2 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -930,6 +930,9 @@ class VideoPlayer extends Component { bufferForPlaybackMs: 2500, bufferForPlaybackAfterRebufferMs: 5000, cacheSizeMB: this.state.useCache ? 200 : 0, + live: { + targetOffsetMs: 500, + }, }} preventsDisplaySleepDuringVideoPlayback={true} poster={this.state.poster} diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 53cec03b46..dac5b5fde9 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -93,6 +93,14 @@ export type Seek = Readonly<{ tolerance?: Float; }>; +type BufferConfigLive = Readonly<{ + maxPlaybackSpeed?: Float; + minPlaybackSpeed?: Float; + maxOffsetMs?: Int32; + minOffsetMs?: Int32; + targetOffsetMs?: Int32; +}>; + type BufferingStrategyType = WithDefault; type BufferConfig = Readonly<{ @@ -105,6 +113,7 @@ type BufferConfig = Readonly<{ minBackBufferMemoryReservePercent?: Float; minBufferMemoryReservePercent?: Float; cacheSizeMB?: Float; + live?: BufferConfigLive; }>; type SubtitleStyle = Readonly<{ diff --git a/src/types/video.ts b/src/types/video.ts index 56694ecd24..3bff67356f 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -74,6 +74,14 @@ export enum BufferingStrategyType { DEPENDING_ON_MEMORY = 'DependingOnMemory', } +export type BufferConfigLive = { + maxPlaybackSpeed?: number; + minPlaybackSpeed?: number; + maxOffsetMs?: number; + minOffsetMs?: number; + targetOffsetMs?: number; +}; + export type BufferConfig = { minBufferMs?: number; maxBufferMs?: number; @@ -84,6 +92,7 @@ export type BufferConfig = { minBackBufferMemoryReservePercent?: number; minBufferMemoryReservePercent?: number; cacheSizeMB?: number; + live?: BufferConfigLive; }; export enum SelectedTrackType {