diff --git a/app/build.gradle b/app/build.gradle index b5c68c4f2e02de9773e305115e7d0074b83249b7..8214f0b6c5b0f52a38a4b5fbbdf38bc53a4f1bc6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -172,13 +172,14 @@ dependencies { implementation("com.caoccao.javet:javet-android:2.2.1") //Exoplayer - implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-rtsp:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-transformer:2.19.1' + implementation 'androidx.media3:media3-exoplayer:1.2.0' + implementation 'androidx.media3:media3-exoplayer-dash:1.2.0' + implementation 'androidx.media3:media3-ui:1.2.0' + implementation 'androidx.media3:media3-session:1.2.0' + implementation 'androidx.media3:media3-exoplayer-hls:1.2.0' + implementation 'androidx.media3:media3-exoplayer-rtsp:1.2.0' + implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.2.0' + implementation 'androidx.media3:media3-transformer:1.2.0' implementation 'androidx.navigation:navigation-fragment-ktx:2.7.5' implementation 'androidx.navigation:navigation-ui-ktx:2.7.5' diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt index fb7f05dd46124ac60a1b1b6595903051bba07efc..9bf35ad204819d5056caa4a33860de75b51f8f55 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt @@ -1,7 +1,7 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.api.media.platforms.js.models.sources +import androidx.media3.datasource.DefaultHttpDataSource +import androidx.media3.datasource.HttpDataSource import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource @@ -11,8 +11,6 @@ import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.orNull import com.futo.platformplayer.views.video.datasources.JSHttpDataSource -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource -import com.google.android.exoplayer2.upstream.HttpDataSource abstract class JSSource { protected val _config: IV8PluginConfig; diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index e7bc360fbd46a479358a5fa8916cfd38e1a2c5ce..4be113edd3c5fde9b4074354265ba2b09c0d3591 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.fragment.mainactivity.main import android.app.PictureInPictureParams @@ -14,7 +12,6 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.net.Uri -import android.support.v4.media.session.PlaybackStateCompat import android.text.Spanned import android.util.AttributeSet import android.util.Log @@ -32,6 +29,12 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.lifecycleScope +import androidx.media3.common.C +import androidx.media3.common.Format +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.HttpDataSource +import androidx.media3.ui.PlayerControlView +import androidx.media3.ui.TimeBar import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition @@ -138,11 +141,6 @@ import com.futo.polycentric.core.ContentType import com.futo.polycentric.core.Models import com.futo.polycentric.core.Opinion import com.futo.polycentric.core.toURLInfoSystemLinkUrl -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.Format -import com.google.android.exoplayer2.ui.PlayerControlView -import com.google.android.exoplayer2.ui.TimeBar -import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException import com.google.protobuf.ByteString import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -155,7 +153,6 @@ import java.time.OffsetDateTime import kotlin.math.abs import kotlin.math.roundToLong - class VideoDetailView : ConstraintLayout { private val TAG = "VideoDetailView" @@ -303,7 +300,7 @@ class VideoDetailView : ConstraintLayout { Pair(0, 10) //around live, try every 10 seconds ); - + @androidx.annotation.OptIn(UnstableApi::class) constructor(context: Context, attrs : AttributeSet? = null) : super(context, attrs) { inflate(context, R.layout.fragview_video_detail, this); @@ -312,7 +309,7 @@ class VideoDetailView : ConstraintLayout { _cast = findViewById(R.id.videodetail_cast); _player = findViewById(R.id.videodetail_player); _playerProgress = findViewById(R.id.videodetail_progress); - _timeBar = _playerProgress.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress); + _timeBar = _playerProgress.findViewById(androidx.media3.ui.R.id.exo_progress); _title = findViewById(R.id.videodetail_title); _subTitle = findViewById(R.id.videodetail_meta); _platform = findViewById(R.id.videodetail_platform); @@ -574,8 +571,8 @@ class VideoDetailView : ConstraintLayout { } _playerProgress.player = _player.exoPlayer?.player; - _playerProgress.setProgressUpdateListener { position, _ -> - StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, position); + _playerProgress.setProgressUpdateListener { _, _ -> + StatePlayer.instance.updateMediaSessionPlaybackState(); } StatePlayer.instance.onQueueChanged.subscribe(this) { @@ -1497,6 +1494,7 @@ class VideoDetailView : ConstraintLayout { } //Events + @androidx.annotation.OptIn(UnstableApi::class) private fun onSourceChanged(videoSource: IVideoSource?, audioSource: IAudioSource?, resume: Boolean){ Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)") @@ -1532,7 +1530,7 @@ class VideoDetailView : ConstraintLayout { private var _didTriggerDatasourceError = false; private fun onDataSourceError(exception: Throwable) { Logger.e(TAG, "onDataSourceError", exception); - if(exception.cause != null && exception.cause is InvalidResponseCodeException && (exception.cause!! as InvalidResponseCodeException).responseCode == 403) { + if(exception.cause != null && exception.cause is HttpDataSource.InvalidResponseCodeException && (exception.cause!! as HttpDataSource.InvalidResponseCodeException).responseCode == 403) { val currentVideo = video if(currentVideo == null || currentVideo !is IPluginSourced) return; @@ -1611,6 +1609,7 @@ class VideoDetailView : ConstraintLayout { val v = video ?: return; updateQualitySourcesOverlay(v, videoLocal, liveStreamVideoFormats, liveStreamAudioFormats); } + @androidx.annotation.OptIn(UnstableApi::class) private fun updateQualitySourcesOverlay(videoDetails: IPlatformVideoDetails?, videoLocal: VideoLocal? = null, liveStreamVideoFormats: List<Format>? = null, liveStreamAudioFormats: List<Format>? = null) { Logger.i(TAG, "updateQualitySourcesOverlay"); @@ -1730,7 +1729,7 @@ class VideoDetailView : ConstraintLayout { { handleSelectAudioTrack(it) }); }.toList().toTypedArray()) else null, - if(video?.subtitles?.isNotEmpty() ?: false && video != null) + if(video?.subtitles?.isNotEmpty() == true) SlideUpMenuGroup(this.context, context.getString(R.string.subtitles), "subtitles", *video.subtitles .map { @@ -1792,7 +1791,7 @@ class VideoDetailView : ConstraintLayout { _cast.setIsPlaying(playing); } else { StatePlayer.instance.updateMediaSession( null); - StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0); + StatePlayer.instance.updateMediaSessionPlaybackState(); } if(playing) { diff --git a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt index 3c03472cf9bf930306b7e485a715fcef889f7f1e..7cf603ec2b044a5d97c85d93d6c1a560c791d246 100644 --- a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt +++ b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt @@ -1,8 +1,7 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.helpers import android.net.Uri +import androidx.annotation.OptIn import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource @@ -15,11 +14,12 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource import com.futo.platformplayer.logging.Logger -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.source.MediaSource -import com.google.android.exoplayer2.source.dash.DashMediaSource -import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser -import com.google.android.exoplayer2.upstream.ResolvingDataSource +import androidx.media3.common.MediaItem +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.ResolvingDataSource +import androidx.media3.exoplayer.dash.DashMediaSource +import androidx.media3.exoplayer.dash.manifest.DashManifestParser +import androidx.media3.exoplayer.source.MediaSource import kotlin.math.abs class VideoHelper { @@ -121,7 +121,7 @@ class VideoHelper { return bestSource; } - @Suppress("DEPRECATION") + @OptIn(UnstableApi::class) fun convertItagSourceToChunkedDashSource(videoSource: JSVideoUrlRangeSource) : MediaSource { val urlToUse = videoSource.getVideoUrl(); val manifestConfig = ProgressiveDashManifestCreator.fromVideoProgressiveStreamingUrl(urlToUse, @@ -140,14 +140,13 @@ class VideoHelper { ); val manifest = DashManifestParser().parse(Uri.parse(""), manifestConfig.byteInputStream()); - return DashMediaSource.Factory(ResolvingDataSource.Factory(videoSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec -> Logger.v("PLAYBACK", "Video REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null); return@Resolver dataSpec; })).createMediaSource(manifest, MediaItem.Builder().setUri(Uri.parse(videoSource.getVideoUrl())).build()) } - @Suppress("DEPRECATION") + @OptIn(UnstableApi::class) fun convertItagSourceToChunkedDashSource(audioSource: JSAudioUrlRangeSource) : MediaSource { val manifestConfig = ProgressiveDashManifestCreator.fromAudioProgressiveStreamingUrl(audioSource.getAudioUrl(), audioSource.duration?.times(1000) ?: 0, diff --git a/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt b/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt index 09e4115012c30a6489b7962a489f037381ca6390..9112b6d5bd2281c50996580b88407a6afb6c2e65 100644 --- a/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt +++ b/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt @@ -17,7 +17,6 @@ class InstallReceiver : BroadcastReceiver() { val onReceiveResult = Event1<String?>(); } - @Suppress("DEPRECATION") override fun onReceive(context: Context, intent: Intent) { val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1); Logger.i(TAG, "Received status $status."); diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt index ba73594e98fdf1272e9669b352960fbb76549af4..437fb960e582c054e6b60c56e12e91290e263c24 100644 --- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt +++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt @@ -13,15 +13,14 @@ import android.graphics.drawable.Drawable import android.media.AudioFocusRequest import android.media.AudioManager import android.media.AudioManager.OnAudioFocusChangeListener -import android.media.MediaMetadata import android.os.Build import android.os.IBinder -import android.os.SystemClock -import android.support.v4.media.MediaMetadataCompat -import android.support.v4.media.session.MediaSessionCompat -import android.support.v4.media.session.PlaybackStateCompat import android.util.Log +import androidx.annotation.OptIn import androidx.core.app.NotificationCompat +import androidx.media3.common.util.UnstableApi +import androidx.media3.session.MediaSession +import androidx.media3.session.MediaStyleNotificationHelper import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition @@ -51,7 +50,7 @@ class MediaPlaybackService : Service() { private var _audioManager: AudioManager? = null; private var _notificationManager: NotificationManager? = null; private var _notificationChannel: NotificationChannel? = null; - private var _mediaSession: MediaSessionCompat? = null; + private var _mediaSession: MediaSession? = null; private var _hasFocus: Boolean = false; private var _focusRequest: AudioFocusRequest? = null; private var _audioFocusLossTime_ms: Long? = null @@ -82,6 +81,7 @@ class MediaPlaybackService : Service() { return START_STICKY; } + @OptIn(UnstableApi::class) fun setupNotificationRequirements() { _audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager; _notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager; @@ -91,47 +91,54 @@ class MediaPlaybackService : Service() { }; _notificationManager!!.createNotificationChannel(_notificationChannel!!); - _mediaSession = MediaSessionCompat(this, "PlayerState"); - _mediaSession?.setPlaybackState(PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f) - .build()); - _mediaSession?.setCallback(object: MediaSessionCompat.Callback() { - override fun onSeekTo(pos: Long) { - super.onSeekTo(pos) - Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)"); - MediaControlReceiver.onSeekToReceived.emit(pos); - } + _mediaSession = MediaSession.Builder(this, StatePlayer.instance.getPlayerOrCreate(this).player) - override fun onPlay() { - super.onPlay(); - Logger.i(TAG, "Media session callback onPlay()"); - MediaControlReceiver.onPlayReceived.emit(); - } + .setCallback(object: MediaSession.Callback { + override fun onMediaButtonEvent(session: MediaSession, controllerInfo: MediaSession.ControllerInfo, intent: Intent): Boolean { + //TODO: Reimplement + return super.onMediaButtonEvent(session, controllerInfo, intent) + } - override fun onPause() { - super.onPause(); - Logger.i(TAG, "Media session callback onPause()"); - MediaControlReceiver.onPauseReceived.emit(); - } + /*override fun onSeekTo(pos: Long) { + super.onSeekTo(pos) + Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)"); + MediaControlReceiver.onSeekToReceived.emit(pos); + } - override fun onStop() { - super.onStop(); - Logger.i(TAG, "Media session callback onStop()"); - MediaControlReceiver.onCloseReceived.emit(); - } + override fun onPlay() { + super.onPlay(); + Logger.i(TAG, "Media session callback onPlay()"); + MediaControlReceiver.onPlayReceived.emit(); + } - override fun onSkipToPrevious() { - super.onSkipToPrevious(); - Logger.i(TAG, "Media session callback onSkipToPrevious()"); - MediaControlReceiver.onPreviousReceived.emit(); - } + override fun onPause() { + super.onPause(); + Logger.i(TAG, "Media session callback onPause()"); + MediaControlReceiver.onPauseReceived.emit(); + } - override fun onSkipToNext() { - super.onSkipToNext() - Logger.i(TAG, "Media session callback onSkipToNext()"); - MediaControlReceiver.onNextReceived.emit(); - } - }); + override fun onStop() { + super.onStop(); + Logger.i(TAG, "Media session callback onStop()"); + MediaControlReceiver.onCloseReceived.emit(); + } + + override fun onSkipToPrevious() { + super.onSkipToPrevious(); + Logger.i(TAG, "Media session callback onSkipToPrevious()"); + MediaControlReceiver.onPreviousReceived.emit(); + } + + override fun onSkipToNext() { + super.onSkipToNext() + Logger.i(TAG, "Media session callback onSkipToNext()"); + MediaControlReceiver.onNextReceived.emit(); + }*/ + }) + .build(); + /*_mediaSession?.setPlaybackState(PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f) + .build());*/ } override fun onCreate() { @@ -186,15 +193,7 @@ class MediaPlaybackService : Service() { if(_notificationChannel == null || _mediaSession == null) setupNotificationRequirements(); - _mediaSession?.setMetadata( - MediaMetadataCompat.Builder() - .putString(MediaMetadata.METADATA_KEY_ARTIST, video.author.name) - .putString(MediaMetadata.METADATA_KEY_TITLE, video.name) - .putLong(MediaMetadata.METADATA_KEY_DURATION, video.duration * 1000) - .build()); - val thumbnail = video.thumbnails.getHQThumbnail(); - _notif_last_video = video; if(isUpdating) @@ -221,6 +220,7 @@ class MediaPlaybackService : Service() { private fun generateMediaAction(icon: Int, title: String, intent: PendingIntent) : NotificationCompat.Action { return NotificationCompat.Action.Builder(icon, title, intent).build(); } + @OptIn(UnstableApi::class) private fun notifyMediaSession(video: IPlatformVideo?, desiredBitmap: Bitmap?) { val channel = _notificationChannel ?: return; val session = _mediaSession ?: return; @@ -249,14 +249,9 @@ class MediaPlaybackService : Service() { .setOngoing(true) .setSilent(true) .setContentIntent(PendingIntent.getActivity(this, 5, bringUpIntent, PendingIntent.FLAG_IMMUTABLE)) - .setStyle(if(hasQueue) - androidx.media.app.NotificationCompat.MediaStyle() - .setMediaSession(session.sessionToken) - .setShowActionsInCompactView(0, 1, 2) - else - androidx.media.app.NotificationCompat.MediaStyle() - .setMediaSession(session.sessionToken) - .setShowActionsInCompactView(0)) + .setStyle( + if(hasQueue) MediaStyleNotificationHelper.MediaStyle(session)//.setShowActionsInCompactView(0, 1, 2) + else MediaStyleNotificationHelper.MediaStyle(session))//.setShowActionsInCompactView(0)) .setDeleteIntent(deleteIntent) .setChannelId(channel.id) @@ -303,7 +298,7 @@ class MediaPlaybackService : Service() { val notif = builder.build(); notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR; - Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.sessionToken}"); + Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.token}"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // For API 29 and above @@ -316,20 +311,7 @@ class MediaPlaybackService : Service() { _notif_last_bitmap = bitmap; } - fun updateMediaSessionPlaybackState(state: Int, pos: Long) { - _mediaSession?.setPlaybackState( - PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_SEEK_TO or - PlaybackStateCompat.ACTION_PLAY or - PlaybackStateCompat.ACTION_PAUSE or - PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or - PlaybackStateCompat.ACTION_SKIP_TO_NEXT or - PlaybackStateCompat.ACTION_PLAY_PAUSE - ) - .setState(state, pos, 1f, SystemClock.elapsedRealtime()) - .build()); - + fun updateMediaSessionPlaybackState() { if(_focusRequest == null) setAudioFocus(); } diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt index e1243a8de7c48a62d684bb1d0c4887104c0addbd..6f2ed3001255ac015548be0fd3b1695357bdb883 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt @@ -1,8 +1,12 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.states import android.content.Context +import androidx.annotation.OptIn +import androidx.media3.common.C +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.DefaultLoadControl +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.upstream.DefaultAllocator import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails @@ -13,10 +17,6 @@ import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.models.Playlist import com.futo.platformplayer.services.MediaPlaybackService import com.futo.platformplayer.video.PlayerManager -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.DefaultLoadControl -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.upstream.DefaultAllocator import kotlin.random.Random /*** @@ -107,8 +107,8 @@ class StatePlayer { fun updateMediaSession(videoUpdated: IPlatformVideoDetails?) { MediaPlaybackService.getService()?.updateMediaSession(videoUpdated); } - fun updateMediaSessionPlaybackState(state: Int, pos: Long) { - MediaPlaybackService.getService()?.updateMediaSessionPlaybackState(state, pos); + fun updateMediaSessionPlaybackState() { + MediaPlaybackService.getService()?.updateMediaSessionPlaybackState(); } fun closeMediaSession() { MediaPlaybackService.getService()?.closeMediaSession(); @@ -557,21 +557,23 @@ class StatePlayer { } //Player Initialization - fun getPlayerOrCreate(context : Context) : PlayerManager { + fun getPlayerOrCreate(context: Context) : PlayerManager { if(_exoplayer == null) { val player = createExoPlayer(context); _exoplayer = PlayerManager(player); } return _exoplayer!!; } - fun getThumbnailPlayerOrCreate(context : Context) : PlayerManager { + fun getThumbnailPlayerOrCreate(context: Context) : PlayerManager { if(_thumbnailExoPlayer == null) { val player = createExoPlayer(context); _thumbnailExoPlayer = PlayerManager(player); } return _thumbnailExoPlayer!!; } - private fun createExoPlayer(context : Context) : ExoPlayer { + + @OptIn(UnstableApi::class) + private fun createExoPlayer(context : Context): ExoPlayer { return ExoPlayer.Builder(context) .setLoadControl( DefaultLoadControl.Builder() @@ -589,7 +591,6 @@ class StatePlayer { .build(); } - fun dispose(){ val player = _exoplayer; val thumbPlayer = _thumbnailExoPlayer; diff --git a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt index adce7f3ae4f2a31c2f5f5cb6eb4ed04e1537f13e..05a6eef0b0a42e10b35718b9c69d6e1b4aedc2b0 100644 --- a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt +++ b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt @@ -1,15 +1,11 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.video -import android.media.session.PlaybackState -import android.support.v4.media.session.PlaybackStateCompat -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.ui.StyledPlayerView +import androidx.media3.common.Player +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.ui.PlayerView class PlayerManager { - private var _currentView: StyledPlayerView? = null; + private var _currentView: PlayerView? = null; private val _stateMap = HashMap<String, PlayerState>(); private var _currentState: PlayerState? = null; val currentState: PlayerState get() { @@ -25,16 +21,8 @@ class PlayerManager { this.player = exoPlayer; } - fun getPlaybackStateCompat() : Int { - return when(player.playbackState) { - ExoPlayer.STATE_READY -> if(player.playWhenReady) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED; - ExoPlayer.STATE_BUFFERING -> PlaybackState.STATE_BUFFERING; - else -> PlaybackState.STATE_NONE - } - } - @Synchronized - fun attach(view: StyledPlayerView, stateName: String) { + fun attach(view: PlayerView, stateName: String) { if(view != _currentView) { _currentView?.player = null; switchState(stateName); diff --git a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt index 535520a7597540cdd2791fc392a5ac4ebdd88ff9..52106dd621757e7db933d3e2ba613b2d6b76e260 100644 --- a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt @@ -1,10 +1,6 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.views.casting import android.content.Context -import android.media.session.PlaybackState -import android.support.v4.media.session.PlaybackStateCompat import android.util.AttributeSet import android.util.TypedValue import android.view.LayoutInflater @@ -13,7 +9,11 @@ import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import androidx.annotation.OptIn import androidx.constraintlayout.widget.ConstraintLayout +import androidx.media3.common.util.UnstableApi +import androidx.media3.ui.DefaultTimeBar +import androidx.media3.ui.TimeBar import com.bumptech.glide.Glide import com.futo.platformplayer.R import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails @@ -23,9 +23,6 @@ import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.toHumanTime import com.futo.platformplayer.views.behavior.GestureControlView -import com.google.android.exoplayer2.ui.DefaultTimeBar -import com.google.android.exoplayer2.ui.TimeBar -import com.google.android.exoplayer2.ui.TimeBar.OnScrubListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -54,6 +51,7 @@ class CastView : ConstraintLayout { val onMinimizeClick = Event0(); val onSettingsClick = Event0(); + @OptIn(UnstableApi::class) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { LayoutInflater.from(context).inflate(R.layout.view_cast, this, true); @@ -83,7 +81,7 @@ class CastView : ConstraintLayout { } _buttonLoop.setImageResource(if(StatePlayer.instance.loopVideo) R.drawable.ic_loop_active else R.drawable.ic_loop); - _timeBar.addListener(object : OnScrubListener { + _timeBar.addListener(object : TimeBar.OnScrubListener { override fun onScrubStart(timeBar: TimeBar, position: Long) { StateCasting.instance.videoSeekTo(position.toDouble()); } @@ -149,11 +147,9 @@ class CastView : ConstraintLayout { _buttonPlay.visibility = View.VISIBLE; } - val position = StateCasting.instance.activeDevice?.expectedCurrentTime?.times(1000.0)?.toLong(); - if(StatePlayer.instance.hasMediaSession()) { StatePlayer.instance.updateMediaSession(null); - StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), (position ?: 0)); + StatePlayer.instance.updateMediaSessionPlaybackState(); } } @@ -183,6 +179,7 @@ class CastView : ConstraintLayout { } } + @OptIn(UnstableApi::class) fun setVideoDetails(video: IPlatformVideoDetails, position: Long) { Glide.with(_thumbnail) .load(video.thumbnails.getHQThumbnail()) @@ -194,10 +191,11 @@ class CastView : ConstraintLayout { _timeBar.setDuration(video.duration); } + @OptIn(UnstableApi::class) fun setTime(ms: Long) { _textPosition.text = ms.toHumanTime(true); _timeBar.setPosition(ms / 1000); - StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), ms); + StatePlayer.instance.updateMediaSessionPlaybackState(); } fun cleanup() { @@ -207,13 +205,4 @@ class CastView : ConstraintLayout { _updateTimeJob = null; _scope.cancel(); } - - private fun getPlaybackStateCompat(): Int { - val d = StateCasting.instance.activeDevice ?: return PlaybackState.STATE_NONE; - - return when(d.isPlaying) { - true -> PlaybackStateCompat.STATE_PLAYING; - else -> PlaybackStateCompat.STATE_PAUSED; - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt index f470115502df6c832cc60894f3dd4e0a9103fa1c..453c5ed0d37a5159387105febc0cf9a627d10bc4 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.views.video import android.content.Context @@ -9,6 +7,10 @@ import android.view.View import android.widget.ImageButton import android.widget.LinearLayout import android.widget.TextView +import androidx.annotation.OptIn +import androidx.media3.common.util.UnstableApi +import androidx.media3.ui.PlayerControlView +import androidx.media3.ui.PlayerView import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource @@ -17,8 +19,6 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.toHumanTime import com.futo.platformplayer.video.PlayerManager -import com.google.android.exoplayer2.ui.PlayerControlView -import com.google.android.exoplayer2.ui.StyledPlayerView class FutoThumbnailPlayer : FutoVideoPlayerBase { @@ -28,7 +28,7 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase { } //Views - private val videoView : StyledPlayerView; + private val videoView : PlayerView; private val videoControls : PlayerControlView; private val buttonMute : ImageButton; private val buttonUnMute : ImageButton; @@ -41,7 +41,8 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase { private val _evMuteChanged = mutableListOf<(FutoThumbnailPlayer, Boolean)->Unit>(); - constructor(context : Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) { + @OptIn(UnstableApi::class) + constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) { LayoutInflater.from(context).inflate(R.layout.thumbnail_video_view, this, true); videoView = findViewById(R.id.video_player); @@ -70,7 +71,7 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase { } } - fun setLive(live : Boolean) { + fun setLive(live: Boolean) { if(live) { containerDuration.visibility = GONE; containerLive.visibility = VISIBLE; @@ -81,7 +82,8 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase { } } - fun setPlayer(player : PlayerManager?){ + @OptIn(UnstableApi::class) + fun setPlayer(player: PlayerManager?){ changePlayer(player); player?.attach(videoView, PLAYER_STATE_NAME); videoControls.player = player?.player; diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt index d306b090b890cd0fb1692db0153c47c192da29e9..ad606f267b178dc321aca939f785cc2f7e3590f7 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.views.video import android.content.Context @@ -15,6 +13,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout import android.widget.ImageButton import android.widget.TextView +import androidx.annotation.OptIn import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.setMargins import com.futo.platformplayer.R @@ -31,13 +30,14 @@ import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.views.behavior.GestureControlView -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.PlaybackParameters -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout -import com.google.android.exoplayer2.ui.PlayerControlView -import com.google.android.exoplayer2.ui.StyledPlayerView -import com.google.android.exoplayer2.ui.TimeBar -import com.google.android.exoplayer2.video.VideoSize +import androidx.media3.common.PlaybackParameters +import androidx.media3.common.VideoSize +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.ui.AspectRatioFrameLayout +import androidx.media3.ui.PlayerControlView +import androidx.media3.ui.PlayerView +import androidx.media3.ui.TimeBar import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -58,7 +58,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { //Views private val _root: ConstraintLayout; - private val _videoView: StyledPlayerView; + private val _videoView: PlayerView; val videoControls: PlayerControlView; private val _videoControls_fullscreen: PlayerControlView; @@ -127,6 +127,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { val onVideoClicked = Event0(); val onTimeBarChanged = Event2<Long, Long>(); + @OptIn(UnstableApi::class) constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) { LayoutInflater.from(context).inflate(R.layout.video_view, this, true); _root = findViewById(R.id.videoview_root); @@ -139,8 +140,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase { _control_rotate_lock = videoControls.findViewById(R.id.exo_rotate_lock); _control_loop = videoControls.findViewById(R.id.exo_loop); _control_cast = videoControls.findViewById(R.id.exo_cast); - _control_play = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play); - _time_bar = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress); + _control_play = videoControls.findViewById(androidx.media3.ui.R.id.exo_play); + _time_bar = videoControls.findViewById(androidx.media3.ui.R.id.exo_progress); _control_chapter = videoControls.findViewById(R.id.text_chapter_current); _buttonNext = videoControls.findViewById(R.id.button_next); _buttonPrevious = videoControls.findViewById(R.id.button_previous); @@ -152,9 +153,9 @@ class FutoVideoPlayer : FutoVideoPlayerBase { _control_rotate_lock_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_rotate_lock); _control_loop_fullscreen = videoControls.findViewById(R.id.exo_loop); _control_cast_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_cast); - _control_play_fullscreen = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play); + _control_play_fullscreen = videoControls.findViewById(androidx.media3.ui.R.id.exo_play); _control_chapter_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_chapter_current); - _time_bar_fullscreen = _videoControls_fullscreen.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress); + _time_bar_fullscreen = _videoControls_fullscreen.findViewById(androidx.media3.ui.R.id.exo_progress); _buttonPrevious_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_previous); _buttonNext_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_next); @@ -404,14 +405,15 @@ class FutoVideoPlayer : FutoVideoPlayerBase { return false; } + @OptIn(UnstableApi::class) fun setArtwork(drawable: Drawable?) { if (drawable != null) { _videoView.defaultArtwork = drawable; - _videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_FILL; + _videoView.artworkDisplayMode = PlayerView.ARTWORK_DISPLAY_MODE_FILL; fitOrFill(isFullScreen); } else { _videoView.defaultArtwork = null; - _videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_OFF; + _videoView.artworkDisplayMode = PlayerView.ARTWORK_DISPLAY_MODE_OFF; } } @@ -436,6 +438,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { return exoPlayer?.player?.playbackParameters?.speed ?: 1.0f; } + @OptIn(UnstableApi::class) fun setFullScreen(fullScreen: Boolean) { if (isFullScreen == fullScreen) { return; @@ -538,6 +541,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { } //Sizing + @OptIn(UnstableApi::class) fun fitHeight(videoSize : VideoSize? = null){ Logger.i(TAG, "Video Fit Height"); if(_originalBottomMargin != 0) { diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index 9a23b935c60efd8030816e10e718391b8d7336e1..9ccc0a6d8b4c747eb8ad55c6ba976a8d30ec5371 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -1,11 +1,27 @@ -@file:Suppress("DEPRECATION") - package com.futo.platformplayer.views.video import android.content.Context import android.net.Uri import android.util.AttributeSet import android.widget.RelativeLayout +import androidx.annotation.OptIn +import androidx.media3.common.C +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.common.VideoSize +import androidx.media3.common.text.CueGroup +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultDataSource +import androidx.media3.datasource.DefaultHttpDataSource +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.dash.DashMediaSource +import androidx.media3.exoplayer.hls.HlsMediaSource +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.MergingMediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource +import androidx.media3.exoplayer.source.SingleSampleMediaSource +import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import com.futo.platformplayer.Settings import com.futo.platformplayer.api.media.models.chapters.IChapter import com.futo.platformplayer.api.media.models.streams.VideoMuxedSourceDescriptor @@ -28,22 +44,6 @@ import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.video.PlayerManager -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.PlaybackException -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.source.MediaSource -import com.google.android.exoplayer2.source.MergingMediaSource -import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.source.SingleSampleMediaSource -import com.google.android.exoplayer2.source.dash.DashMediaSource -import com.google.android.exoplayer2.source.hls.HlsMediaSource -import com.google.android.exoplayer2.text.CueGroup -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector -import com.google.android.exoplayer2.upstream.DefaultDataSource -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource -import com.google.android.exoplayer2.video.VideoSize import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -251,6 +251,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { _targetTrackAudioBitrate = bitrate; updateTrackSelector(); } + @OptIn(UnstableApi::class) private fun updateTrackSelector() { var builder = DefaultTrackSelector.Parameters.Builder(context); if(_targetTrackVideoHeight > 0) { @@ -298,6 +299,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { return loadSelectedSources(play, resume); } + @OptIn(UnstableApi::class) fun swapSubtitles(scope: CoroutineScope, subtitles: ISubtitleSource?) { if(subtitles == null) clearSubtitles(); @@ -369,6 +371,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { } //Video loads + @OptIn(UnstableApi::class) private fun swapVideoSourceLocal(videoSource: LocalVideoSource) { Logger.i(TAG, "Loading VideoSource [Local]"); val file = File(videoSource.filePath); @@ -377,14 +380,13 @@ abstract class FutoVideoPlayerBase : RelativeLayout { _lastVideoMediaSource = ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context)) .createMediaSource(MediaItem.fromUri(Uri.fromFile(file))); } + @OptIn(UnstableApi::class) private fun swapVideoSourceUrlRange(videoSource: JSVideoUrlRangeSource) { Logger.i(TAG, "Loading JSVideoUrlRangeSource"); if(videoSource.hasItag) { //Temporary workaround for Youtube try { _lastVideoMediaSource = VideoHelper.convertItagSourceToChunkedDashSource(videoSource); - if(_lastVideoMediaSource == null) - throw java.lang.IllegalStateException("Dash manifest workaround failed"); return; } //If it fails to create the dash workaround, fallback to standard progressive @@ -397,18 +399,21 @@ abstract class FutoVideoPlayerBase : RelativeLayout { } else throw IllegalArgumentException("source without itag data..."); } + @OptIn(UnstableApi::class) private fun swapVideoSourceUrl(videoSource: IVideoUrlSource) { Logger.i(TAG, "Loading VideoSource [Url]"); _lastVideoMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory() .setUserAgent(DEFAULT_USER_AGENT)) .createMediaSource(MediaItem.fromUri(videoSource.getVideoUrl())); } + @OptIn(UnstableApi::class) private fun swapVideoSourceDash(videoSource: IDashManifestSource) { Logger.i(TAG, "Loading VideoSource [Dash]"); _lastVideoMediaSource = DashMediaSource.Factory(DefaultHttpDataSource.Factory() .setUserAgent(DEFAULT_USER_AGENT)) .createMediaSource(MediaItem.fromUri(videoSource.url)) } + @OptIn(UnstableApi::class) private fun swapVideoSourceHLS(videoSource: IHLSManifestSource) { Logger.i(TAG, "Loading VideoSource [HLS]"); _lastVideoMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory() @@ -416,7 +421,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout { .createMediaSource(MediaItem.fromUri(videoSource.url)); } + //Audio loads + @OptIn(UnstableApi::class) private fun swapAudioSourceLocal(audioSource: LocalAudioSource) { Logger.i(TAG, "Loading AudioSource [Local]"); val file = File(audioSource.filePath); @@ -425,6 +432,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { _lastAudioMediaSource = ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context)) .createMediaSource(MediaItem.fromUri(Uri.fromFile(file))); } + @OptIn(UnstableApi::class) private fun swapAudioSourceUrlRange(audioSource: JSAudioUrlRangeSource) { Logger.i(TAG, "Loading JSAudioUrlRangeSource"); if(audioSource.hasItag) { @@ -444,12 +452,14 @@ abstract class FutoVideoPlayerBase : RelativeLayout { } else throw IllegalArgumentException("source without itag data...") } + @OptIn(UnstableApi::class) private fun swapAudioSourceUrl(audioSource: IAudioUrlSource) { Logger.i(TAG, "Loading AudioSource [Url]"); _lastAudioMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory() .setUserAgent(DEFAULT_USER_AGENT)) .createMediaSource(MediaItem.fromUri(audioSource.getAudioUrl())); } + @OptIn(UnstableApi::class) private fun swapAudioSourceHLS(audioSource: IHLSManifestAudioSource) { Logger.i(TAG, "Loading AudioSource [HLS]"); _lastAudioMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory() @@ -479,6 +489,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { return VideoHelper.selectBestAudioSource(video.video, PREFERED_AUDIO_CONTAINERS, preferredLanguage); } + @OptIn(UnstableApi::class) private fun loadSelectedSources(play: Boolean, resume: Boolean): Boolean { val sourceVideo = if(!isAudioMode || _lastAudioMediaSource == null) _lastVideoMediaSource else null; val sourceAudio = _lastAudioMediaSource; @@ -506,11 +517,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout { return true; } + @OptIn(UnstableApi::class) private fun reloadMediaSource(play: Boolean = false, resume: Boolean = true) { - val player = exoPlayer - if (player == null) - return; - + val player = exoPlayer ?: return val positionBefore = player.player.currentPosition; if(_mediaSource != null) { player.player.setMediaSource(_mediaSource!!); diff --git a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java index 00a49cf85d8c89f0b6d5f95fb61ea28cdc7a5b12..d153c4405606ec9488baebb7f0bc39d73ef41f1a 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java +++ b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java @@ -1,26 +1,28 @@ package com.futo.platformplayer.views.video.datasources; -import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader; -import static com.google.android.exoplayer2.util.Assertions.checkNotNull; -import static com.google.android.exoplayer2.util.Util.castNonNull; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Util.castNonNull; +import static androidx.media3.datasource.HttpUtil.buildRangeRequestHeader; import static java.lang.Math.min; import android.net.Uri; +import android.util.Log; + import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.upstream.BaseDataSource; -import com.google.android.exoplayer2.upstream.DataSourceException; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.HttpUtil; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.common.C; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; +import androidx.media3.datasource.BaseDataSource; +import androidx.media3.datasource.DataSourceException; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.HttpUtil; +import androidx.media3.datasource.TransferListener; + import com.google.common.base.Predicate; import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMap; @@ -45,6 +47,7 @@ import java.util.zip.GZIPInputStream; * Based on the default ExoPlayer DefaultHttpDataSource */ +@UnstableApi public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { public static final class Factory implements HttpDataSource.Factory { @@ -142,7 +145,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { /** * Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a * {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link - * JSHttpDataSource#open(com.google.android.exoplayer2.upstream.DataSpec)}. + * JSHttpDataSource#open(androidx.media3.datasource.DataSpec)}. * * <p>The default is {@code null}. * @@ -160,7 +163,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { * * <p>The default is {@code null}. * - * <p>See {@link com.google.android.exoplayer2.upstream.DataSource#addTransferListener(TransferListener)}. + * <p>See {@link androidx.media3.datasource.DataSource#addTransferListener(TransferListener)}. * * @param transferListener The listener that will be used. * @return This factory. @@ -367,12 +370,12 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { if (dataSpec.length != C.LENGTH_UNSET) { bytesToRead = dataSpec.length; } else { - long contentLength = - HttpUtil.getContentLength( - connection.getHeaderField(HttpHeaders.CONTENT_LENGTH), - connection.getHeaderField(HttpHeaders.CONTENT_RANGE)); - bytesToRead = - contentLength != C.LENGTH_UNSET ? (contentLength - bytesToSkip) : C.LENGTH_UNSET; + long contentLength = HttpUtil.getContentLength( + connection.getHeaderField(HttpHeaders.CONTENT_LENGTH), + connection.getHeaderField(HttpHeaders.CONTENT_RANGE) + ); + + bytesToRead = contentLength != C.LENGTH_UNSET ? (contentLength - bytesToSkip) : C.LENGTH_UNSET; } } else { // Gzip is enabled. If the server opts to use gzip then the content length in the response @@ -457,7 +460,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { /** Establishes a connection, following redirects to do so where permitted. */ private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException { URL url = new URL(dataSpec.uri.toString()); - @HttpMethod int httpMethod = dataSpec.httpMethod; + @DataSpec.HttpMethod int httpMethod = dataSpec.httpMethod; @Nullable byte[] httpBody = dataSpec.httpBody; long position = dataSpec.position; long length = dataSpec.length; @@ -543,7 +546,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { */ private HttpURLConnection makeConnection( URL url, - @HttpMethod int httpMethod, + @DataSpec.HttpMethod int httpMethod, @Nullable byte[] httpBody, long position, long length, diff --git a/app/src/main/res/layout/thumbnail_video_view.xml b/app/src/main/res/layout/thumbnail_video_view.xml index 20b4cd71430c0b6a1484ca7dd2f54097804cfcb5..b63e6715659ee7a14afb401019a521dde7689217 100644 --- a/app/src/main/res/layout/thumbnail_video_view.xml +++ b/app/src/main/res/layout/thumbnail_video_view.xml @@ -4,7 +4,7 @@ android:layout_height="wrap_content" android:background="@color/transparent" xmlns:app="http://schemas.android.com/apk/res-auto"> - <com.google.android.exoplayer2.ui.StyledPlayerView + <com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_player" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/app/src/main/res/layout/video_view.xml b/app/src/main/res/layout/video_view.xml index 10f9a8fb27d8f6507321fa79d8aa97d44e23e5aa..3cb0bc597644ccfd1c74a9e122c049622a92a951 100644 --- a/app/src/main/res/layout/video_view.xml +++ b/app/src/main/res/layout/video_view.xml @@ -6,7 +6,7 @@ android:id="@+id/videoview_root" android:background="@color/transparent" xmlns:app="http://schemas.android.com/apk/res-auto"> - <com.google.android.exoplayer2.ui.StyledPlayerView + <com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_player" android:layout_width="match_parent" android:layout_height="match_parent"