Skip to content

Commit

Permalink
exoplayer code shrinking, baseline profile
Browse files Browse the repository at this point in the history
  • Loading branch information
nift4 committed May 26, 2024
1 parent a8faff3 commit 8e21eca
Show file tree
Hide file tree
Showing 30 changed files with 32,294 additions and 18 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ val aboutLibsVersion = "11.1.4" // keep in sync with plugin version

plugins {
id("com.android.application")
id("androidx.baselineprofile")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.plugin.parcelize")
Expand Down Expand Up @@ -74,6 +75,10 @@ android {
lintConfig = file("lint.xml")
}

baselineProfile {
dexLayoutOptimization = true
}

defaultConfig {
applicationId = "org.akanework.gramophone"
// Reasons to not support KK include me.zhanghai.android.fastscroll, WindowInsets for
Expand All @@ -98,6 +103,9 @@ android {
"\"$releaseType\""
)
setProperty("archivesBaseName", "Gramophone-$versionName${versionNameSuffix ?: ""}")
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
Expand Down Expand Up @@ -198,6 +206,8 @@ dependencies {
//noinspection GradleDependency newer versions need java.nio which is api 26+
//implementation("com.github.albfernandez:juniversalchardet:2.0.3") TODO
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") // TODO kill it once alpha2 is out
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
"baselineProfile"(project(":baselineprofile"))
// --- below does not apply to release builds ---
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
// Note: JAudioTagger is not compatible with Android 5, we can't ship it in app
Expand Down
3 changes: 3 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

-dontobfuscate

# Enable more agressive optimizations changing access of classes and methods
-allowaccessmodification

# reflection by androidx via theme attr viewInflaterClass
-keep class org.akanework.gramophone.logic.ui.ViewCompatInflater { *; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ import kotlinx.coroutines.sync.Semaphore
import org.akanework.gramophone.BuildConfig
import org.akanework.gramophone.R
import org.akanework.gramophone.logic.utils.CircularShuffleOrder
import org.akanework.gramophone.logic.utils.EndedWorkaroundPlayer
import org.akanework.gramophone.logic.utils.LastPlayedManager
import org.akanework.gramophone.logic.utils.LrcUtils.extractAndParseLyrics
import org.akanework.gramophone.logic.utils.LrcUtils.loadAndParseLyricsFile
import org.akanework.gramophone.logic.utils.MediaStoreUtils
import org.akanework.gramophone.logic.utils.exoplayer.EndedWorkaroundPlayer
import org.akanework.gramophone.logic.utils.exoplayer.GramophoneMediaSourceFactory
import org.akanework.gramophone.logic.utils.exoplayer.GramophoneRenderFactory
import org.akanework.gramophone.ui.MainActivity
import kotlin.random.Random

Expand Down Expand Up @@ -233,17 +235,16 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
.build(),
)

// TODO https://developer.android.com/media/media3/exoplayer/shrinking
val player = EndedWorkaroundPlayer(ExoPlayer.Builder(
this,
DefaultRenderersFactory(this)
GramophoneRenderFactory(this)
.setEnableAudioFloatOutput(
prefs.getBooleanStrict("floatoutput", false))
.setEnableDecoderFallback(true)
.setEnableAudioTrackPlaybackParams( // hardware/system-accelerated playback speed
prefs.getBooleanStrict("ps_hardware_acc", true))
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
)
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER),
GramophoneMediaSourceFactory(this))
.setWakeMode(C.WAKE_MODE_LOCAL)
.setSkipSilenceEnabled(prefs.getBooleanStrict("skip_silence", false))
/*.setMediaSourceFactory(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.akanework.gramophone.BuildConfig
import org.akanework.gramophone.logic.use
import org.akanework.gramophone.logic.utils.exoplayer.EndedWorkaroundPlayer
import java.nio.charset.StandardCharsets

@OptIn(UnstableApi::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.akanework.gramophone.logic.utils
package org.akanework.gramophone.logic.utils.exoplayer

import android.util.Log
import androidx.media3.common.ForwardingPlayer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package org.akanework.gramophone.logic.utils.exoplayer

import android.net.Uri
import androidx.annotation.GuardedBy
import androidx.annotation.OptIn
import androidx.media3.common.FileTypes
import androidx.media3.common.util.UnstableApi
import androidx.media3.extractor.Extractor
import androidx.media3.extractor.ExtractorsFactory
import androidx.media3.extractor.flac.FlacExtractor
import androidx.media3.extractor.mkv.MatroskaExtractor
import androidx.media3.extractor.mp3.Mp3Extractor
import androidx.media3.extractor.mp4.Mp4Extractor
import androidx.media3.extractor.ogg.OggExtractor
import androidx.media3.extractor.text.SubtitleParser
import androidx.media3.extractor.ts.Ac3Extractor
import androidx.media3.extractor.ts.Ac4Extractor
import androidx.media3.extractor.ts.AdtsExtractor
import androidx.media3.extractor.wav.WavExtractor
import java.lang.reflect.Constructor
import java.lang.reflect.InvocationTargetException
import java.util.concurrent.atomic.AtomicBoolean

@OptIn(UnstableApi::class)
class GramophoneExtractorsFactory : ExtractorsFactory {
companion object {
private val DEFAULT_EXTRACTOR_ORDER =
intArrayOf(5, 4, 12, 8, 3, 10, 9, 11, 6, 2, 0, 1, 7, 16, 15)
private val FLAC_EXTENSION_LOADER =
ExtensionLoader {
val isFlacNativeLibraryAvailable =
java.lang.Boolean.TRUE == Class.forName("androidx.media3.decoder.flac.FlacLibrary")
.getMethod("isAvailable").invoke(null as Any?)
if (isFlacNativeLibraryAvailable) Class.forName("androidx.media3.decoder.flac.FlacExtractor")
.asSubclass(
Extractor::class.java
).getConstructor(Integer.TYPE) else null
}
private val MIDI_EXTENSION_LOADER =
ExtensionLoader {
Class.forName("androidx.media3.decoder.midi.MidiExtractor").asSubclass(
Extractor::class.java
).getConstructor()
}
}
private var constantBitrateSeekingEnabled = false
private var constantBitrateSeekingAlwaysEnabled = false
private var adtsFlags = 0
private var flacFlags = 0
private var matroskaFlags = 0
private var mp4Flags = 0
//private var fragmentedMp4Flags = 0
private var mp3Flags = 0

@Synchronized
fun setConstantBitrateSeekingEnabled(constantBitrateSeekingEnabled: Boolean): GramophoneExtractorsFactory {
this.constantBitrateSeekingEnabled = constantBitrateSeekingEnabled
return this
}

@Synchronized
fun setConstantBitrateSeekingAlwaysEnabled(constantBitrateSeekingAlwaysEnabled: Boolean): GramophoneExtractorsFactory {
this.constantBitrateSeekingAlwaysEnabled = constantBitrateSeekingAlwaysEnabled
return this
}

@Synchronized
fun setAdtsExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.adtsFlags = flags
return this
}

@Synchronized
fun setFlacExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.flacFlags = flags
return this
}

@Synchronized
fun setMatroskaExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.matroskaFlags = flags
return this
}

@Synchronized
fun setMp4ExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.mp4Flags = flags
return this
}

/*@Synchronized
fun setFragmentedMp4ExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.fragmentedMp4Flags = flags
return this
}*/

@Synchronized
fun setMp3ExtractorFlags(flags: Int): GramophoneExtractorsFactory {
this.mp3Flags = flags
return this
}

@Synchronized
override fun createExtractors(): Array<out Extractor> {
return this.createExtractors(Uri.EMPTY, hashMapOf())
}

@Synchronized
override fun createExtractors(
uri: Uri,
responseHeaders: MutableMap<String, List<String>>
): Array<out Extractor> {
val extractors: MutableList<Extractor> = ArrayList(DEFAULT_EXTRACTOR_ORDER.size)
val responseHeadersInferredFileType =
FileTypes.inferFileTypeFromResponseHeaders(responseHeaders)
if (responseHeadersInferredFileType != -1) {
this.addExtractorsForFileType(responseHeadersInferredFileType, extractors)
}

val uriInferredFileType = FileTypes.inferFileTypeFromUri(uri)
if (uriInferredFileType != -1 && uriInferredFileType != responseHeadersInferredFileType) {
this.addExtractorsForFileType(uriInferredFileType, extractors)
}


for (fileType in DEFAULT_EXTRACTOR_ORDER) {
if (fileType != responseHeadersInferredFileType && fileType != uriInferredFileType) {
this.addExtractorsForFileType(fileType, extractors)
}
}

return extractors.toTypedArray()
}

private fun addExtractorsForFileType(fileType: Int, extractors: MutableList<Extractor>) {
when (fileType) {
0 -> extractors.add(Ac3Extractor())
1 -> extractors.add(Ac4Extractor())
2 -> extractors.add(AdtsExtractor(this.adtsFlags or (if (this.constantBitrateSeekingEnabled) AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING else 0) or (if (this.constantBitrateSeekingAlwaysEnabled) AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS else 0)))
4 -> {
val flacExtractor = FLAC_EXTENSION_LOADER.getExtractor(this.flacFlags)
if (flacExtractor != null) {
extractors.add(flacExtractor)
} else {
extractors.add(FlacExtractor(this.flacFlags))
}
}
6 -> extractors.add(
MatroskaExtractor(
SubtitleParser.Factory.UNSUPPORTED,
this.matroskaFlags or MatroskaExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA
)
)
7 -> extractors.add(Mp3Extractor(this.mp3Flags or (if (this.constantBitrateSeekingEnabled) Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING else 0) or (if (this.constantBitrateSeekingAlwaysEnabled) Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS else 0)))
8 -> {
/*extractors.add(
FragmentedMp4Extractor(
SubtitleParser.Factory.UNSUPPORTED,
this.fragmentedMp4Flags or FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA
)
)*/
extractors.add(
Mp4Extractor(
SubtitleParser.Factory.UNSUPPORTED,
this.mp4Flags or Mp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA
)
)
}
9 -> extractors.add(OggExtractor())
12 -> extractors.add(WavExtractor())
15 -> {
val midiExtractor = MIDI_EXTENSION_LOADER.getExtractor()
if (midiExtractor != null) {
extractors.add(midiExtractor)
}
}

else -> {}
}
}

private class ExtensionLoader(private val constructorSupplier: ConstructorSupplier) {
private val extensionLoaded = AtomicBoolean(false)

@GuardedBy("extensionLoaded")
private val extractorConstructor: Constructor<out Extractor>? = null

fun getExtractor(vararg constructorParams: Any?): Extractor? {
val extractorConstructor = this.maybeLoadExtractorConstructor()
return if (extractorConstructor == null) {
null
} else {
try {
extractorConstructor.newInstance(*constructorParams) as Extractor
} catch (var4: Exception) {
throw IllegalStateException("Unexpected error creating extractor", var4)
}
}
}

private fun maybeLoadExtractorConstructor(): Constructor<out Extractor>? {
synchronized(this.extensionLoaded) {
if (extensionLoaded.get()) {
return this.extractorConstructor
} else {
val var10000: Constructor<*>?
try {
var10000 = constructorSupplier.getConstructor()
} catch (var4: ClassNotFoundException) {
extensionLoaded.set(true)
return this.extractorConstructor
} catch (var5: Exception) {
throw RuntimeException("Error instantiating extension", var5)
}

return var10000
}
}
}
}

fun interface ConstructorSupplier {
@Throws(
InvocationTargetException::class,
IllegalAccessException::class,
NoSuchMethodException::class,
ClassNotFoundException::class
)
fun getConstructor(): Constructor<out Extractor?>?
}
}
Loading

0 comments on commit 8e21eca

Please sign in to comment.