Skip to content

Commit

Permalink
fix(android): implement live configuration management (#3792)
Browse files Browse the repository at this point in the history
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* feat(android): implement live buffer configuration

* chore: fix linter
  • Loading branch information
freeboub authored May 20, 2024
1 parent 8fdc523 commit e16730d
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 1 deletion.
32 changes: 32 additions & 0 deletions android/src/main/java/com/brentvatne/common/api/BufferConfig.kt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<StreamKey> streamKeys = new ArrayList();
Expand Down
19 changes: 18 additions & 1 deletion docs/pages/component/props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -93,7 +107,10 @@ bufferConfig={{
bufferForPlaybackMs: 2500,
bufferForPlaybackAfterRebufferMs: 5000,
backBufferDurationMs: 120000,
cacheSizeMB: 0
cacheSizeMB: 0,
live: {
targetOffsetMs: 500,
},
}}
```

Expand Down
3 changes: 3 additions & 0 deletions examples/basic/src/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
9 changes: 9 additions & 0 deletions src/specs/VideoNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, 'Default'>;

type BufferConfig = Readonly<{
Expand All @@ -105,6 +113,7 @@ type BufferConfig = Readonly<{
minBackBufferMemoryReservePercent?: Float;
minBufferMemoryReservePercent?: Float;
cacheSizeMB?: Float;
live?: BufferConfigLive;
}>;

type SubtitleStyle = Readonly<{
Expand Down
9 changes: 9 additions & 0 deletions src/types/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -84,6 +92,7 @@ export type BufferConfig = {
minBackBufferMemoryReservePercent?: number;
minBufferMemoryReservePercent?: number;
cacheSizeMB?: number;
live?: BufferConfigLive;
};

export enum SelectedTrackType {
Expand Down

0 comments on commit e16730d

Please sign in to comment.