[vlc-devel] [PATCH] Added functionality for Automatic Frame Rate switching for HDMI screens. When a video starts playing, VLC will determine the best frame rate to apply on the media, in consideration of the frame rates supported by the display. For example, 24p videos will play at 24p if supported by the display

Clement Osumo clementosumo at gmail.com
Sat Jun 4 11:46:11 UTC 2022


---
 .../vlc/gui/video/VideoPlayerActivity.kt      | 94 +++++++++++++++----
 1 file changed, 76 insertions(+), 18 deletions(-)

diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt
index 0dc0f3a1a..3179b88f4 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt
@@ -444,8 +444,8 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
         try {
             requestedOrientation = getScreenOrientation(orientationMode)
             //as there is no ActivityInfo.SCREEN_ORIENTATION_SENSOR_REVERSE_LANDSCAPE, now that we are in reverse landscape, enable the sensor if needed
-            if (screenOrientationSetting == 103 && sensor){
-                orientationMode = PlayerOrientationMode(true,ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
+            if (screenOrientationSetting == 103 && sensor) {
+                orientationMode = PlayerOrientationMode(true, ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
                 requestedOrientation = getScreenOrientation(orientationMode)
             }
 
@@ -461,7 +461,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
             // Orientation
             // Tips
             if (!BuildConfig.DEBUG && !isTv && !settings.getBoolean(PREF_TIPS_SHOWN, false)
-                && !isBenchmark
+                    && !isBenchmark
             ) {
                 tipsDelegate.init()
             }
@@ -1347,7 +1347,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
                                     service.spuTracks?.iterator()?.let { spuTracks ->
                                         while (spuTracks.hasNext()) {
                                             val next = spuTracks.next()
-                                            val realTrack = allTracks.find {it.id == next.id }
+                                            val realTrack = allTracks.find { it.id == next.id }
                                             if (LocaleUtil.getLocaleFromVLC(realTrack?.language
                                                             ?: "") == preferredSpuLang) {
                                                 preferredTrack = next.id
@@ -1417,10 +1417,10 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
      *
      * @return a list of [IMedia.Track]
      */
-    private fun getCurrentMediaTracks():List<IMedia.Track> {
+    private fun getCurrentMediaTracks(): List<IMedia.Track> {
 
         service?.let { service ->
-            val allTracks= ArrayList<IMedia.Track>()
+            val allTracks = ArrayList<IMedia.Track>()
             service.mediaplayer.media?.let { media ->
                 if (currentTracks?.first == media.uri.toString()) return currentTracks!!.second
                 for (i in 0..media.trackCount) {
@@ -1463,6 +1463,64 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
             }
         }
         if (tipsDelegate.currentTip != null) pause()
+
+        //automatic framerate switching for HDMI
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            val supportedModes: ArrayList<out Display.Mode>? = display?.getSupportedModes()?.toCollection(ArrayList())
+            val currentMode = display?.mode;
+            System.out.println("24p")
+
+            //remove modes which don't match the current resolution, cause we want resolution to remain the same
+            val modeIterator = supportedModes?.iterator();
+            while (modeIterator!!.hasNext()) {
+                val mode: Display.Mode = modeIterator.next();
+                if ((mode.physicalHeight != currentMode?.physicalHeight) || (mode.physicalWidth != currentMode?.physicalWidth)) {
+                    modeIterator.remove();
+                }
+            }
+
+            //run if display supports more than one mode
+            if ((supportedModes != null) && (supportedModes.size > 1)) {
+                var videoTrack = service?.mediaplayer?.currentVideoTrack
+                if (videoTrack != null) {
+                    var videoframerate =
+                            videoTrack.frameRateNum / videoTrack.frameRateDen.toDouble()
+                    System.out.println("Video is " + videoframerate.toString() + " fps")
+
+                    var index: Int = 0
+                    var diff: Double = 0.0
+                    val diffArray: ArrayList<Double>? = ArrayList()
+
+                    for (mode in supportedModes!!) {
+                        System.out.println(mode.toString())
+                        if (mode.refreshRate > videoframerate) {
+                            diff = mode.refreshRate - videoframerate
+                        } else {
+                            diff = videoframerate - mode.refreshRate
+                        }
+                        diffArray?.add(diff)
+                        index = index++
+                    }
+
+                    var frameratetouse = supportedModes[diffArray?.indexOf(diffArray?.min())!!]
+                    System.out.println("We will use " + frameratetouse.refreshRate + " fps")
+
+                    val windowattributes = window.attributes
+                    windowattributes.preferredDisplayModeId = frameratetouse.modeId
+
+                    window.attributes = windowattributes // set framerate
+
+                    //switching HDMI mode causes playback to pause
+                    //wait 0.5 seconds and resume play
+                    //sounds hacky, maybe there's a better solution
+                    Handler(Looper.getMainLooper()).postDelayed({
+                        //Do something after 2000ms
+                        play();
+                    }, 500)
+                    //rootView?.updateLayoutParams<ConstraintLayout.LayoutParams> {  ConstraintLayout.LayoutParams.preferredDisplayModeId = frameratetouse.modeId }
+                }
+            }
+        }
     }
 
     private fun encounteredError() {
@@ -1640,7 +1698,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
         service?.saveMediaMeta()
         service?.playlistManager?.getMedia(position)
         service?.playlistManager?.getMediaList()?.let {
-            if (it[position] == item)  service?.playIndex(position)
+            if (it[position] == item) service?.playIndex(position)
             else {
                 for ((index, media) in it.withIndex()) if (item == media) {
                     service?.playIndex(index)
@@ -1745,11 +1803,11 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
         service?.let { seek(position, it.length, fromUser) }
     }
 
-    fun seek(position: Long, fromUser: Boolean = false, fast:Boolean = false) {
+    fun seek(position: Long, fromUser: Boolean = false, fast: Boolean = false) {
         service?.let { seek(position, it.length, fromUser, fast) }
     }
 
-    internal fun seek(position: Long, length: Long, fromUser: Boolean = false, fast:Boolean = false) {
+    internal fun seek(position: Long, length: Long, fromUser: Boolean = false, fast: Boolean = false) {
         service?.let { service ->
             forcedTime = position
             lastTime = service.getTime()
@@ -1810,7 +1868,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
      */
     @SuppressLint("SdCardPath")
     @TargetApi(12)
-    protected open fun loadMedia(fromStart: Boolean = false, forceUsingNew:Boolean = false) {
+    protected open fun loadMedia(fromStart: Boolean = false, forceUsingNew: Boolean = false) {
         if (fromStart) {
             askResume = false
             intent.putExtra(PLAY_EXTRA_FROM_START, fromStart)
@@ -1903,12 +1961,12 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
                         lastSpuTrack = media.spuTrack
                     } else if (!fromStart) {
                         // not in media library
-                            val rTime = settings.getLong(VIDEO_RESUME_TIME, -1L)
-                            val lastUri = settings.getString(VIDEO_RESUME_URI, "")
-                            if (rTime > 0 && service.currentMediaLocation == lastUri) {
-                                    settings.putSingle(VIDEO_RESUME_TIME, -1L)
-                                    startTime = rTime
-                            }
+                        val rTime = settings.getLong(VIDEO_RESUME_TIME, -1L)
+                        val lastUri = settings.getString(VIDEO_RESUME_URI, "")
+                        if (rTime > 0 && service.currentMediaLocation == lastUri) {
+                            settings.putSingle(VIDEO_RESUME_TIME, -1L)
+                            startTime = rTime
+                        }
                     }
                 }
 
@@ -2180,7 +2238,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback,
     /**
      * Callback called when a Control setting has been changed in the advanced options
      */
-    fun onChangedControlSetting(key: String) = when(key) {
+    fun onChangedControlSetting(key: String) = when (key) {
         AUDIO_BOOST -> isAudioBoostEnabled = settings.getBoolean(AUDIO_BOOST, true)
         ENABLE_VOLUME_GESTURE, ENABLE_BRIGHTNESS_GESTURE, ENABLE_DOUBLE_TAP_SEEK, ENABLE_DOUBLE_TAP_PLAY, ENABLE_SWIPE_SEEK -> touchDelegate.touchControls = generateTouchFlags()
         ENABLE_SEEK_BUTTONS -> overlayDelegate.seekButtons = settings.getBoolean(ENABLE_SEEK_BUTTONS, false)
@@ -2297,5 +2355,5 @@ fun setConstraintPercent(view: Guideline, percent: Float) {
 
 @BindingAdapter("mediamax")
 fun setProgressMax(view: SeekBar, length: Long) {
-    view.max =  if (length == 0L) NO_LENGTH_PROGRESS_MAX else length.toInt()
+    view.max = if (length == 0L) NO_LENGTH_PROGRESS_MAX else length.toInt()
 }
\ No newline at end of file
-- 
2.36.1



More information about the vlc-devel mailing list