Skip to content

Commit b024097

Browse files
Merge pull request #35 from scribd/griffinf/APT-10204-pass-loadcontrol-to-exoplayer
[APT-10204] pass loadcontrol to exoplayer
2 parents 67d37df + 9bf48ab commit b024097

File tree

9 files changed

+62
-27
lines changed

9 files changed

+62
-27
lines changed

Armadillo/src/main/java/com/scribd/armadillo/ArmadilloConfiguration.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package com.scribd.armadillo
22

3+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
4+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS
5+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_MIN_BUFFER_MS
6+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_MAX_BUFFER_MS
7+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS
8+
import com.google.android.exoplayer2.DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES
39
import com.scribd.armadillo.models.AudioPlayable
410
import com.scribd.armadillo.time.milliseconds
11+
import java.io.Serializable
512

613
/**
714
* Used to specify various settings when starting playback on a new [AudioPlayable]
@@ -11,10 +18,27 @@ import com.scribd.armadillo.time.milliseconds
1118
* @property maxDurationDiscrepancy Armadillo will output errors if the metadata for the audio duration doesn't match the
1219
* actual duration of playback. This value can be used to set the allowed maximum difference in seconds between stated vs. actual duration.
1320
* Can also be set to a negative value to ignore any discrepancies.
21+
* @property minBufferMs The minumum amount of audio attempted to be buffered at all times in milliseconds.
22+
* @property maxBufferMs The maximum amount of audio attempted to be buffered at all times in milliseconds.
23+
* @property bufferForPlaybackMs The duration of media that must be buffered for playback to start or
24+
* resume following a user action such as a seek, in milliseconds.
25+
* @property bufferForPlaybackAfterRebufferMs The duration of media that must be buffered for playback
26+
* to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion
27+
* rather than a user action.
28+
* @property targetBufferSize The desired size of the media buffer in bytes. An unset buffer size will
29+
* will be calculated based on the selected tracks.
30+
* @property prioritizeTimeOverSizeThresholds Whether the load control prioritizes buffer time constraints
31+
* over buffer size constraints.
1432
*/
1533
data class ArmadilloConfiguration(val initialOffset: Milliseconds = 0.milliseconds,
1634
val isAutoPlay: Boolean = true,
17-
val maxDurationDiscrepancy: Int = MAX_DISCREPANCY_DEFAULT) {
35+
val maxDurationDiscrepancy: Int = MAX_DISCREPANCY_DEFAULT,
36+
val minBufferMs: Int = DEFAULT_MIN_BUFFER_MS,
37+
val maxBufferMs: Int = DEFAULT_MAX_BUFFER_MS,
38+
val bufferForPlaybackMs: Int = DEFAULT_BUFFER_FOR_PLAYBACK_MS,
39+
val bufferForPlaybackAfterRebufferMs: Int = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
40+
val targetBufferSize: Int = DEFAULT_TARGET_BUFFER_BYTES,
41+
val prioritizeTimeOverSizeThresholds: Boolean = DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS): Serializable {
1842

1943
companion object {
2044
// Default duration discrepancy values in seconds

Armadillo/src/main/java/com/scribd/armadillo/ArmadilloPlayerChoreographer.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,8 @@ internal class ArmadilloPlayerChoreographer : ArmadilloPlayer {
268268
mediaSessionConnection.connectToMediaSession(object : MediaSessionConnection.Listener {
269269
override fun onConnectionCallback(transportControls: MediaControllerCompat.TransportControls) {
270270
val bundle = Bundle()
271+
bundle.putSerializable(Constants.Keys.KEY_ARMADILLO_CONFIG, config)
271272
bundle.putSerializable(Constants.Keys.KEY_AUDIO_PLAYABLE, audioPlayable)
272-
bundle.putSerializable(Constants.Keys.KEY_INITIAL_OFFSET, config.initialOffset)
273-
bundle.putBoolean(Constants.Keys.KEY_IS_AUTO_PLAY, config.isAutoPlay)
274-
bundle.putInt(Constants.Keys.KEY_MAX_DURATION_DISCREPANCY, config.maxDurationDiscrepancy)
275273
transportControls.playFromUri(audioPlayable.request.url.toUri(), bundle)
276274
updateProgressPollTask()
277275
}

Armadillo/src/main/java/com/scribd/armadillo/Constants.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,8 @@ object Constants {
2828
private val APP_NAME = R.string.arm_app_name
2929

3030
internal object Keys {
31+
const val KEY_ARMADILLO_CONFIG = "armadillo_config"
3132
const val KEY_AUDIO_PLAYABLE = "audio_playable"
32-
const val KEY_IS_AUTO_PLAY = "is_auto_play"
33-
const val KEY_MAX_DURATION_DISCREPANCY = "max_duration_discrepancy"
34-
const val KEY_PLAYBACK_READY = "playback_ready"
35-
const val KEY_INITIAL_OFFSET = "initial_offset"
36-
const val KEY_ERROR = "error"
3733
}
3834

3935
internal object DI {

Armadillo/src/main/java/com/scribd/armadillo/playback/ExoplayerExt.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.scribd.armadillo.playback
33
import android.content.Context
44
import com.google.android.exoplayer2.C
55
import com.google.android.exoplayer2.ExoPlayer
6+
import com.google.android.exoplayer2.LoadControl
67
import com.google.android.exoplayer2.RenderersFactory
78
import com.google.android.exoplayer2.audio.AudioAttributes
89
import com.google.android.exoplayer2.audio.AudioCapabilities
@@ -41,8 +42,9 @@ internal fun ExoPlayer.playerDuration(): Milliseconds? = if (duration == C.TIME_
4142
*
4243
* We provide our own renderers factory so that Proguard can remove any non-audio rendering code.
4344
*/
44-
internal fun createExoplayerInstance(context: Context, attributes: AudioAttributes): ExoPlayer =
45+
internal fun createExoplayerInstance(context: Context, attributes: AudioAttributes, loadControl: LoadControl): ExoPlayer =
4546
ExoPlayer.Builder(context, createRenderersFactory(context))
47+
.setLoadControl(loadControl)
4648
.build().apply {
4749
setAudioAttributes(attributes, true)
4850
}

Armadillo/src/main/java/com/scribd/armadillo/playback/MediaSessionCallback.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import androidx.annotation.VisibleForTesting
1313
import com.scribd.armadillo.ArmadilloConfiguration
1414
import com.scribd.armadillo.Constants
1515
import com.scribd.armadillo.Constants.AUDIO_POSITION_SHIFT_IN_MS
16-
import com.scribd.armadillo.Milliseconds
1716
import com.scribd.armadillo.StateStore
1817
import com.scribd.armadillo.actions.CustomMediaSessionAction
1918
import com.scribd.armadillo.actions.UpdateProgressAction
@@ -125,9 +124,6 @@ internal class MediaSessionCallback(private val onMediaSessionEventListener: OnM
125124

126125
override fun onPlayFromUri(uri: Uri, extras: Bundle) {
127126
val newAudioPlayable = extras.getSerializable(Constants.Keys.KEY_AUDIO_PLAYABLE) as AudioPlayable
128-
val isAutoPlay = extras.getBoolean(Constants.Keys.KEY_IS_AUTO_PLAY, false)
129-
val maxDurationDiscrepancy = extras.getInt(Constants.Keys.KEY_MAX_DURATION_DISCREPANCY,
130-
ArmadilloConfiguration.MAX_DISCREPANCY_DEFAULT)
131127

132128
if (newAudioPlayable == stateProvider.currentState.playbackInfo?.audioPlayable && isPlaying) {
133129
Log.v(TAG, "onPlayFromUri: already playing audioPlayable: ${newAudioPlayable.id} - Skipping setup")
@@ -138,10 +134,11 @@ internal class MediaSessionCallback(private val onMediaSessionEventListener: OnM
138134
onStop()
139135
}
140136

141-
@Suppress("UNCHECKED_CAST") val initialOffset = extras.getSerializable(Constants.Keys.KEY_INITIAL_OFFSET) as Milliseconds
137+
val config = extras.getSerializable(Constants.Keys.KEY_ARMADILLO_CONFIG) as ArmadilloConfiguration
138+
Log.v(TAG, "ArmadilloConfiguration: $config")
142139
playbackEngine = playbackEngineFactory.createPlaybackEngine(newAudioPlayable)
143-
playbackEngine?.beginPlayback(isAutoPlay, maxDurationDiscrepancy, initialOffset)
144-
playbackEngine?.seekTo(initialOffset)
140+
playbackEngine?.beginPlayback(config)
141+
playbackEngine?.seekTo(config.initialOffset)
145142

146143
isPlaying = true
147144

Armadillo/src/main/java/com/scribd/armadillo/playback/PlaybackEngine.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package com.scribd.armadillo.playback
22

33
import android.content.Context
44
import androidx.annotation.VisibleForTesting
5+
import com.google.android.exoplayer2.DefaultLoadControl
56
import com.google.android.exoplayer2.ExoPlayer
67
import com.google.android.exoplayer2.PlaybackParameters
78
import com.google.android.exoplayer2.Player
89
import com.google.android.exoplayer2.upstream.cache.Cache
10+
import com.scribd.armadillo.ArmadilloConfiguration
911
import com.scribd.armadillo.ArmadilloPlayerChoreographer
1012
import com.scribd.armadillo.Constants
1113
import com.scribd.armadillo.Milliseconds
@@ -46,7 +48,7 @@ internal interface AudioPlaybackEngine {
4648
*/
4749
var offloadAudio: Boolean
4850

49-
fun beginPlayback(isAutoPlay: Boolean, maxDurationDiscrepancy: Int, initialOffset: Milliseconds = 0.milliseconds)
51+
fun beginPlayback(config: ArmadilloConfiguration)
5052

5153
/**
5254
* Updates the request for the currently playing media. This could be to change the request headers.
@@ -118,18 +120,30 @@ internal class ExoplayerPlaybackEngine(private var audioPlayable: AudioPlayable)
118120
}
119121
}
120122

121-
override fun beginPlayback(isAutoPlay: Boolean, maxDurationDiscrepancy: Int, initialOffset: Milliseconds) {
122-
stateModifier.dispatch(NewAudioPlayableAction(audioPlayable, maxDurationDiscrepancy, initialOffset))
123+
override fun beginPlayback(config: ArmadilloConfiguration) {
124+
stateModifier.dispatch(NewAudioPlayableAction(
125+
audioPlayable = audioPlayable,
126+
maxDurationDiscrepancy = config.maxDurationDiscrepancy,
127+
initialOffset = config.initialOffset))
123128

124-
exoPlayer = createExoplayerInstance(context, audioAttributes.exoPlayerAttrs)
129+
val loadControl = DefaultLoadControl.Builder()
130+
.setBufferDurationsMs(config.minBufferMs,
131+
config.maxBufferMs,
132+
config.bufferForPlaybackMs,
133+
config.bufferForPlaybackAfterRebufferMs)
134+
.setTargetBufferBytes(config.targetBufferSize)
135+
.setPrioritizeTimeOverSizeThresholds(config.prioritizeTimeOverSizeThresholds)
136+
.build()
137+
138+
exoPlayer = createExoplayerInstance(context, audioAttributes.exoPlayerAttrs, loadControl)
125139

126140
val mediaSource = mediaSourceRetriever.generateMediaSource(audioPlayable.request, context)
127141
exoPlayer.setMediaSource(mediaSource)
128142
exoPlayer.prepare()
129143

130144
exoPlayer.addListener(playerEventListener)
131145

132-
exoPlayer.playWhenReady = isAutoPlay
146+
exoPlayer.playWhenReady = config.isAutoPlay
133147

134148
stateModifier.dispatch(PlaybackEngineReady(true))
135149
stateModifier.dispatch(PlayerStateAction(PlaybackState.PAUSED))
@@ -243,7 +257,7 @@ internal class ExoplayerPlaybackEngine(private var audioPlayable: AudioPlayable)
243257
}
244258

245259
private fun seekToExo(position: Milliseconds) {
246-
stateModifier.dispatch(PlaybackProgressAction(position, exoPlayer.playerDuration()))
247260
exoPlayer.seekTo(exoPlayer.currentMediaItemIndex, position.longValue)
261+
stateModifier.dispatch(PlaybackProgressAction(position, exoPlayer.playerDuration()))
248262
}
249263
}

Armadillo/src/test/java/com/scribd/armadillo/playback/MediaSessionCallbackTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class MediaSessionCallbackTest {
138138
mediaSessionCallback.onPlayFromUri(URL.toUri(), bundleTwo)
139139
verify(mediaSessionCallback.playbackEngine!!, times(1)).deinit()
140140
verify(mediaSessionCallback.playbackEngine!!, times(1))
141-
.beginPlayback(false, ArmadilloConfiguration.MAX_DISCREPANCY_DEFAULT, 0.milliseconds)
141+
.beginPlayback(ArmadilloConfiguration())
142142
}
143143

144144
@Test
@@ -147,7 +147,7 @@ class MediaSessionCallbackTest {
147147
whenever(playbackInfo.audioPlayable).thenReturn(audiobookTwo)
148148
mediaSessionCallback.onPlayFromUri(URL.toUri(), bundleOne)
149149
verify(mediaSessionCallback.playbackEngine!!, times(1))
150-
.beginPlayback(false, ArmadilloConfiguration.MAX_DISCREPANCY_DEFAULT, 0.milliseconds)
150+
.beginPlayback(ArmadilloConfiguration())
151151
}
152152

153153
@Test
@@ -321,6 +321,6 @@ class MediaSessionCallbackTest {
321321

322322
private fun Bundle.addAudiobook(audiobook: AudioPlayable) {
323323
putSerializable(Constants.Keys.KEY_AUDIO_PLAYABLE, audiobook)
324-
putSerializable(Constants.Keys.KEY_INITIAL_OFFSET, 0.milliseconds)
324+
putSerializable(Constants.Keys.KEY_ARMADILLO_CONFIG, ArmadilloConfiguration())
325325
}
326326
}

RELEASE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Project Armadillo Release Notes
22

3+
## 1.3.3
4+
- Reverted fix in 1.3.2 as it may have affected listening progress not being correctly reported
5+
- Added support for passing in load control parameters via ArmadilloConfiguration to the exo player instance
6+
37
## 1.3.2
48
- Added a fix for order of dispatched actions during seek related events
59

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ org.gradle.jvmargs=-Xmx1536m
1212
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
1313
# org.gradle.parallel=true
1414
PACKAGE_NAME=com.scribd.armadillo
15-
LIBRARY_VERSION=1.3.2
15+
LIBRARY_VERSION=1.3.3
1616
EXOPLAYER_VERSION=2.17.1
1717
RXJAVA_VERSION=2.2.4
1818
RXANDROID_VERSION=2.0.1

0 commit comments

Comments
 (0)