@@ -10,6 +10,7 @@ import android.media.MediaCodecList
1010import android.media.MediaFormat
1111import android.os.Build
1212import android.os.Build.VERSION.SDK_INT
13+ import android.util.Log
1314import android.util.Range
1415import androidx.annotation.RequiresApi
1516import androidx.annotation.StringRes
@@ -46,6 +47,14 @@ private const val AC3_MAX_SAMPLE_RATE = 48000
4647private const val GOOGLE_RAW_DECODER = " OMX.google.raw.decoder"
4748private const val MEDIATEK_RAW_DECODER = " OMX.MTK.AUDIO.DECODER.RAW"
4849
50+ private val incorrectMpeg4ResolutionModelList = listOf (
51+ " Nokia 1" ,
52+ " moto c" ,
53+ " infinix x650" ,
54+ " LG-X230" ,
55+ " twist 2 pro"
56+ )
57+
4958private val platformSupportedTypes = arrayOf(
5059 " audio/3gpp" ,
5160 " audio/amr-mb" ,
@@ -98,6 +107,7 @@ private var mediaCodecInfos: Array<MediaCodecInfo> = emptyArray()
98107val audioCodecList: MutableList <CodecSimpleInfo > = mutableListOf ()
99108val videoCodecList: MutableList <CodecSimpleInfo > = mutableListOf ()
100109
110+ val detailedCodecInfos: MutableMap <String , List <DetailsProperty >> = mutableMapOf ()
101111
102112fun getSimpleCodecInfoList (context : Context , isAudio : Boolean ): MutableList <CodecSimpleInfo > {
103113 if (isAudio && audioCodecList.isNotEmpty()) {
@@ -209,7 +219,19 @@ fun getSimpleCodecInfoList(context: Context, isAudio: Boolean): MutableList<Code
209219 return codecSimpleInfoList
210220}
211221
222+ fun isDetailedCodecInfoCached (codecId : String , codecName : String ): Boolean {
223+ val combinedCodecName = " $codecId /$codecName "
224+ return detailedCodecInfos[combinedCodecName] != null
225+ }
226+
212227fun getDetailedCodecInfo (context : Context , codecId : String , codecName : String ): List <DetailsProperty > {
228+ val combinedCodecName = " $codecId /$codecName "
229+ if (detailedCodecInfos[combinedCodecName] != null ) {
230+ return detailedCodecInfos[combinedCodecName]!! .also {
231+ saveToLogcat(context, codecId, codecName, it)
232+ }
233+ }
234+
213235 val mediaCodecInfo = mediaCodecInfos.find { it.name == codecName } ? : return emptyList()
214236
215237 // Google uses the same decoder for both DP and non-DP content for MPEG-4,
@@ -335,7 +357,11 @@ fun getDetailedCodecInfo(context: Context, codecId: String, codecName: String):
335357 propertyList.add(DetailsProperty (propertyList.size.toLong(), profileString, it))
336358 }
337359
338- return propertyList
360+ detailedCodecInfos[combinedCodecName] = propertyList
361+
362+ return propertyList.also {
363+ saveToLogcat(context, codecId, codecName, it)
364+ }
339365}
340366
341367@RequiresApi(30 )
@@ -698,12 +724,17 @@ private fun getMaxResolution(codecId: String, videoCapabilities: MediaCodecInfo.
698724 // capabilities. The supported height reported for H265@3840x2160 is 2144,
699725 // and H264@1920x1080 is 1072.
700726 // Cross reference with CamcorderProfile to ensure a resolution is supported.
727+ // Several other devices also indicate a wrong resolution (174x174) for MPEG-4 SP.
728+ // On Huawei Mate 9, HEVC reports only H265@3840x2112 instead of full 4K.
701729 if (maxHeight == 1072 && CamcorderProfile .hasProfile(CamcorderProfile .QUALITY_1080P )) {
702730 defaultResolution[0 ] = 1920
703731 defaultResolution[1 ] = 1080
704- } else if (maxHeight == 2144 && CamcorderProfile .hasProfile(CamcorderProfile .QUALITY_2160P )) {
732+ } else if (( maxHeight == 2144 || (maxHeight == 2112 && Build . MODEL == " mha-l29 " )) && CamcorderProfile .hasProfile(CamcorderProfile .QUALITY_2160P )) {
705733 defaultResolution[0 ] = 3840
706734 defaultResolution[1 ] = 2160
735+ } else if (needsMaxResolutionFixForMPEG4(codecId)) {
736+ defaultResolution[0 ] = 1280
737+ defaultResolution[1 ] = 720
707738 }
708739
709740 return if (! areCapabilitiesUnknown(videoCapabilities)) {
@@ -760,20 +791,35 @@ private fun getFrameRatePerResolutions(context: Context, codecId: String,
760791 return @forEachIndexed
761792 }
762793
763- if (videoCapabilities.isSizeSupported(resolution[0 ], resolution[1 ])) {
764- maxFrameRate = getSupportedFrameRatesFor(codecId, videoCapabilities, resolution[0 ], resolution[1 ]).upper
794+ try {
795+ if (isSizeSupported(videoCapabilities, resolution[0 ], resolution[1 ])) {
796+ maxFrameRate = getSupportedFrameRatesFor(codecId, videoCapabilities, resolution[0 ], resolution[1 ]).upper
765797
766- if (option == 0 ) {
767- capabilities.append(" ${framerateClasses[index]} : ${" %.1f" .format(maxFrameRate)} $fpsString \n " )
768- } else {
769- capabilities.append(" ${resolution[0 ]} x${resolution[1 ]} : ${" %.1f" .format(maxFrameRate)} $fpsString \n " )
798+ if (option == 0 ) {
799+ capabilities.append(" ${framerateClasses[index]} : ${" %.1f" .format(maxFrameRate)} $fpsString \n " )
800+ } else {
801+ capabilities.append(" ${resolution[0 ]} x${resolution[1 ]} : ${" %.1f" .format(maxFrameRate)} $fpsString \n " )
802+ }
770803 }
771- }
804+ } catch (_ : Throwable ) {}
772805 }
773806
774807 return capabilities.toString().dropLast(1 ) // Remove the last \n
775808}
776809
810+ private fun isSizeSupported (videoCapabilities : MediaCodecInfo .VideoCapabilities , width : Int , height : Int ): Boolean {
811+ if (videoCapabilities.isSizeSupported(width, height)) return true
812+
813+ if (width == 1920 && height == 1080 ) {
814+ return CamcorderProfile .hasProfile(CamcorderProfile .QUALITY_1080P )
815+ }
816+ if (width == 3840 && height == 2160 ) {
817+ return CamcorderProfile .hasProfile(CamcorderProfile .QUALITY_2160P )
818+ }
819+
820+ return false
821+ }
822+
777823@SuppressLint(" NewApi" )
778824private fun getProfileLevels (context : Context , codecId : String , codecName : String ,
779825 capabilities : MediaCodecInfo .CodecCapabilities ): String? {
@@ -1064,4 +1110,15 @@ private fun needsHevc10BitProfileExcluded(codecId: String, profile: Int): Boolea
10641110 // See https://github.com/google/ExoPlayer/issues/3537 for more info.
10651111 return " video/hevc" == codecId && HEVCProfiles .HEVCProfileMain10 .value == profile
10661112 && (" sailfish" == Build .DEVICE || " marlin" == Build .DEVICE )
1113+ }
1114+
1115+ private fun needsMaxResolutionFixForMPEG4 (codecId : String ) = " video/mp4v-es" == codecId && Build .MODEL in incorrectMpeg4ResolutionModelList
1116+
1117+ private fun saveToLogcat (context : Context , codecId : String , codecName : String , detailsList : List <DetailsProperty >) {
1118+ val prefs = PreferenceManager .getDefaultSharedPreferences(context)
1119+ val saveDetailsToLogcat = prefs.getBoolean(" save_details_to_logcat" , false )
1120+ if (saveDetailsToLogcat) {
1121+ Log .i(" CodecUtils" , " Codec MIME type: $codecId , codec name: $codecName " )
1122+ Log .i(" CodecUtils" , detailsList.joinToString(" \n " ))
1123+ }
10671124}
0 commit comments