From 7d1aa8b7b3fccc003dd4fe5d827aadc1bc912d81 Mon Sep 17 00:00:00 2001 From: Matthew Rice Date: Fri, 29 Jul 2022 10:07:16 -0600 Subject: [PATCH 1/3] Updates exoplayer to match flutter video_player. Fixes conflicts from update --- android/build.gradle | 2 +- .../jhomlala/better_player/BetterPlayer.kt | 401 +++++++++++------- 2 files changed, 248 insertions(+), 155 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 40d3f6b57..a8e5e1b5b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ group 'com.jhomlala.better_player.better_player' version '1.0-SNAPSHOT' buildscript { - ext.exoPlayerVersion = "2.17.1" + ext.exoPlayerVersion = "2.18.0" ext.lifecycleVersion = "2.4.0-beta01" ext.annotationVersion = "1.2.0" ext.workVersion = "2.7.0" diff --git a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt index f5e494430..074ac4d00 100644 --- a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt +++ b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent -import android.content.ComponentName import android.content.Context import android.content.Intent import android.graphics.Bitmap @@ -32,7 +31,6 @@ import com.google.android.exoplayer2.drm.FrameworkMediaDrm import com.google.android.exoplayer2.drm.UnsupportedDrmException import com.google.android.exoplayer2.drm.DummyExoMediaDrm import com.google.android.exoplayer2.drm.LocalMediaDrmCallback -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.source.MediaSource import com.google.android.exoplayer2.source.ClippingMediaSource import com.google.android.exoplayer2.ui.PlayerNotificationManager.MediaDescriptionAdapter @@ -51,14 +49,12 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory import io.flutter.plugin.common.EventChannel.EventSink -import androidx.media.session.MediaButtonReceiver import androidx.work.Data import com.google.android.exoplayer2.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.drm.DrmSessionManagerProvider import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride -import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides +import com.google.android.exoplayer2.trackselection.TrackSelectionOverride import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultDataSource import com.google.android.exoplayer2.util.Util @@ -78,7 +74,8 @@ internal class BetterPlayer( ) { private val exoPlayer: ExoPlayer? private val eventSink = QueuingEventSink() - private val trackSelector: DefaultTrackSelector = DefaultTrackSelector(context) + private val trackSelector: DefaultTrackSelector = + DefaultTrackSelector(context) private val loadControl: LoadControl private var isInitialized = false private var surface: Surface? = null @@ -137,14 +134,20 @@ internal class BetterPlayer( val userAgent = getUserAgent(headers) if (licenseUrl != null && licenseUrl.isNotEmpty()) { val httpMediaDrmCallback = - HttpMediaDrmCallback(licenseUrl, DefaultHttpDataSource.Factory()) + HttpMediaDrmCallback( + licenseUrl, + DefaultHttpDataSource.Factory() + ) if (drmHeaders != null) { for ((drmKey, drmValue) in drmHeaders) { httpMediaDrmCallback.setKeyRequestProperty(drmKey, drmValue) } } if (Util.SDK_INT < 18) { - Log.e(TAG, "Protected content not supported on API levels below 18") + Log.e( + TAG, + "Protected content not supported on API levels below 18" + ) drmSessionManager = null } else { val drmSchemeUuid = Util.getDrmUuid("widevine") @@ -154,9 +157,13 @@ internal class BetterPlayer( drmSchemeUuid ) { uuid: UUID? -> try { - val mediaDrm = FrameworkMediaDrm.newInstance(uuid!!) + val mediaDrm = + FrameworkMediaDrm.newInstance(uuid!!) // Force L3. - mediaDrm.setPropertyString("securityLevel", "L3") + mediaDrm.setPropertyString( + "securityLevel", + "L3" + ) return@setUuidAndExoMediaDrmProvider mediaDrm } catch (e: UnsupportedDrmException) { return@setUuidAndExoMediaDrmProvider DummyExoMediaDrm() @@ -168,7 +175,10 @@ internal class BetterPlayer( } } else if (clearKey != null && clearKey.isNotEmpty()) { drmSessionManager = if (Util.SDK_INT < 18) { - Log.e(TAG, "Protected content not supported on API levels below 18") + Log.e( + TAG, + "Protected content not supported on API levels below 18" + ) null } else { DefaultDrmSessionManager.Builder() @@ -193,9 +203,16 @@ internal class BetterPlayer( } else { dataSourceFactory = DefaultDataSource.Factory(context) } - val mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, cacheKey, context) + val mediaSource = buildMediaSource( + uri, + dataSourceFactory, + formatHint, + cacheKey, + context + ) if (overriddenDuration != 0L) { - val clippingMediaSource = ClippingMediaSource(mediaSource, 0, overriddenDuration * 1000) + val clippingMediaSource = + ClippingMediaSource(mediaSource, 0, overriddenDuration * 1000) exoPlayer?.setMediaSource(clippingMediaSource) } else { exoPlayer?.setMediaSource(mediaSource) @@ -209,86 +226,92 @@ internal class BetterPlayer( imageUrl: String?, notificationChannelName: String?, activityName: String ) { - val mediaDescriptionAdapter: MediaDescriptionAdapter = object : MediaDescriptionAdapter { - override fun getCurrentContentTitle(player: Player): String { - return title - } - - @SuppressLint("UnspecifiedImmutableFlag") - override fun createCurrentContentIntent(player: Player): PendingIntent? { - val packageName = context.applicationContext.packageName - val notificationIntent = Intent() - notificationIntent.setClassName( - packageName, - "$packageName.$activityName" - ) - notificationIntent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TOP - or Intent.FLAG_ACTIVITY_SINGLE_TOP) - return PendingIntent.getActivity( - context, 0, - notificationIntent, - PendingIntent.FLAG_IMMUTABLE - ) - } - - override fun getCurrentContentText(player: Player): String? { - return author - } + val mediaDescriptionAdapter: MediaDescriptionAdapter = + object : MediaDescriptionAdapter { + override fun getCurrentContentTitle(player: Player): String { + return title + } - override fun getCurrentLargeIcon( - player: Player, - callback: BitmapCallback - ): Bitmap? { - if (imageUrl == null) { - return null + @SuppressLint("UnspecifiedImmutableFlag") + override fun createCurrentContentIntent(player: Player): PendingIntent? { + val packageName = context.applicationContext.packageName + val notificationIntent = Intent() + notificationIntent.setClassName( + packageName, + "$packageName.$activityName" + ) + notificationIntent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TOP + or Intent.FLAG_ACTIVITY_SINGLE_TOP) + return PendingIntent.getActivity( + context, 0, + notificationIntent, + PendingIntent.FLAG_IMMUTABLE + ) } - if (bitmap != null) { - return bitmap + + override fun getCurrentContentText(player: Player): String? { + return author } - val imageWorkRequest = OneTimeWorkRequest.Builder(ImageWorker::class.java) - .addTag(imageUrl) - .setInputData( - Data.Builder() - .putString(BetterPlayerPlugin.URL_PARAMETER, imageUrl) + + override fun getCurrentLargeIcon( + player: Player, + callback: BitmapCallback + ): Bitmap? { + if (imageUrl == null) { + return null + } + if (bitmap != null) { + return bitmap + } + val imageWorkRequest = + OneTimeWorkRequest.Builder(ImageWorker::class.java) + .addTag(imageUrl) + .setInputData( + Data.Builder() + .putString( + BetterPlayerPlugin.URL_PARAMETER, + imageUrl + ) + .build() + ) .build() - ) - .build() - workManager.enqueue(imageWorkRequest) - val workInfoObserver = Observer { workInfo: WorkInfo? -> - try { - if (workInfo != null) { - val state = workInfo.state - if (state == WorkInfo.State.SUCCEEDED) { - val outputData = workInfo.outputData - val filePath = - outputData.getString(BetterPlayerPlugin.FILE_PATH_PARAMETER) - //Bitmap here is already processed and it's very small, so it won't - //break anything. - bitmap = BitmapFactory.decodeFile(filePath) - bitmap?.let { bitmap -> - callback.onBitmap(bitmap) + workManager.enqueue(imageWorkRequest) + val workInfoObserver = Observer { workInfo: WorkInfo? -> + try { + if (workInfo != null) { + val state = workInfo.state + if (state == WorkInfo.State.SUCCEEDED) { + val outputData = workInfo.outputData + val filePath = + outputData.getString(BetterPlayerPlugin.FILE_PATH_PARAMETER) + //Bitmap here is already processed and it's very small, so it won't + //break anything. + bitmap = BitmapFactory.decodeFile(filePath) + bitmap?.let { bitmap -> + callback.onBitmap(bitmap) + } } - } - if (state == WorkInfo.State.SUCCEEDED || state == WorkInfo.State.CANCELLED || state == WorkInfo.State.FAILED) { - val uuid = imageWorkRequest.id - val observer = workerObserverMap.remove(uuid) - if (observer != null) { - workManager.getWorkInfoByIdLiveData(uuid) - .removeObserver(observer) + if (state == WorkInfo.State.SUCCEEDED || state == WorkInfo.State.CANCELLED || state == WorkInfo.State.FAILED) { + val uuid = imageWorkRequest.id + val observer = + workerObserverMap.remove(uuid) + if (observer != null) { + workManager.getWorkInfoByIdLiveData(uuid) + .removeObserver(observer) + } } } + } catch (exception: Exception) { + Log.e(TAG, "Image select error: $exception") } - } catch (exception: Exception) { - Log.e(TAG, "Image select error: $exception") } + val workerUuid = imageWorkRequest.id + workManager.getWorkInfoByIdLiveData(workerUuid) + .observeForever(workInfoObserver) + workerObserverMap[workerUuid] = workInfoObserver + return null } - val workerUuid = imageWorkRequest.id - workManager.getWorkInfoByIdLiveData(workerUuid) - .observeForever(workInfoObserver) - workerObserverMap[workerUuid] = workInfoObserver - return null } - } var playerNotificationChannelName = notificationChannelName if (notificationChannelName == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -328,17 +351,26 @@ internal class BetterPlayer( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { refreshHandler = Handler(Looper.getMainLooper()) refreshRunnable = Runnable { - val playbackState: PlaybackStateCompat = if (exoPlayer?.isPlaying == true) { - PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SEEK_TO) - .setState(PlaybackStateCompat.STATE_PLAYING, position, 1.0f) - .build() - } else { - PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SEEK_TO) - .setState(PlaybackStateCompat.STATE_PAUSED, position, 1.0f) - .build() - } + val playbackState: PlaybackStateCompat = + if (exoPlayer?.isPlaying == true) { + PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SEEK_TO) + .setState( + PlaybackStateCompat.STATE_PLAYING, + position, + 1.0f + ) + .build() + } else { + PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SEEK_TO) + .setState( + PlaybackStateCompat.STATE_PAUSED, + position, + 1.0f + ) + .build() + } mediaSession?.setPlaybackState(playbackState) refreshHandler?.postDelayed(refreshRunnable!!, 1000) } @@ -348,7 +380,10 @@ internal class BetterPlayer( override fun onPlaybackStateChanged(playbackState: Int) { mediaSession?.setMetadata( MediaMetadataCompat.Builder() - .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, getDuration()) + .putLong( + MediaMetadataCompat.METADATA_KEY_DURATION, + getDuration() + ) .build() ) } @@ -383,17 +418,13 @@ internal class BetterPlayer( ): MediaSource { val type: Int if (formatHint == null) { - var lastPathSegment = uri.lastPathSegment - if (lastPathSegment == null) { - lastPathSegment = "" - } - type = Util.inferContentType(lastPathSegment) + type = Util.inferContentType(uri) } else { type = when (formatHint) { - FORMAT_SS -> C.TYPE_SS - FORMAT_DASH -> C.TYPE_DASH - FORMAT_HLS -> C.TYPE_HLS - FORMAT_OTHER -> C.TYPE_OTHER + FORMAT_SS -> C.CONTENT_TYPE_SS + FORMAT_DASH -> C.CONTENT_TYPE_DASH + FORMAT_HLS -> C.CONTENT_TYPE_HLS + FORMAT_OTHER -> C.CONTENT_TYPE_OTHER else -> -1 } } @@ -405,30 +436,40 @@ internal class BetterPlayer( val mediaItem = mediaItemBuilder.build() var drmSessionManagerProvider: DrmSessionManagerProvider? = null drmSessionManager?.let { drmSessionManager -> - drmSessionManagerProvider = DrmSessionManagerProvider { drmSessionManager } + drmSessionManagerProvider = + DrmSessionManagerProvider { drmSessionManager } } return when (type) { - C.TYPE_SS -> SsMediaSource.Factory( + C.CONTENT_TYPE_SS -> SsMediaSource.Factory( DefaultSsChunkSource.Factory(mediaDataSourceFactory), DefaultDataSource.Factory(context, mediaDataSourceFactory) - ) - .setDrmSessionManagerProvider(drmSessionManagerProvider) - .createMediaSource(mediaItem) - C.TYPE_DASH -> DashMediaSource.Factory( + ).apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } + }.createMediaSource(mediaItem) + C.CONTENT_TYPE_DASH -> DashMediaSource.Factory( DefaultDashChunkSource.Factory(mediaDataSourceFactory), DefaultDataSource.Factory(context, mediaDataSourceFactory) - ) - .setDrmSessionManagerProvider(drmSessionManagerProvider) - .createMediaSource(mediaItem) - C.TYPE_HLS -> HlsMediaSource.Factory(mediaDataSourceFactory) - .setDrmSessionManagerProvider(drmSessionManagerProvider) - .createMediaSource(mediaItem) - C.TYPE_OTHER -> ProgressiveMediaSource.Factory( + ).apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } + }.createMediaSource(mediaItem) + C.CONTENT_TYPE_HLS -> HlsMediaSource.Factory(mediaDataSourceFactory) + .apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } + }.createMediaSource(mediaItem) + C.CONTENT_TYPE_OTHER -> ProgressiveMediaSource.Factory( mediaDataSourceFactory, DefaultExtractorsFactory() - ) - .setDrmSessionManagerProvider(drmSessionManagerProvider) - .createMediaSource(mediaItem) + ).apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } + }.createMediaSource(mediaItem) else -> { throw IllegalStateException("Unsupported type: $type") } @@ -436,7 +477,9 @@ internal class BetterPlayer( } private fun setupVideoPlayer( - eventChannel: EventChannel, textureEntry: SurfaceTextureEntry, result: MethodChannel.Result + eventChannel: EventChannel, + textureEntry: SurfaceTextureEntry, + result: MethodChannel.Result ) { eventChannel.setStreamHandler( object : EventChannel.StreamHandler { @@ -482,7 +525,11 @@ internal class BetterPlayer( } override fun onPlayerError(error: PlaybackException) { - eventSink.error("VideoError", "Video player had error $error", "") + eventSink.error( + "VideoError", + "Video player had error $error", + "" + ) } }) val reply: MutableMap = HashMap() @@ -504,16 +551,21 @@ internal class BetterPlayer( } @Suppress("DEPRECATION") - private fun setAudioAttributes(exoPlayer: ExoPlayer?, mixWithOthers: Boolean) { + private fun setAudioAttributes( + exoPlayer: ExoPlayer?, + mixWithOthers: Boolean + ) { val audioComponent = exoPlayer?.audioComponent ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { audioComponent.setAudioAttributes( - AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), + AudioAttributes.Builder() + .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(), !mixWithOthers ) } else { audioComponent.setAudioAttributes( - AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MUSIC).build(), + AudioAttributes.Builder() + .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC).build(), !mixWithOthers ) } @@ -528,7 +580,8 @@ internal class BetterPlayer( } fun setLooping(value: Boolean) { - exoPlayer?.repeatMode = if (value) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF + exoPlayer?.repeatMode = + if (value) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF } fun setVolume(value: Double) { @@ -571,7 +624,10 @@ internal class BetterPlayer( timeline?.let { if (!timeline.isEmpty) { val windowStartTimeMs = - timeline.getWindow(0, Timeline.Window()).windowStartTimeMs + timeline.getWindow( + 0, + Timeline.Window() + ).windowStartTimeMs val pos = exoPlayer?.currentPosition ?: 0L return windowStartTimeMs + pos } @@ -621,7 +677,8 @@ internal class BetterPlayer( 0, mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE ) - val mediaSession = MediaSessionCompat(context, TAG, null, pendingIntent) + val mediaSession = + MediaSessionCompat(context, TAG, null, pendingIntent) mediaSession.setCallback(object : MediaSessionCompat.Callback() { override fun onSeekTo(pos: Long) { sendSeekToEvent(pos) @@ -659,7 +716,8 @@ internal class BetterPlayer( if (mappedTrackInfo.getRendererType(rendererIndex) != C.TRACK_TYPE_AUDIO) { continue } - val trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex) + val trackGroupArray = + mappedTrackInfo.getTrackGroups(rendererIndex) var hasElementWithoutLabel = false var hasStrangeAudioTrack = false for (groupIndex in 0 until trackGroupArray.length) { @@ -679,18 +737,30 @@ internal class BetterPlayer( for (groupElementIndex in 0 until group.length) { val label = group.getFormat(groupElementIndex).label if (name == label && index == groupIndex) { - setAudioTrack(rendererIndex, groupIndex, groupElementIndex) + setAudioTrack( + rendererIndex, + groupIndex, + groupElementIndex + ) return } ///Fallback option if (!hasStrangeAudioTrack && hasElementWithoutLabel && index == groupIndex) { - setAudioTrack(rendererIndex, groupIndex, groupElementIndex) + setAudioTrack( + rendererIndex, + groupIndex, + groupElementIndex + ) return } ///Fallback option if (hasStrangeAudioTrack && name == label) { - setAudioTrack(rendererIndex, groupIndex, groupElementIndex) + setAudioTrack( + rendererIndex, + groupIndex, + groupElementIndex + ) return } } @@ -702,22 +772,22 @@ internal class BetterPlayer( } } - private fun setAudioTrack(rendererIndex: Int, groupIndex: Int, groupElementIndex: Int) { + private fun setAudioTrack( + rendererIndex: Int, + groupIndex: Int, + groupElementIndex: Int + ) { val mappedTrackInfo = trackSelector.currentMappedTrackInfo if (mappedTrackInfo != null) { + val builder = trackSelector.parameters.buildUpon() .setRendererDisabled(rendererIndex, false) - .setTrackSelectionOverrides( - TrackSelectionOverrides.Builder().addOverride( - TrackSelectionOverrides.TrackSelectionOverride( - mappedTrackInfo.getTrackGroups( - rendererIndex - ).get(groupIndex) - ) - ).build() - ) + .addOverride(TrackSelectionOverride(mappedTrackInfo.getTrackGroups( + rendererIndex + ).get(groupIndex), rendererIndex)) + .build() - trackSelector.setParameters(builder) + trackSelector.parameters = builder } } @@ -765,7 +835,8 @@ internal class BetterPlayer( private const val FORMAT_DASH = "dash" private const val FORMAT_HLS = "hls" private const val FORMAT_OTHER = "other" - private const val DEFAULT_NOTIFICATION_CHANNEL = "BETTER_PLAYER_NOTIFICATION" + private const val DEFAULT_NOTIFICATION_CHANNEL = + "BETTER_PLAYER_NOTIFICATION" private const val NOTIFICATION_ID = 20772077 //Clear cache without accessing BetterPlayerCache. @@ -798,17 +869,34 @@ internal class BetterPlayer( //Start pre cache of video. Invoke work manager job and start caching in background. fun preCache( - context: Context?, dataSource: String?, preCacheSize: Long, - maxCacheSize: Long, maxCacheFileSize: Long, headers: Map, - cacheKey: String?, result: MethodChannel.Result + context: Context?, + dataSource: String?, + preCacheSize: Long, + maxCacheSize: Long, + maxCacheFileSize: Long, + headers: Map, + cacheKey: String?, + result: MethodChannel.Result ) { val dataBuilder = Data.Builder() .putString(BetterPlayerPlugin.URL_PARAMETER, dataSource) - .putLong(BetterPlayerPlugin.PRE_CACHE_SIZE_PARAMETER, preCacheSize) - .putLong(BetterPlayerPlugin.MAX_CACHE_SIZE_PARAMETER, maxCacheSize) - .putLong(BetterPlayerPlugin.MAX_CACHE_FILE_SIZE_PARAMETER, maxCacheFileSize) + .putLong( + BetterPlayerPlugin.PRE_CACHE_SIZE_PARAMETER, + preCacheSize + ) + .putLong( + BetterPlayerPlugin.MAX_CACHE_SIZE_PARAMETER, + maxCacheSize + ) + .putLong( + BetterPlayerPlugin.MAX_CACHE_FILE_SIZE_PARAMETER, + maxCacheFileSize + ) if (cacheKey != null) { - dataBuilder.putString(BetterPlayerPlugin.CACHE_KEY_PARAMETER, cacheKey) + dataBuilder.putString( + BetterPlayerPlugin.CACHE_KEY_PARAMETER, + cacheKey + ) } for (headerKey in headers.keys) { dataBuilder.putString( @@ -817,9 +905,10 @@ internal class BetterPlayer( ) } if (dataSource != null && context != null) { - val cacheWorkRequest = OneTimeWorkRequest.Builder(CacheWorker::class.java) - .addTag(dataSource) - .setInputData(dataBuilder.build()).build() + val cacheWorkRequest = + OneTimeWorkRequest.Builder(CacheWorker::class.java) + .addTag(dataSource) + .setInputData(dataBuilder.build()).build() WorkManager.getInstance(context).enqueue(cacheWorkRequest) } result.success(null) @@ -827,7 +916,11 @@ internal class BetterPlayer( //Stop pre cache of video with given url. If there's no work manager job for given url, then //it will be ignored. - fun stopPreCache(context: Context?, url: String?, result: MethodChannel.Result) { + fun stopPreCache( + context: Context?, + url: String?, + result: MethodChannel.Result + ) { if (url != null && context != null) { WorkManager.getInstance(context).cancelAllWorkByTag(url) } From 0c7ff8dfdf8e4459aa4e8edb6bbfea84b160d039 Mon Sep 17 00:00:00 2001 From: Matthew Rice Date: Wed, 10 Aug 2022 10:42:56 -0600 Subject: [PATCH 2/3] Fixes log formatting --- .../kotlin/com/jhomlala/better_player/BetterPlayer.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt index 074ac4d00..795b4953b 100644 --- a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt +++ b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt @@ -144,10 +144,7 @@ internal class BetterPlayer( } } if (Util.SDK_INT < 18) { - Log.e( - TAG, - "Protected content not supported on API levels below 18" - ) + Log.e(TAG, "Protected content not supported on API levels below 18") drmSessionManager = null } else { val drmSchemeUuid = Util.getDrmUuid("widevine") @@ -175,10 +172,7 @@ internal class BetterPlayer( } } else if (clearKey != null && clearKey.isNotEmpty()) { drmSessionManager = if (Util.SDK_INT < 18) { - Log.e( - TAG, - "Protected content not supported on API levels below 18" - ) + Log.e(TAG, "Protected content not supported on API levels below 18") null } else { DefaultDrmSessionManager.Builder() From e45f447b6d3ae6a3404ddfd9663f8aeae864ddec Mon Sep 17 00:00:00 2001 From: Matthew Rice Date: Thu, 11 Aug 2022 09:26:51 -0600 Subject: [PATCH 3/3] Fixes formatting for entire commit. --- .../jhomlala/better_player/BetterPlayer.kt | 356 +++++++----------- 1 file changed, 136 insertions(+), 220 deletions(-) diff --git a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt index 795b4953b..0e22b1e23 100644 --- a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt +++ b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt @@ -74,8 +74,7 @@ internal class BetterPlayer( ) { private val exoPlayer: ExoPlayer? private val eventSink = QueuingEventSink() - private val trackSelector: DefaultTrackSelector = - DefaultTrackSelector(context) + private val trackSelector: DefaultTrackSelector = DefaultTrackSelector(context) private val loadControl: LoadControl private var isInitialized = false private var surface: Surface? = null @@ -134,10 +133,7 @@ internal class BetterPlayer( val userAgent = getUserAgent(headers) if (licenseUrl != null && licenseUrl.isNotEmpty()) { val httpMediaDrmCallback = - HttpMediaDrmCallback( - licenseUrl, - DefaultHttpDataSource.Factory() - ) + HttpMediaDrmCallback(licenseUrl, DefaultHttpDataSource.Factory()) if (drmHeaders != null) { for ((drmKey, drmValue) in drmHeaders) { httpMediaDrmCallback.setKeyRequestProperty(drmKey, drmValue) @@ -154,13 +150,9 @@ internal class BetterPlayer( drmSchemeUuid ) { uuid: UUID? -> try { - val mediaDrm = - FrameworkMediaDrm.newInstance(uuid!!) + val mediaDrm = FrameworkMediaDrm.newInstance(uuid!!) // Force L3. - mediaDrm.setPropertyString( - "securityLevel", - "L3" - ) + mediaDrm.setPropertyString("securityLevel", "L3") return@setUuidAndExoMediaDrmProvider mediaDrm } catch (e: UnsupportedDrmException) { return@setUuidAndExoMediaDrmProvider DummyExoMediaDrm() @@ -197,16 +189,9 @@ internal class BetterPlayer( } else { dataSourceFactory = DefaultDataSource.Factory(context) } - val mediaSource = buildMediaSource( - uri, - dataSourceFactory, - formatHint, - cacheKey, - context - ) + val mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, cacheKey, context) if (overriddenDuration != 0L) { - val clippingMediaSource = - ClippingMediaSource(mediaSource, 0, overriddenDuration * 1000) + val clippingMediaSource = ClippingMediaSource(mediaSource, 0, overriddenDuration * 1000) exoPlayer?.setMediaSource(clippingMediaSource) } else { exoPlayer?.setMediaSource(mediaSource) @@ -220,92 +205,86 @@ internal class BetterPlayer( imageUrl: String?, notificationChannelName: String?, activityName: String ) { - val mediaDescriptionAdapter: MediaDescriptionAdapter = - object : MediaDescriptionAdapter { - override fun getCurrentContentTitle(player: Player): String { - return title - } + val mediaDescriptionAdapter: MediaDescriptionAdapter = object : MediaDescriptionAdapter { + override fun getCurrentContentTitle(player: Player): String { + return title + } - @SuppressLint("UnspecifiedImmutableFlag") - override fun createCurrentContentIntent(player: Player): PendingIntent? { - val packageName = context.applicationContext.packageName - val notificationIntent = Intent() - notificationIntent.setClassName( - packageName, - "$packageName.$activityName" - ) - notificationIntent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TOP - or Intent.FLAG_ACTIVITY_SINGLE_TOP) - return PendingIntent.getActivity( - context, 0, - notificationIntent, - PendingIntent.FLAG_IMMUTABLE - ) - } + @SuppressLint("UnspecifiedImmutableFlag") + override fun createCurrentContentIntent(player: Player): PendingIntent? { + val packageName = context.applicationContext.packageName + val notificationIntent = Intent() + notificationIntent.setClassName( + packageName, + "$packageName.$activityName" + ) + notificationIntent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TOP + or Intent.FLAG_ACTIVITY_SINGLE_TOP) + return PendingIntent.getActivity( + context, 0, + notificationIntent, + PendingIntent.FLAG_IMMUTABLE + ) + } - override fun getCurrentContentText(player: Player): String? { - return author - } + override fun getCurrentContentText(player: Player): String? { + return author + } - override fun getCurrentLargeIcon( - player: Player, - callback: BitmapCallback - ): Bitmap? { - if (imageUrl == null) { - return null - } - if (bitmap != null) { - return bitmap - } - val imageWorkRequest = - OneTimeWorkRequest.Builder(ImageWorker::class.java) - .addTag(imageUrl) - .setInputData( - Data.Builder() - .putString( - BetterPlayerPlugin.URL_PARAMETER, - imageUrl - ) - .build() - ) + override fun getCurrentLargeIcon( + player: Player, + callback: BitmapCallback + ): Bitmap? { + if (imageUrl == null) { + return null + } + if (bitmap != null) { + return bitmap + } + val imageWorkRequest = OneTimeWorkRequest.Builder(ImageWorker::class.java) + .addTag(imageUrl) + .setInputData( + Data.Builder() + .putString(BetterPlayerPlugin.URL_PARAMETER, imageUrl) .build() - workManager.enqueue(imageWorkRequest) - val workInfoObserver = Observer { workInfo: WorkInfo? -> - try { - if (workInfo != null) { - val state = workInfo.state - if (state == WorkInfo.State.SUCCEEDED) { - val outputData = workInfo.outputData - val filePath = - outputData.getString(BetterPlayerPlugin.FILE_PATH_PARAMETER) - //Bitmap here is already processed and it's very small, so it won't - //break anything. - bitmap = BitmapFactory.decodeFile(filePath) - bitmap?.let { bitmap -> - callback.onBitmap(bitmap) - } + ) + .build() + workManager.enqueue(imageWorkRequest) + val workInfoObserver = Observer { workInfo: WorkInfo? -> + try { + if (workInfo != null) { + val state = workInfo.state + if (state == WorkInfo.State.SUCCEEDED) { + val outputData = workInfo.outputData + val filePath = + outputData.getString(BetterPlayerPlugin.FILE_PATH_PARAMETER) + //Bitmap here is already processed and it's very small, so it won't + //break anything. + bitmap = BitmapFactory.decodeFile(filePath) + bitmap?.let { bitmap -> + callback.onBitmap(bitmap) } - if (state == WorkInfo.State.SUCCEEDED || state == WorkInfo.State.CANCELLED || state == WorkInfo.State.FAILED) { - val uuid = imageWorkRequest.id - val observer = - workerObserverMap.remove(uuid) - if (observer != null) { - workManager.getWorkInfoByIdLiveData(uuid) - .removeObserver(observer) - } + } + if (state == WorkInfo.State.SUCCEEDED || state == WorkInfo.State.CANCELLED || state == WorkInfo.State.FAILED) { + val uuid = imageWorkRequest.id + val observer = workerObserverMap.remove(uuid) + if (observer != null) { + workManager.getWorkInfoByIdLiveData(uuid) + .removeObserver(observer) } } - } catch (exception: Exception) { - Log.e(TAG, "Image select error: $exception") } + } catch (exception: Exception) { + Log.e(TAG, "Image select error: $exception") } - val workerUuid = imageWorkRequest.id - workManager.getWorkInfoByIdLiveData(workerUuid) - .observeForever(workInfoObserver) - workerObserverMap[workerUuid] = workInfoObserver - return null } + val workerUuid = imageWorkRequest.id + workManager.getWorkInfoByIdLiveData(workerUuid) + .observeForever(workInfoObserver) + workerObserverMap[workerUuid] = workInfoObserver + return null } + } var playerNotificationChannelName = notificationChannelName if (notificationChannelName == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -345,26 +324,17 @@ internal class BetterPlayer( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { refreshHandler = Handler(Looper.getMainLooper()) refreshRunnable = Runnable { - val playbackState: PlaybackStateCompat = - if (exoPlayer?.isPlaying == true) { - PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SEEK_TO) - .setState( - PlaybackStateCompat.STATE_PLAYING, - position, - 1.0f - ) - .build() - } else { - PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SEEK_TO) - .setState( - PlaybackStateCompat.STATE_PAUSED, - position, - 1.0f - ) - .build() - } + val playbackState: PlaybackStateCompat = if (exoPlayer?.isPlaying == true) { + PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SEEK_TO) + .setState(PlaybackStateCompat.STATE_PLAYING, position, 1.0f) + .build() + } else { + PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SEEK_TO) + .setState(PlaybackStateCompat.STATE_PAUSED, position, 1.0f) + .build() + } mediaSession?.setPlaybackState(playbackState) refreshHandler?.postDelayed(refreshRunnable!!, 1000) } @@ -374,10 +344,7 @@ internal class BetterPlayer( override fun onPlaybackStateChanged(playbackState: Int) { mediaSession?.setMetadata( MediaMetadataCompat.Builder() - .putLong( - MediaMetadataCompat.METADATA_KEY_DURATION, - getDuration() - ) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, getDuration()) .build() ) } @@ -430,40 +397,46 @@ internal class BetterPlayer( val mediaItem = mediaItemBuilder.build() var drmSessionManagerProvider: DrmSessionManagerProvider? = null drmSessionManager?.let { drmSessionManager -> - drmSessionManagerProvider = - DrmSessionManagerProvider { drmSessionManager } + drmSessionManagerProvider = DrmSessionManagerProvider { drmSessionManager } } return when (type) { C.CONTENT_TYPE_SS -> SsMediaSource.Factory( DefaultSsChunkSource.Factory(mediaDataSourceFactory), DefaultDataSource.Factory(context, mediaDataSourceFactory) - ).apply { - if (drmSessionManagerProvider != null) { - setDrmSessionManagerProvider(drmSessionManagerProvider!!) + ) + .apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } } - }.createMediaSource(mediaItem) + .createMediaSource(mediaItem) C.CONTENT_TYPE_DASH -> DashMediaSource.Factory( DefaultDashChunkSource.Factory(mediaDataSourceFactory), DefaultDataSource.Factory(context, mediaDataSourceFactory) - ).apply { - if (drmSessionManagerProvider != null) { - setDrmSessionManagerProvider(drmSessionManagerProvider!!) + ) + .apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } } - }.createMediaSource(mediaItem) + .createMediaSource(mediaItem) C.CONTENT_TYPE_HLS -> HlsMediaSource.Factory(mediaDataSourceFactory) .apply { if (drmSessionManagerProvider != null) { setDrmSessionManagerProvider(drmSessionManagerProvider!!) } - }.createMediaSource(mediaItem) + } + .createMediaSource(mediaItem) C.CONTENT_TYPE_OTHER -> ProgressiveMediaSource.Factory( mediaDataSourceFactory, DefaultExtractorsFactory() - ).apply { - if (drmSessionManagerProvider != null) { - setDrmSessionManagerProvider(drmSessionManagerProvider!!) + ) + .apply { + if (drmSessionManagerProvider != null) { + setDrmSessionManagerProvider(drmSessionManagerProvider!!) + } } - }.createMediaSource(mediaItem) + .createMediaSource(mediaItem) else -> { throw IllegalStateException("Unsupported type: $type") } @@ -471,9 +444,7 @@ internal class BetterPlayer( } private fun setupVideoPlayer( - eventChannel: EventChannel, - textureEntry: SurfaceTextureEntry, - result: MethodChannel.Result + eventChannel: EventChannel, textureEntry: SurfaceTextureEntry, result: MethodChannel.Result ) { eventChannel.setStreamHandler( object : EventChannel.StreamHandler { @@ -519,11 +490,7 @@ internal class BetterPlayer( } override fun onPlayerError(error: PlaybackException) { - eventSink.error( - "VideoError", - "Video player had error $error", - "" - ) + eventSink.error("VideoError", "Video player had error $error", "") } }) val reply: MutableMap = HashMap() @@ -545,21 +512,16 @@ internal class BetterPlayer( } @Suppress("DEPRECATION") - private fun setAudioAttributes( - exoPlayer: ExoPlayer?, - mixWithOthers: Boolean - ) { + private fun setAudioAttributes(exoPlayer: ExoPlayer?, mixWithOthers: Boolean) { val audioComponent = exoPlayer?.audioComponent ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { audioComponent.setAudioAttributes( - AudioAttributes.Builder() - .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(), + AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(), !mixWithOthers ) } else { audioComponent.setAudioAttributes( - AudioAttributes.Builder() - .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC).build(), + AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MUSIC).build(), !mixWithOthers ) } @@ -574,8 +536,7 @@ internal class BetterPlayer( } fun setLooping(value: Boolean) { - exoPlayer?.repeatMode = - if (value) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF + exoPlayer?.repeatMode = if (value) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF } fun setVolume(value: Double) { @@ -618,10 +579,7 @@ internal class BetterPlayer( timeline?.let { if (!timeline.isEmpty) { val windowStartTimeMs = - timeline.getWindow( - 0, - Timeline.Window() - ).windowStartTimeMs + timeline.getWindow(0, Timeline.Window()).windowStartTimeMs val pos = exoPlayer?.currentPosition ?: 0L return windowStartTimeMs + pos } @@ -671,8 +629,7 @@ internal class BetterPlayer( 0, mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE ) - val mediaSession = - MediaSessionCompat(context, TAG, null, pendingIntent) + val mediaSession = MediaSessionCompat(context, TAG, null, pendingIntent) mediaSession.setCallback(object : MediaSessionCompat.Callback() { override fun onSeekTo(pos: Long) { sendSeekToEvent(pos) @@ -710,8 +667,7 @@ internal class BetterPlayer( if (mappedTrackInfo.getRendererType(rendererIndex) != C.TRACK_TYPE_AUDIO) { continue } - val trackGroupArray = - mappedTrackInfo.getTrackGroups(rendererIndex) + val trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex) var hasElementWithoutLabel = false var hasStrangeAudioTrack = false for (groupIndex in 0 until trackGroupArray.length) { @@ -731,30 +687,18 @@ internal class BetterPlayer( for (groupElementIndex in 0 until group.length) { val label = group.getFormat(groupElementIndex).label if (name == label && index == groupIndex) { - setAudioTrack( - rendererIndex, - groupIndex, - groupElementIndex - ) + setAudioTrack(rendererIndex, groupIndex, groupElementIndex) return } ///Fallback option if (!hasStrangeAudioTrack && hasElementWithoutLabel && index == groupIndex) { - setAudioTrack( - rendererIndex, - groupIndex, - groupElementIndex - ) + setAudioTrack(rendererIndex, groupIndex, groupElementIndex) return } ///Fallback option if (hasStrangeAudioTrack && name == label) { - setAudioTrack( - rendererIndex, - groupIndex, - groupElementIndex - ) + setAudioTrack(rendererIndex, groupIndex, groupElementIndex) return } } @@ -766,19 +710,14 @@ internal class BetterPlayer( } } - private fun setAudioTrack( - rendererIndex: Int, - groupIndex: Int, - groupElementIndex: Int - ) { + private fun setAudioTrack(rendererIndex: Int, groupIndex: Int, groupElementIndex: Int) { val mappedTrackInfo = trackSelector.currentMappedTrackInfo if (mappedTrackInfo != null) { - val builder = trackSelector.parameters.buildUpon() .setRendererDisabled(rendererIndex, false) - .addOverride(TrackSelectionOverride(mappedTrackInfo.getTrackGroups( - rendererIndex - ).get(groupIndex), rendererIndex)) + .addOverride(TrackSelectionOverride(mappedTrackInfo.getTrackGroups(rendererIndex) + .get(groupIndex), rendererIndex) + ) .build() trackSelector.parameters = builder @@ -829,8 +768,7 @@ internal class BetterPlayer( private const val FORMAT_DASH = "dash" private const val FORMAT_HLS = "hls" private const val FORMAT_OTHER = "other" - private const val DEFAULT_NOTIFICATION_CHANNEL = - "BETTER_PLAYER_NOTIFICATION" + private const val DEFAULT_NOTIFICATION_CHANNEL = "BETTER_PLAYER_NOTIFICATION" private const val NOTIFICATION_ID = 20772077 //Clear cache without accessing BetterPlayerCache. @@ -863,34 +801,17 @@ internal class BetterPlayer( //Start pre cache of video. Invoke work manager job and start caching in background. fun preCache( - context: Context?, - dataSource: String?, - preCacheSize: Long, - maxCacheSize: Long, - maxCacheFileSize: Long, - headers: Map, - cacheKey: String?, - result: MethodChannel.Result + context: Context?, dataSource: String?, preCacheSize: Long, + maxCacheSize: Long, maxCacheFileSize: Long, headers: Map, + cacheKey: String?, result: MethodChannel.Result ) { val dataBuilder = Data.Builder() .putString(BetterPlayerPlugin.URL_PARAMETER, dataSource) - .putLong( - BetterPlayerPlugin.PRE_CACHE_SIZE_PARAMETER, - preCacheSize - ) - .putLong( - BetterPlayerPlugin.MAX_CACHE_SIZE_PARAMETER, - maxCacheSize - ) - .putLong( - BetterPlayerPlugin.MAX_CACHE_FILE_SIZE_PARAMETER, - maxCacheFileSize - ) + .putLong(BetterPlayerPlugin.PRE_CACHE_SIZE_PARAMETER, preCacheSize) + .putLong(BetterPlayerPlugin.MAX_CACHE_SIZE_PARAMETER, maxCacheSize) + .putLong(BetterPlayerPlugin.MAX_CACHE_FILE_SIZE_PARAMETER, maxCacheFileSize) if (cacheKey != null) { - dataBuilder.putString( - BetterPlayerPlugin.CACHE_KEY_PARAMETER, - cacheKey - ) + dataBuilder.putString(BetterPlayerPlugin.CACHE_KEY_PARAMETER, cacheKey) } for (headerKey in headers.keys) { dataBuilder.putString( @@ -899,10 +820,9 @@ internal class BetterPlayer( ) } if (dataSource != null && context != null) { - val cacheWorkRequest = - OneTimeWorkRequest.Builder(CacheWorker::class.java) - .addTag(dataSource) - .setInputData(dataBuilder.build()).build() + val cacheWorkRequest = OneTimeWorkRequest.Builder(CacheWorker::class.java) + .addTag(dataSource) + .setInputData(dataBuilder.build()).build() WorkManager.getInstance(context).enqueue(cacheWorkRequest) } result.success(null) @@ -910,11 +830,7 @@ internal class BetterPlayer( //Stop pre cache of video with given url. If there's no work manager job for given url, then //it will be ignored. - fun stopPreCache( - context: Context?, - url: String?, - result: MethodChannel.Result - ) { + fun stopPreCache(context: Context?, url: String?, result: MethodChannel.Result) { if (url != null && context != null) { WorkManager.getInstance(context).cancelAllWorkByTag(url) }