Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preload manager to exoplayer to improve short form playback experience #91

Merged
merged 9 commits into from
Sep 6, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ package com.google.android.samples.socialite.ui.home.timeline

import android.content.Context
import android.net.Uri
import android.os.HandlerThread
import android.os.Process
import android.util.Log
import androidx.annotation.OptIn
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
Expand Down Expand Up @@ -59,7 +62,11 @@ class TimelineViewModel @Inject constructor(
private val enablePreloadManager: Boolean = true
private lateinit var preloadManager: PreloadManagerWrapper

var timeToFirstFrame = 0L
// Playback thread; Internal playback / preload operations are running on the playback thread.
private val playerThread: HandlerThread =
HandlerThread("playback-thread", Process.THREAD_PRIORITY_AUDIO)

var playbackStartTimeMs = C.TIME_UNSET

private val videoSizeListener = object : Player.Listener {
override fun onVideoSizeChanged(videoSize: VideoSize) {
Expand All @@ -74,8 +81,8 @@ class TimelineViewModel @Inject constructor(

private val firstFrameListener = object : Player.Listener {
override fun onRenderedFirstFrame() {
timeToFirstFrame = System.currentTimeMillis() - timeToFirstFrame
Log.d("PreloadManager", "\t\tTime to first Frame = $timeToFirstFrame ")
val timeToFirstFrameMs = System.currentTimeMillis() - playbackStartTimeMs
Log.d("PreloadManager", "\t\tTime to first Frame = $timeToFirstFrameMs ")
super.onRenderedFirstFrame()
}
}
Expand Down Expand Up @@ -115,9 +122,13 @@ class TimelineViewModel @Inject constructor(
val loadControl =
DefaultLoadControl.Builder().setBufferDurationsMs(5_000, 20_000, 5_00, DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
.setPrioritizeTimeOverSizeThresholds(true).build()

playerThread.start()

val newPlayer = ExoPlayer
.Builder(application.applicationContext)
.setLoadControl(loadControl)
.setPlaybackLooper(playerThread.looper)
.build()
.also {
it.repeatMode = ExoPlayer.REPEAT_MODE_ONE
Expand All @@ -130,14 +141,17 @@ class TimelineViewModel @Inject constructor(
player = newPlayer

if (enablePreloadManager) {
initPreloadManager(loadControl)
initPreloadManager(loadControl, playerThread)
}
}

private fun initPreloadManager(loadControl: DefaultLoadControl) {
private fun initPreloadManager(
loadControl: DefaultLoadControl,
preloadAndPlaybackThread: HandlerThread,
) {
preloadManager =
PreloadManagerWrapper.build(
(player as ExoPlayer).applicationLooper,
preloadAndPlaybackThread.looper,
loadControl,
application.applicationContext,
)
Expand Down Expand Up @@ -186,7 +200,7 @@ class TimelineViewModel @Inject constructor(
setMediaItem(mediaItem)
}

timeToFirstFrame = System.currentTimeMillis()
playbackStartTimeMs = System.currentTimeMillis()
Log.d("PreloadManager", "Video Playing $uri ")
prepare()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private constructor(
// Queue of media items to be preloaded. Can be ranked based on ranking data
private val preloadWindow: ArrayDeque<Pair<MediaItem, Int>> = ArrayDeque()

// Default window size for preload manager. This defines how many maximum items will be preloaded at a time. If more than maximum items are added to the preload window
// Default window size for preload manager. This defines how many maximum items will be preloaded at a time.
private var preloadWindowMaxSize = 5

private var currentPlayingIndex = C.INDEX_UNSET
Expand All @@ -57,6 +57,10 @@ private constructor(

/** Builds a preload manager instance with default parameters. Preload manager should use the same looper and load control as the player */
companion object {

// Thread on which the preload manager is running
private lateinit var playbackAndPreloadThread: Looper
MayuriKhinvasara marked this conversation as resolved.
Show resolved Hide resolved

fun build(
playbackLooper: Looper,
loadControl: DefaultLoadControl,
Expand All @@ -65,6 +69,7 @@ private constructor(
val trackSelector = DefaultTrackSelector(context)
trackSelector.init({}, DefaultBandwidthMeter.getSingletonInstance(context))
val renderersFactory = DefaultRenderersFactory(context)
playbackAndPreloadThread = playbackLooper
val preloadManager = DefaultPreloadManager(
PreloadStatusControl(),
DefaultMediaSourceFactory(context),
Expand Down Expand Up @@ -100,7 +105,7 @@ private constructor(
mediaItemsList = mediaList
}

/** Ensure that current playing item is in the middle of the preload Window . */
/** Add the next set of items to preload, w.r.t to the current playing index. */
private fun preloadNextItems() {
var lastPreloadedIndex = 0
if (!preloadWindow.isEmpty()) {
Expand Down Expand Up @@ -148,6 +153,7 @@ private constructor(
defaultPreloadManager.release()
preloadWindow.clear()
mediaItemsList.toMutableList().clear()
playbackAndPreloadThread.quit()
MayuriKhinvasara marked this conversation as resolved.
Show resolved Hide resolved
}

/** Retrieve the preloaded media source */
Expand All @@ -159,7 +165,7 @@ private constructor(
@androidx.media3.common.util.UnstableApi
class PreloadStatusControl : TargetPreloadStatusControl<Int> {
override fun getTargetPreloadStatus(rankingData: Int): DefaultPreloadManager.Status {
// By default preload first 5 seconds of the video
// By default preload first 3 seconds of the video
return DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 3000L)
}
}
Expand Down
Loading