Skip to content

Commit

Permalink
Merge pull request #5934 from Bnyro/master
Browse files Browse the repository at this point in the history
feat: preference to force maximum audio quality
  • Loading branch information
Bnyro authored Apr 23, 2024
2 parents 885a19c + 4649b08 commit faecb1d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 44 deletions.
99 changes: 64 additions & 35 deletions app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.github.libretube.helpers

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.net.Uri
import android.util.Base64
import android.util.Log
import android.view.accessibility.CaptioningManager
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.annotation.StringRes
import androidx.core.app.PendingIntentCompat
import androidx.core.app.RemoteActionCompat
Expand All @@ -19,7 +20,9 @@ import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.PlaybackParameters
import androidx.media3.common.Player
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.Tracks
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.cronet.CronetDataSource
import androidx.media3.exoplayer.DefaultLoadControl
Expand All @@ -45,14 +48,14 @@ import com.github.libretube.extensions.updateParameters
import com.github.libretube.obj.VideoStats
import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.TextUtils
import java.util.Locale
import java.util.concurrent.Executors
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.Locale
import java.util.concurrent.Executors
import kotlin.math.absoluteValue
import kotlin.math.roundToInt

object PlayerHelper {
private const val ACTION_MEDIA_CONTROL = "media_control"
Expand Down Expand Up @@ -99,7 +102,7 @@ object PlayerHelper {
/**
* Get the system's default captions style
*/
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@OptIn(androidx.media3.common.util.UnstableApi::class)
fun getCaptionStyle(context: Context): CaptionStyleCompat {
val captioningManager = context.getSystemService<CaptioningManager>()!!
return if (!captioningManager.isEnabled) {
Expand Down Expand Up @@ -339,15 +342,15 @@ object PlayerHelper {

val playAutomatically: Boolean
get() = PreferenceHelper.getBoolean(
PreferenceKeys.PLAY_AUTOMATICALLY,
true
)
PreferenceKeys.PLAY_AUTOMATICALLY,
true
)

val disablePipedProxy: Boolean
get() = PreferenceHelper.getBoolean(
PreferenceKeys.DISABLE_VIDEO_IMAGE_PROXY,
false
)
PreferenceKeys.DISABLE_VIDEO_IMAGE_PROXY,
false
)

fun shouldPlayNextVideo(isPlaylist: Boolean = false): Boolean {
// if there is no next video, it obviously should not be played
Expand All @@ -356,11 +359,11 @@ object PlayerHelper {
}

return autoPlayEnabled || (
isPlaylist && PreferenceHelper.getBoolean(
PreferenceKeys.AUTOPLAY_PLAYLISTS,
false
)
)
isPlaylist && PreferenceHelper.getBoolean(
PreferenceKeys.AUTOPLAY_PLAYLISTS,
false
)
)
}

private val handleAudioFocus
Expand All @@ -382,19 +385,44 @@ object PlayerHelper {
.toIntOrNull()
}

/**
* Apply the preferred audio quality: auto or worst
*/
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
fun applyPreferredAudioQuality(context: Context, trackSelector: DefaultTrackSelector) {
@OptIn(UnstableApi::class)
fun setPreferredAudioQuality(
context: Context,
player: Player,
trackSelector: DefaultTrackSelector
) {
val prefKey = if (NetworkHelper.isNetworkMetered(context)) {
PreferenceKeys.PLAYER_AUDIO_QUALITY_MOBILE
} else {
PreferenceKeys.PLAYER_AUDIO_QUALITY
}
when (PreferenceHelper.getString(prefKey, "auto")) {
"worst" -> trackSelector.updateParameters {
setMaxAudioBitrate(1)

val qualityPref = PreferenceHelper.getString(prefKey, "auto")
if (qualityPref == "auto") return

// multiple groups due to different possible audio languages
val audioTrackGroups = player.currentTracks.groups
.filter { it.type == C.TRACK_TYPE_AUDIO }

for (audioTrackGroup in audioTrackGroups) {
// find the best audio bitrate
val streams = (0 until audioTrackGroup.length).map { index ->
index to audioTrackGroup.getTrackFormat(index).bitrate
}

// if no bitrate info is available, fallback to the
// - first stream for lowest quality
// - last stream for highest quality
val streamIndex = if (qualityPref == "best") {
streams.maxByOrNull { it.second }?.takeIf { it.second != -1 }?.first
?: (streams.size - 1)
} else {
streams.minByOrNull { it.second }?.takeIf { it.second != -1 }?.first ?: 0
}

trackSelector.updateParameters {
val override = TrackSelectionOverride(audioTrackGroup.mediaTrackGroup, streamIndex)
setOverrideForType(override)
}
}
}
Expand All @@ -412,7 +440,8 @@ object PlayerHelper {
val intent = Intent(getIntentActionName(activity))
.setPackage(activity.packageName)
.putExtra(CONTROL_TYPE, event)
val pendingIntent = PendingIntentCompat.getBroadcast(activity, event.ordinal, intent, 0, false)!!
val pendingIntent =
PendingIntentCompat.getBroadcast(activity, event.ordinal, intent, 0, false)!!

val text = activity.getString(title)
val icon = IconCompat.createWithResource(activity, id)
Expand Down Expand Up @@ -468,7 +497,7 @@ object PlayerHelper {
/**
* Create a basic player, that is used for all types of playback situations inside the app
*/
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@OptIn(androidx.media3.common.util.UnstableApi::class)
fun createPlayer(
context: Context,
trackSelector: DefaultTrackSelector,
Expand Down Expand Up @@ -501,7 +530,7 @@ object PlayerHelper {
/**
* Get the load controls for the player (buffering, etc)
*/
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@OptIn(androidx.media3.common.util.UnstableApi::class)
fun getLoadControl(): LoadControl {
return DefaultLoadControl.Builder()
// cache the last three minutes
Expand All @@ -518,7 +547,7 @@ object PlayerHelper {
/**
* Load playback parameters such as speed and skip silence
*/
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@OptIn(androidx.media3.common.util.UnstableApi::class)
fun ExoPlayer.loadPlaybackParams(isBackgroundMode: Boolean = false): ExoPlayer {
skipSilenceEnabled = skipSilence
val speed = if (isBackgroundMode) backgroundSpeed else playbackSpeed
Expand Down Expand Up @@ -767,12 +796,12 @@ object PlayerHelper {
*/
fun haveAudioTrackRoleFlagSet(@C.RoleFlags roleFlags: Int): Boolean {
return isFlagSet(roleFlags, C.ROLE_FLAG_DESCRIBES_VIDEO) ||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
}

@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@OptIn(androidx.media3.common.util.UnstableApi::class)
fun getVideoStats(player: ExoPlayer, videoId: String): VideoStats {
val videoInfo = "${player.videoFormat?.codecs.orEmpty()} ${
TextUtils.formatBitrate(
Expand Down Expand Up @@ -812,12 +841,12 @@ object PlayerHelper {
}

PlayerEvent.Forward -> {
player.seekBy(PlayerHelper.seekIncrement)
player.seekBy(seekIncrement)
true
}

PlayerEvent.Rewind -> {
player.seekBy(-PlayerHelper.seekIncrement)
player.seekBy(-seekIncrement)
true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class OnlinePlayerService : LifecycleService() {
* The [ExoPlayer] player. Followed tutorial [here](https://developer.android.com/codelabs/exoplayer-intro)
*/
var player: ExoPlayer? = null
private var trackSelector: DefaultTrackSelector? = null
private var isTransitioning = true

/**
Expand Down Expand Up @@ -163,6 +164,14 @@ class OnlinePlayerService : LifecycleService() {
).show()
}
}

override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events)

if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
PlayerHelper.setPreferredAudioQuality(this@OnlinePlayerService, player, trackSelector ?: return)
}
}
}

private val playerActionReceiver = object : BroadcastReceiver() {
Expand Down Expand Up @@ -321,13 +330,12 @@ class OnlinePlayerService : LifecycleService() {
private fun initializePlayer() {
if (player != null) return

val trackSelector = DefaultTrackSelector(this)
PlayerHelper.applyPreferredAudioQuality(this, trackSelector)
trackSelector.updateParameters {
trackSelector = DefaultTrackSelector(this)
trackSelector!!.updateParameters {
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, true)
}

player = PlayerHelper.createPlayer(this, trackSelector, true)
player = PlayerHelper.createPlayer(this, trackSelector!!, true)
// prevent android from putting LibreTube to sleep when locked
player!!.setWakeMode(WAKE_MODE_NETWORK)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onEvents(player: Player, events: Player.Events) {
updateDisplayedDuration()
super.onEvents(player, events)

if (events.containsAny(
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED,
Expand All @@ -281,6 +282,10 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
) {
updatePlayPauseButton()
}

if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
PlayerHelper.setPreferredAudioQuality(requireContext(), exoPlayer, trackSelector)
}
}

override fun onPlaybackStateChanged(playbackState: Int) {
Expand Down Expand Up @@ -586,7 +591,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
playOnBackground()
}

binding.relPlayerPip.isVisible = PictureInPictureCompat.isPictureInPictureAvailable(requireContext())
binding.relPlayerPip.isVisible =
PictureInPictureCompat.isPictureInPictureAvailable(requireContext())

binding.relPlayerPip.setOnClickListener {
PictureInPictureCompat.enterPictureInPictureMode(requireActivity(), pipParams)
Expand Down Expand Up @@ -916,7 +922,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {

val videoStream = streams.videoStreams.firstOrNull()
isShort = PlayingQueue.getCurrent()?.isShort == true ||
(videoStream?.height ?: 0) > (videoStream?.width ?: 0)
(videoStream?.height ?: 0) > (videoStream?.width ?: 0)

PlayingQueue.setOnQueueTapListener { streamItem ->
streamItem.url?.toID()?.let { playNextVideo(it) }
Expand Down Expand Up @@ -952,7 +958,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (binding.playerMotionLayout.progress != 1.0f) {
// show controllers when not in picture in picture mode
val inPipMode = PlayerHelper.pipEnabled &&
PictureInPictureCompat.isInPictureInPictureMode(requireActivity())
PictureInPictureCompat.isInPictureInPictureMode(requireActivity())
if (!inPipMode) {
binding.player.useController = true
}
Expand Down Expand Up @@ -1349,7 +1355,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
this.setPreferredVideoMimeType(mimeType)
}
}
PlayerHelper.applyPreferredAudioQuality(requireContext(), trackSelector)
}

/**
Expand Down Expand Up @@ -1570,7 +1575,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {

private fun shouldStartPiP(): Boolean {
return shouldUsePip() && exoPlayer.isPlaying &&
!BackgroundHelper.isBackgroundServiceRunning(requireContext())
!BackgroundHelper.isBackgroundServiceRunning(requireContext())
}

private fun killPlayerFragment() {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/array.xml
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,13 @@
<string-array name="audioQuality">
<item>@string/auto</item>
<item>@string/worst_quality</item>
<item>@string/best_quality</item>
</string-array>

<string-array name="audioQualityValues">
<item>auto</item>
<item>worst</item>
<item>best</item>
</string-array>

<string-array name="audioQualityBitrates">
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
<string name="playerAudioFormat">Audio format for player</string>
<string name="playerAudioQuality">Audio quality</string>
<string name="worst_quality">Worst</string>
<string name="best_quality">Best</string>
<string name="default_subtitle_language">Subtitle language</string>
<string name="notifications">Notifications</string>
<string name="notify_new_streams">Show notifications for new streams</string>
Expand Down

0 comments on commit faecb1d

Please sign in to comment.