Migrate to androidx media3 (#213)
* Migrate to media3 * Update docs * Move display_extended_title to AppPreferences * Move display_extended_title to AppPreferences p2 * Add MediaSession support to the player * Fix mpv player * Disable animations on tv player controls and rename the tv_control_view file * New media3 decoder ffmpeg
This commit is contained in:
parent
6ed7e12035
commit
65f4c2f639
20 changed files with 156 additions and 134 deletions
|
@ -77,6 +77,9 @@ dependencies {
|
|||
implementation(libs.androidx.leanback)
|
||||
implementation(libs.androidx.lifecycle.runtime)
|
||||
implementation(libs.androidx.lifecycle.viewmodel)
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
implementation(libs.androidx.media3.ui)
|
||||
implementation(libs.androidx.media3.session)
|
||||
implementation(libs.androidx.navigation.fragment)
|
||||
implementation(libs.androidx.navigation.ui)
|
||||
implementation(libs.androidx.paging)
|
||||
|
@ -87,8 +90,6 @@ dependencies {
|
|||
kapt(libs.androidx.room.compiler)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
implementation(libs.androidx.swiperefreshlayout)
|
||||
implementation(libs.exoplayer.core)
|
||||
implementation(libs.exoplayer.ui)
|
||||
implementation(libs.glide)
|
||||
kapt(libs.glide.compiler)
|
||||
implementation(libs.hilt.android)
|
||||
|
@ -97,9 +98,9 @@ dependencies {
|
|||
implementation(libs.material)
|
||||
implementation(libs.timber)
|
||||
|
||||
// ExoPlayer FFmpeg extension
|
||||
implementation(files("libs/extension-ffmpeg-release.aar"))
|
||||
|
||||
// MPV
|
||||
implementation(files("libs/libmpv.aar"))
|
||||
|
||||
// Media3 FFmpeg decoder
|
||||
implementation(files("libs/lib-decoder-ffmpeg-release.aar"))
|
||||
}
|
||||
|
|
Binary file not shown.
BIN
app/libs/lib-decoder-ffmpeg-release.aar
Normal file
BIN
app/libs/lib-decoder-ffmpeg-release.aar
Normal file
Binary file not shown.
7
app/lint.xml
Normal file
7
app/lint.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lint>
|
||||
<issue id="UnsafeOptInUsageError">
|
||||
<ignore
|
||||
regexp='\(markerClass = androidx\.media3\.common\.util\.UnstableApi\.class\)' />
|
||||
</issue>
|
||||
</lint>
|
|
@ -5,23 +5,39 @@ import android.view.View
|
|||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.updatePadding
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector
|
||||
import androidx.media3.exoplayer.trackselection.MappingTrackSelector
|
||||
import androidx.media3.session.MediaSession
|
||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||
|
||||
abstract class BasePlayerActivity : AppCompatActivity() {
|
||||
|
||||
abstract val viewModel: PlayerActivityViewModel
|
||||
|
||||
lateinit var mediaSession: MediaSession
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
mediaSession = MediaSession.Builder(this, viewModel.player).build()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
viewModel.player.playWhenReady = viewModel.playWhenReady
|
||||
hideSystemUI()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
viewModel.playWhenReady = viewModel.player.playWhenReady == true
|
||||
viewModel.player.playWhenReady = false
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.player.playWhenReady = viewModel.playWhenReady
|
||||
hideSystemUI()
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
|
||||
mediaSession.release()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
|
|
@ -8,10 +8,10 @@ import android.view.WindowManager
|
|||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.ui.TrackSelectionDialogBuilder
|
||||
import androidx.navigation.navArgs
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
|
||||
import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment
|
||||
|
|
|
@ -12,32 +12,31 @@ import android.view.SurfaceHolder
|
|||
import android.view.SurfaceView
|
||||
import android.view.TextureView
|
||||
import androidx.core.content.getSystemService
|
||||
import com.google.android.exoplayer2.BasePlayer
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.DeviceInfo
|
||||
import com.google.android.exoplayer2.ExoPlaybackException
|
||||
import com.google.android.exoplayer2.Format
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.MediaMetadata
|
||||
import com.google.android.exoplayer2.PlaybackParameters
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.Player.Commands
|
||||
import com.google.android.exoplayer2.Timeline
|
||||
import com.google.android.exoplayer2.Tracks
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.source.TrackGroup
|
||||
import com.google.android.exoplayer2.text.Cue
|
||||
import com.google.android.exoplayer2.text.CueGroup
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters
|
||||
import com.google.android.exoplayer2.util.Clock
|
||||
import com.google.android.exoplayer2.util.FlagSet
|
||||
import com.google.android.exoplayer2.util.ListenerSet
|
||||
import com.google.android.exoplayer2.util.MimeTypes
|
||||
import com.google.android.exoplayer2.util.Size
|
||||
import com.google.android.exoplayer2.util.Util
|
||||
import com.google.android.exoplayer2.video.VideoSize
|
||||
import dev.jdtech.jellyfin.utils.AppPreferences
|
||||
import `is`.xyz.mpv.MPVLib
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.BasePlayer
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.DeviceInfo
|
||||
import androidx.media3.common.FlagSet
|
||||
import androidx.media3.common.Format
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.MimeTypes
|
||||
import androidx.media3.common.PlaybackParameters
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.Player.Commands
|
||||
import androidx.media3.common.Timeline
|
||||
import androidx.media3.common.TrackGroup
|
||||
import androidx.media3.common.TrackSelectionParameters
|
||||
import androidx.media3.common.Tracks
|
||||
import androidx.media3.common.VideoSize
|
||||
import androidx.media3.common.text.CueGroup
|
||||
import androidx.media3.common.util.Clock
|
||||
import androidx.media3.common.util.ListenerSet
|
||||
import androidx.media3.common.util.Size
|
||||
import androidx.media3.common.util.Util
|
||||
import androidx.media3.exoplayer.ExoPlaybackException
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
|
@ -177,6 +176,7 @@ class MPVPlayer(
|
|||
var currentMpvTracks: List<Track> = emptyList()
|
||||
private var initialCommands = mutableListOf<Array<String>>()
|
||||
private var initialSeekTo: Long = 0L
|
||||
private var trackSelectionParameters: TrackSelectionParameters = TrackSelectionParameters.Builder(context).build()
|
||||
|
||||
// mpv events
|
||||
override fun eventProperty(property: String) {
|
||||
|
@ -386,13 +386,13 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Populates a [com.google.android.exoplayer2.Timeline.Window] with data for the window at the specified index.
|
||||
* Populates a [androidx.media3.common.Timeline.Window] with data for the window at the specified index.
|
||||
*
|
||||
* @param windowIndex The index of the window.
|
||||
* @param window The [com.google.android.exoplayer2.Timeline.Window] to populate. Must not be null.
|
||||
* @param window The [androidx.media3.common.Timeline.Window] to populate. Must not be null.
|
||||
* @param defaultPositionProjectionUs A duration into the future that the populated window's
|
||||
* default start position should be projected.
|
||||
* @return The populated [com.google.android.exoplayer2.Timeline.Window], for convenience.
|
||||
* @return The populated [androidx.media3.common.Timeline.Window], for convenience.
|
||||
*/
|
||||
override fun getWindow(
|
||||
windowIndex: Int,
|
||||
|
@ -427,14 +427,14 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Populates a [com.google.android.exoplayer2.Timeline.Period] with data for the period at the specified index.
|
||||
* Populates a [androidx.media3.common.Timeline.Period] with data for the period at the specified index.
|
||||
*
|
||||
* @param periodIndex The index of the period.
|
||||
* @param period The [com.google.android.exoplayer2.Timeline.Period] to populate. Must not be null.
|
||||
* @param setIds Whether [com.google.android.exoplayer2.Timeline.Period.id] and [com.google.android.exoplayer2.Timeline.Period.uid] should be populated. If false,
|
||||
* @param period The [androidx.media3.common.Timeline.Period] to populate. Must not be null.
|
||||
* @param setIds Whether [androidx.media3.common.Timeline.Period.id] and [androidx.media3.common.Timeline.Period.uid] should be populated. If false,
|
||||
* the fields will be set to null. The caller should pass false for efficiency reasons unless
|
||||
* the fields are required.
|
||||
* @return The populated [com.google.android.exoplayer2.Timeline.Period], for convenience.
|
||||
* @return The populated [androidx.media3.common.Timeline.Period], for convenience.
|
||||
*/
|
||||
override fun getPeriod(periodIndex: Int, period: Period, setIds: Boolean): Period {
|
||||
return period.set(
|
||||
|
@ -447,7 +447,7 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the period identified by its unique [com.google.android.exoplayer2.Timeline.Period.uid], or [ ][C.INDEX_UNSET] if the period is not in the timeline.
|
||||
* Returns the index of the period identified by its unique [Timeline.Period.uid], or [ ][C.INDEX_UNSET] if the period is not in the timeline.
|
||||
*
|
||||
* @param uid A unique identifier for a period.
|
||||
* @return The index of the period, or [C.INDEX_UNSET] if the period was not found.
|
||||
|
@ -557,12 +557,12 @@ class MPVPlayer(
|
|||
* Clears the playlist and adds the specified [MediaItems][MediaItem].
|
||||
*
|
||||
* @param mediaItems The new [MediaItems][MediaItem].
|
||||
* @param startWindowIndex The window index to start playback from. If [com.google.android.exoplayer2.C.INDEX_UNSET] is
|
||||
* @param startWindowIndex The window index to start playback from. If [C.INDEX_UNSET] is
|
||||
* passed, the current position is not reset.
|
||||
* @param startPositionMs The position in milliseconds to start playback from. If [ ][com.google.android.exoplayer2.C.TIME_UNSET] is passed, the default position of the given window is used. In any case, if
|
||||
* `startWindowIndex` is set to [com.google.android.exoplayer2.C.INDEX_UNSET], this parameter is ignored and the
|
||||
* @param startPositionMs The position in milliseconds to start playback from. If [ ][C.TIME_UNSET] is passed, the default position of the given window is used. In any case, if
|
||||
* `startWindowIndex` is set to [C.INDEX_UNSET], this parameter is ignored and the
|
||||
* position is not reset at all.
|
||||
* @throws com.google.android.exoplayer2.IllegalSeekPositionException If the provided `startWindowIndex` is not within the
|
||||
* @throws androidx.media3.common.IllegalSeekPositionException If the provided `startWindowIndex` is not within the
|
||||
* bounds of the list of media items.
|
||||
*/
|
||||
override fun setMediaItems(
|
||||
|
@ -611,10 +611,10 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the player's currently available [com.google.android.exoplayer2.Player.Commands].
|
||||
* Returns the player's currently available [Commands].
|
||||
*
|
||||
*
|
||||
* The returned [com.google.android.exoplayer2.Player.Commands] are not updated when available commands change. Use [ ][com.google.android.exoplayer2.Player.Listener.onAvailableCommandsChanged] to get an update when the available commands
|
||||
* The returned [Commands] are not updated when available commands change. Use [ ][androidx.media3.common.Player.Listener.onAvailableCommandsChanged] to get an update when the available commands
|
||||
* change.
|
||||
*
|
||||
*
|
||||
|
@ -625,8 +625,8 @@ class MPVPlayer(
|
|||
* [.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM] and [.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM]
|
||||
* are unavailable if there is no such [MediaItem].
|
||||
*
|
||||
* @return The currently available [com.google.android.exoplayer2.Player.Commands].
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onAvailableCommandsChanged
|
||||
* @return The currently available [Commands].
|
||||
* @see androidx.media3.common.Player.Listener.onAvailableCommandsChanged
|
||||
*/
|
||||
override fun getAvailableCommands(): Commands {
|
||||
return Commands.Builder()
|
||||
|
@ -682,10 +682,10 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the current [playback state][com.google.android.exoplayer2.Player.State] of the player.
|
||||
* Returns the current [playback state][androidx.media3.common.Player.State] of the player.
|
||||
*
|
||||
* @return The current [playback state][com.google.android.exoplayer2.Player.State].
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onPlaybackStateChanged
|
||||
* @return The current [playback state][androidx.media3.common.Player.State].
|
||||
* @see androidx.media3.common.Player.Listener.onPlaybackStateChanged
|
||||
*/
|
||||
override fun getPlaybackState(): Int {
|
||||
return playbackState
|
||||
|
@ -694,8 +694,8 @@ class MPVPlayer(
|
|||
/**
|
||||
* Returns the reason why playback is suppressed even though [.getPlayWhenReady] is `true`, or [.PLAYBACK_SUPPRESSION_REASON_NONE] if playback is not suppressed.
|
||||
*
|
||||
* @return The current [playback suppression reason][com.google.android.exoplayer2.Player.PlaybackSuppressionReason].
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onPlaybackSuppressionReasonChanged
|
||||
* @return The current [playback suppression reason][androidx.media3.common.Player.PlaybackSuppressionReason].
|
||||
* @see androidx.media3.common.Player.Listener.onPlaybackSuppressionReasonChanged
|
||||
*/
|
||||
override fun getPlaybackSuppressionReason(): Int {
|
||||
return PLAYBACK_SUPPRESSION_REASON_NONE
|
||||
|
@ -703,7 +703,7 @@ class MPVPlayer(
|
|||
|
||||
/**
|
||||
* Returns the error that caused playback to fail. This is the same error that will have been
|
||||
* reported via [com.google.android.exoplayer2.Player.Listener.onPlayerError] at the time of failure. It
|
||||
* reported via [androidx.media3.common.Player.Listener.onPlayerError] at the time of failure. It
|
||||
* can be queried using this method until the player is re-prepared.
|
||||
*
|
||||
*
|
||||
|
@ -711,7 +711,7 @@ class MPVPlayer(
|
|||
* [.STATE_IDLE].
|
||||
*
|
||||
* @return The error, or `null`.
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onPlayerError
|
||||
* @see androidx.media3.common.Player.Listener.onPlayerError
|
||||
*/
|
||||
override fun getPlayerError(): ExoPlaybackException? {
|
||||
return null
|
||||
|
@ -741,14 +741,14 @@ class MPVPlayer(
|
|||
* Whether playback will proceed when [.getPlaybackState] == [.STATE_READY].
|
||||
*
|
||||
* @return Whether playback will proceed when ready.
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onPlayWhenReadyChanged
|
||||
* @see androidx.media3.common.Player.Listener.onPlayWhenReadyChanged
|
||||
*/
|
||||
override fun getPlayWhenReady(): Boolean {
|
||||
return currentPlayWhenReady
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [com.google.android.exoplayer2.Player.RepeatMode] to be used for playback.
|
||||
* Sets the [androidx.media3.common.Player.RepeatMode] to be used for playback.
|
||||
*
|
||||
* @param repeatMode The repeat mode.
|
||||
*/
|
||||
|
@ -757,10 +757,10 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the current [com.google.android.exoplayer2.Player.RepeatMode] used for playback.
|
||||
* Returns the current [androidx.media3.common.Player.RepeatMode] used for playback.
|
||||
*
|
||||
* @return The current repeat mode.
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onRepeatModeChanged
|
||||
* @see androidx.media3.common.Player.Listener.onRepeatModeChanged
|
||||
*/
|
||||
override fun getRepeatMode(): Int {
|
||||
return repeatMode
|
||||
|
@ -778,7 +778,7 @@ class MPVPlayer(
|
|||
/**
|
||||
* Returns whether shuffling of windows is enabled.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onShuffleModeEnabledChanged
|
||||
* @see androidx.media3.common.Player.Listener.onShuffleModeEnabledChanged
|
||||
*/
|
||||
override fun getShuffleModeEnabled(): Boolean {
|
||||
return false
|
||||
|
@ -788,7 +788,7 @@ class MPVPlayer(
|
|||
* Whether the player is currently loading the source.
|
||||
*
|
||||
* @return Whether the player is currently loading the source.
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onIsLoadingChanged
|
||||
* @see androidx.media3.common.Player.Listener.onIsLoadingChanged
|
||||
*/
|
||||
override fun isLoading(): Boolean {
|
||||
return false
|
||||
|
@ -798,9 +798,9 @@ class MPVPlayer(
|
|||
* Seeks to a position specified in milliseconds in the specified window.
|
||||
*
|
||||
* @param windowIndex The index of the window.
|
||||
* @param positionMs The seek position in the specified window, or [com.google.android.exoplayer2.C.TIME_UNSET] to seek to
|
||||
* @param positionMs The seek position in the specified window, or [C.TIME_UNSET] to seek to
|
||||
* the window's default position.
|
||||
* @throws com.google.android.exoplayer2.IllegalSeekPositionException If the player has a non-empty timeline and the provided
|
||||
* @throws androidx.media3.common.IllegalSeekPositionException If the player has a non-empty timeline and the provided
|
||||
* `windowIndex` is not within the bounds of the current timeline.
|
||||
*/
|
||||
override fun seekTo(windowIndex: Int, positionMs: Long) {
|
||||
|
@ -866,7 +866,7 @@ class MPVPlayer(
|
|||
* player to the default, which means there is no speed or pitch adjustment.
|
||||
*
|
||||
*
|
||||
* Playback parameters changes may cause the player to buffer. [ ][com.google.android.exoplayer2.Player.Listener.onPlaybackParametersChanged] will be called whenever the currently
|
||||
* Playback parameters changes may cause the player to buffer. [ ][androidx.media3.common.Player.Listener.onPlaybackParametersChanged] will be called whenever the currently
|
||||
* active playback parameters change.
|
||||
*
|
||||
* @param playbackParameters The playback parameters.
|
||||
|
@ -880,7 +880,7 @@ class MPVPlayer(
|
|||
/**
|
||||
* Returns the currently active playback parameters.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onPlaybackParametersChanged
|
||||
* @see androidx.media3.common.Player.Listener.onPlaybackParametersChanged
|
||||
*/
|
||||
override fun getPlaybackParameters(): PlaybackParameters {
|
||||
return playbackParameters
|
||||
|
@ -914,11 +914,11 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
override fun getTrackSelectionParameters(): TrackSelectionParameters {
|
||||
TODO("Not yet implemented")
|
||||
return trackSelectionParameters
|
||||
}
|
||||
|
||||
override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
|
||||
TODO("Not yet implemented")
|
||||
trackSelectionParameters = parameters
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -927,15 +927,15 @@ class MPVPlayer(
|
|||
*
|
||||
*
|
||||
* This [MediaMetadata] is a combination of the [MediaItem.mediaMetadata] and the
|
||||
* static and dynamic metadata sourced from [com.google.android.exoplayer2.Player.Listener.onMediaMetadataChanged] and
|
||||
* [com.google.android.exoplayer2.metadata.MetadataOutput.onMetadata].
|
||||
* static and dynamic metadata sourced from [androidx.media3.common.Player.Listener.onMediaMetadataChanged] and
|
||||
* [androidx.media3.exoplayer.metadata.MetadataOutput.onMetadata].
|
||||
*/
|
||||
override fun getMediaMetadata(): MediaMetadata {
|
||||
return MediaMetadata.EMPTY
|
||||
}
|
||||
|
||||
override fun getPlaylistMetadata(): MediaMetadata {
|
||||
TODO("Not yet implemented")
|
||||
return MediaMetadata.EMPTY
|
||||
}
|
||||
|
||||
override fun setPlaylistMetadata(mediaMetadata: MediaMetadata) {
|
||||
|
@ -945,7 +945,7 @@ class MPVPlayer(
|
|||
/**
|
||||
* Returns the current [Timeline]. Never null, but may be empty.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onTimelineChanged
|
||||
* @see androidx.media3.common.Player.Listener.onTimelineChanged
|
||||
*/
|
||||
override fun getCurrentTimeline(): Timeline {
|
||||
return timeline
|
||||
|
@ -961,7 +961,7 @@ class MPVPlayer(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the duration of the current content window or ad in milliseconds, or [ ][com.google.android.exoplayer2.C.TIME_UNSET] if the duration is not known.
|
||||
* Returns the duration of the current content window or ad in milliseconds, or [ ][C.TIME_UNSET] if the duration is not known.
|
||||
*/
|
||||
override fun getDuration(): Long {
|
||||
return timeline.getWindow(currentMediaItemIndex, window).durationMs
|
||||
|
@ -999,7 +999,7 @@ class MPVPlayer(
|
|||
|
||||
/**
|
||||
* If [.isPlayingAd] returns true, returns the index of the ad group in the period
|
||||
* currently being played. Returns [com.google.android.exoplayer2.C.INDEX_UNSET] otherwise.
|
||||
* currently being played. Returns [C.INDEX_UNSET] otherwise.
|
||||
*/
|
||||
override fun getCurrentAdGroupIndex(): Int {
|
||||
return C.INDEX_UNSET
|
||||
|
@ -1007,7 +1007,7 @@ class MPVPlayer(
|
|||
|
||||
/**
|
||||
* If [.isPlayingAd] returns true, returns the index of the ad in its ad group. Returns
|
||||
* [com.google.android.exoplayer2.C.INDEX_UNSET] otherwise.
|
||||
* [C.INDEX_UNSET] otherwise.
|
||||
*/
|
||||
override fun getCurrentAdIndexInAdGroup(): Int {
|
||||
return C.INDEX_UNSET
|
||||
|
@ -1051,7 +1051,7 @@ class MPVPlayer(
|
|||
* @return The linear gain applied to all audio channels.
|
||||
*/
|
||||
override fun getVolume(): Float {
|
||||
TODO("Not yet implemented")
|
||||
return MPVLib.getPropertyInt("volume") / 100F
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1154,7 +1154,7 @@ class MPVPlayer(
|
|||
* The video's width and height are `0` if there is no video or its size has not been
|
||||
* determined yet.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Player.Listener.onVideoSizeChanged
|
||||
* @see androidx.media3.common.Player.Listener.onVideoSizeChanged
|
||||
*/
|
||||
override fun getVideoSize(): VideoSize {
|
||||
return VideoSize.UNKNOWN
|
||||
|
@ -1169,14 +1169,14 @@ class MPVPlayer(
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns the current [Cues][Cue]. This list may be empty. */
|
||||
/** Returns the current [CueGroup]. This list may be empty. */
|
||||
override fun getCurrentCues(): CueGroup {
|
||||
TODO("Not yet implemented")
|
||||
return CueGroup(emptyList(), 0)
|
||||
}
|
||||
|
||||
/** Gets the device information. */
|
||||
override fun getDeviceInfo(): DeviceInfo {
|
||||
TODO("Not yet implemented")
|
||||
return DeviceInfo(DeviceInfo.PLAYBACK_TYPE_LOCAL, 0, 100)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1184,7 +1184,7 @@ class MPVPlayer(
|
|||
*
|
||||
*
|
||||
* For devices with [local playback][DeviceInfo.PLAYBACK_TYPE_LOCAL], the volume returned
|
||||
* by this method varies according to the current [stream type][com.google.android.exoplayer2.C.StreamType]. The stream
|
||||
* by this method varies according to the current [stream type][C.StreamType]. The stream
|
||||
* type is determined by [AudioAttributes.usage] which can be converted to stream type with
|
||||
* [Util.getStreamTypeForAudioUsage].
|
||||
*
|
||||
|
@ -1193,12 +1193,12 @@ class MPVPlayer(
|
|||
* remote device is returned.
|
||||
*/
|
||||
override fun getDeviceVolume(): Int {
|
||||
TODO("Not yet implemented")
|
||||
return MPVLib.getPropertyInt("volume")
|
||||
}
|
||||
|
||||
/** Gets whether the device is muted or not. */
|
||||
override fun isDeviceMuted(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
return MPVLib.getPropertyBoolean("mute")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,7 +42,7 @@ internal class TvPlayerActivity : BasePlayerActivity() {
|
|||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
binding.playerView.player = viewModel.player
|
||||
val playerControls = binding.playerView.findViewById<View>(R.id.tv_player_controls)
|
||||
val playerControls = binding.playerView.findViewById<View>(R.id.player_controls)
|
||||
configureInsets(playerControls)
|
||||
|
||||
bind()
|
||||
|
@ -51,7 +51,7 @@ internal class TvPlayerActivity : BasePlayerActivity() {
|
|||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return if (!binding.playerView.isControllerVisible) {
|
||||
return if (!binding.playerView.isControllerFullyVisible) {
|
||||
binding.playerView.showController()
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -3,8 +3,8 @@ package dev.jdtech.jellyfin.utils
|
|||
import android.content.SharedPreferences
|
||||
import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
import androidx.core.content.edit
|
||||
import com.google.android.exoplayer2.C.DEFAULT_SEEK_BACK_INCREMENT_MS
|
||||
import com.google.android.exoplayer2.C.DEFAULT_SEEK_FORWARD_INCREMENT_MS
|
||||
import androidx.media3.common.C.DEFAULT_SEEK_BACK_INCREMENT_MS
|
||||
import androidx.media3.common.C.DEFAULT_SEEK_FORWARD_INCREMENT_MS
|
||||
import javax.inject.Inject
|
||||
|
||||
class AppPreferences
|
||||
|
@ -26,6 +26,8 @@ constructor(
|
|||
val dynamicColors = sharedPreferences.getBoolean(Constants.PREF_DYNAMIC_COLORS, true)
|
||||
|
||||
// Player
|
||||
val displayExtendedTitle = sharedPreferences.getBoolean(Constants.PREF_DISPLAY_EXTENDED_TITLE, false)
|
||||
|
||||
val playerGestures = sharedPreferences.getBoolean(Constants.PREF_PLAYER_GESTURES, true)
|
||||
val playerGesturesVB = sharedPreferences.getBoolean(Constants.PREF_PLAYER_GESTURES_VB, true)
|
||||
val playerGesturesZoom = sharedPreferences.getBoolean(Constants.PREF_PLAYER_GESTURES_ZOOM, true)
|
||||
|
|
|
@ -9,6 +9,7 @@ object Constants {
|
|||
|
||||
// pref
|
||||
const val PREF_CURRENT_SERVER = "pref_current_server"
|
||||
const val PREF_DISPLAY_EXTENDED_TITLE = "pref_player_display_extended_title"
|
||||
const val PREF_PLAYER_GESTURES = "pref_player_gestures"
|
||||
const val PREF_PLAYER_GESTURES_VB = "pref_player_gestures_vb"
|
||||
const val PREF_PLAYER_GESTURES_ZOOM = "pref_player_gestures_zoom"
|
||||
|
|
|
@ -10,8 +10,8 @@ import android.view.ScaleGestureDetector
|
|||
import android.view.View
|
||||
import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
|
||||
import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.PlayerView
|
||||
import dev.jdtech.jellyfin.PlayerActivity
|
||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||
import kotlin.math.abs
|
||||
|
@ -20,7 +20,7 @@ import timber.log.Timber
|
|||
class PlayerGestureHelper(
|
||||
private val appPreferences: AppPreferences,
|
||||
private val activity: PlayerActivity,
|
||||
private val playerView: StyledPlayerView,
|
||||
private val playerView: PlayerView,
|
||||
private val audioManager: AudioManager
|
||||
) {
|
||||
/**
|
||||
|
|
|
@ -7,14 +7,14 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||
import dev.jdtech.jellyfin.models.PlayerItem
|
||||
|
@ -36,7 +36,7 @@ constructor(
|
|||
application: Application,
|
||||
private val jellyfinRepository: JellyfinRepository,
|
||||
private val downloadDatabase: DownloadDatabaseDao,
|
||||
appPreferences: AppPreferences,
|
||||
private val appPreferences: AppPreferences,
|
||||
) : ViewModel(), Player.Listener {
|
||||
val player: Player
|
||||
|
||||
|
@ -63,8 +63,6 @@ constructor(
|
|||
var playbackSpeed: Float = 1f
|
||||
var disableSubtitle: Boolean = false
|
||||
|
||||
private val sp = PreferenceManager.getDefaultSharedPreferences(application)
|
||||
|
||||
init {
|
||||
if (appPreferences.playerMpv) {
|
||||
player = MPVPlayer(
|
||||
|
@ -126,6 +124,11 @@ constructor(
|
|||
MediaItem.Builder()
|
||||
.setMediaId(item.itemId.toString())
|
||||
.setUri(streamUrl)
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(item.name)
|
||||
.build()
|
||||
)
|
||||
.setSubtitleConfigurations(mediaSubtitles)
|
||||
.build()
|
||||
mediaItems.add(mediaItem)
|
||||
|
@ -135,8 +138,7 @@ constructor(
|
|||
}
|
||||
|
||||
player.setMediaItems(mediaItems, currentMediaItemIndex, items.getOrNull(currentMediaItemIndex)?.playbackPosition ?: C.TIME_UNSET)
|
||||
val useMpv = sp.getBoolean("mpv_player", false)
|
||||
if (!useMpv || !playFromDownloads)
|
||||
if (!appPreferences.playerMpv || !playFromDownloads)
|
||||
player.prepare() // TODO: This line causes a crash when playing from downloads with MPV
|
||||
player.play()
|
||||
pollPosition(player)
|
||||
|
@ -196,10 +198,7 @@ constructor(
|
|||
try {
|
||||
for (item in items) {
|
||||
if (item.itemId.toString() == (player.currentMediaItem?.mediaId ?: "")) {
|
||||
if (sp.getBoolean(
|
||||
"display_extended_title",
|
||||
false
|
||||
) && item.parentIndexNumber != null && item.indexNumber != null && item.name != null
|
||||
if (appPreferences.displayExtendedTitle && item.parentIndexNumber != null && item.indexNumber != null && item.name != null
|
||||
)
|
||||
_currentItemTitle.value =
|
||||
"S${item.parentIndexNumber}:E${item.indexNumber} - ${item.name}"
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.net.Uri
|
|||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.android.exoplayer2.util.MimeTypes
|
||||
import androidx.media3.common.MimeTypes
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tv_player_controls"
|
||||
android:id="@+id/player_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/player_background"
|
||||
|
@ -66,7 +66,7 @@
|
|||
app:layout_constraintStart_toEndOf="@id/btn_audio_track"
|
||||
/>
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
<androidx.media3.ui.DefaultTimeBar
|
||||
android:id="@+id/exo_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
|
@ -6,15 +6,13 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".PlayerActivity">
|
||||
|
||||
<com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
app:animation_enabled="false"
|
||||
app:show_buffering="always">
|
||||
|
||||
</com.google.android.exoplayer2.ui.StyledPlayerView>
|
||||
app:show_buffering="always" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/progress_scrubber_layout"
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PlayerActivity"
|
||||
>
|
||||
tools:context=".PlayerActivity">
|
||||
|
||||
<com.google.android.exoplayer2.ui.PlayerView
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
app:controller_layout_id="@layout/tv_player_controls"
|
||||
app:show_buffering="always"
|
||||
/>
|
||||
app:animation_enabled="false"
|
||||
app:show_buffering="always" />
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
<androidx.media3.ui.DefaultTimeBar
|
||||
android:id="@+id/exo_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
|
@ -2,7 +2,7 @@
|
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||
<androidx.media3.ui.AspectRatioFrameLayout
|
||||
android:id="@id/exo_content_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -46,9 +46,9 @@
|
|||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
|
||||
</androidx.media3.ui.AspectRatioFrameLayout>
|
||||
|
||||
<com.google.android.exoplayer2.ui.SubtitleView
|
||||
<androidx.media3.ui.SubtitleView
|
||||
android:id="@id/exo_subtitles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<SwitchPreference
|
||||
app:key="display_extended_title"
|
||||
app:key="pref_player_display_extended_title"
|
||||
app:summary="@string/display_extended_title_summary"
|
||||
app:title="@string/display_extended_title" />
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ androidx-constraintlayout = "2.1.4"
|
|||
androidx-core = "1.9.0"
|
||||
androidx-leanback = "1.2.0-alpha02"
|
||||
androidx-lifecycle = "2.5.1"
|
||||
androidx-media3 = "1.0.0-beta03"
|
||||
androidx-navigation = "2.5.3"
|
||||
androidx-paging = "3.1.1"
|
||||
androidx-preference = "1.2.0"
|
||||
|
@ -14,7 +15,6 @@ androidx-recyclerview = "1.2.1"
|
|||
androidx-recyclerview-selection = "1.1.0"
|
||||
androidx-room = "2.4.3"
|
||||
androidx-swiperefreshlayout = "1.1.0"
|
||||
exoplayer = "2.18.2"
|
||||
glide = "4.14.2"
|
||||
hilt = "2.44.2"
|
||||
jellyfin = "1.3.7"
|
||||
|
@ -33,6 +33,9 @@ androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-cor
|
|||
androidx-leanback = { module = "androidx.leanback:leanback", version.ref = "androidx-leanback" }
|
||||
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
|
||||
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
|
||||
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "androidx-media3" }
|
||||
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "androidx-media3" }
|
||||
androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "androidx-media3" }
|
||||
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" }
|
||||
androidx-navigation-ui = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" }
|
||||
androidx-paging = { module = "androidx.paging:paging-runtime-ktx", version.ref = "androidx-paging" }
|
||||
|
@ -43,8 +46,6 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
|
|||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidx-room" }
|
||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" }
|
||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" }
|
||||
exoplayer-core = { module = "com.google.android.exoplayer:exoplayer-core", version.ref = "exoplayer" }
|
||||
exoplayer-ui = { module = "com.google.android.exoplayer:exoplayer-ui", version.ref = "exoplayer" }
|
||||
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" }
|
||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
||||
|
|
Loading…
Reference in a new issue