From 2f190f1e0b8ff6f266f0ba41ce891e90f97f0da1 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sun, 9 Jan 2022 12:24:06 -0700 Subject: [PATCH] playback: use custom exoplayer Use a custom ExoPlayer fork with the FLAC extension enabled. This greatly improves the listening experience, as it enables metadata support on OGG files and FLAC files to be played below API 27. --- README.md | 8 ++ app/build.gradle | 3 +- .../org/oxycblt/auxio/coil/AuxioFetcher.kt | 2 +- .../auxio/playback/system/AudioReactor.kt | 2 +- .../auxio/playback/system/PlaybackService.kt | 7 +- gradlew | 0 info/FAQ.md | 4 +- prebuild.py | 85 +++++++++++++++++++ settings.gradle | 4 +- 9 files changed, 106 insertions(+), 9 deletions(-) mode change 100644 => 100755 gradlew create mode 100755 prebuild.py diff --git a/README.md b/README.md index 9738fb875..e4457cb6a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,14 @@ I primarily built Auxio for myself, but you can use it too, I guess. - Storage (`READ_EXTERNAL_STORAGE`): to read and play your media files - Services (`FOREGROUND_SERVICE`, `WAKE_LOCK`): to keep the music playing even if the app itself is in background +## Building + +Auxio relies on a local version of ExoPlayer that enables some extra features. So, the build process is as follows: + +1. Change into the project directory +2. Run `python3 prebuild.py`, which installs ExoPlayer and it's extensions. +3. Build the project normally in Android Studio. + ## Contributing Auxio accepts most contributions as long as they follow the [Contribution Guidelines](/.github/CONTRIBUTING.md). diff --git a/app/build.gradle b/app/build.gradle index 63b9932cc..efa21d646 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,8 @@ dependencies { // --- THIRD PARTY --- // ExoPlayer - implementation "com.google.android.exoplayer:exoplayer-core:2.16.1" + implementation project(':exoplayer-library-core') + implementation project(':exoplayer-extension-flac') // Image loading implementation 'io.coil-kt:coil:2.0.0-alpha06' diff --git a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt index 07bd896bd..6c2bcf791 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt @@ -18,8 +18,8 @@ import coil.size.pxOrElse import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaMetadata import com.google.android.exoplayer2.MetadataRetriever -import com.google.android.exoplayer2.metadata.flac.PictureFrame import com.google.android.exoplayer2.metadata.id3.ApicFrame +import com.google.android.exoplayer2.metadata.xiph.PictureFrame import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okio.buffer diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt index 85c65267a..c7a81d6a9 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt @@ -25,8 +25,8 @@ import androidx.media.AudioAttributesCompat import androidx.media.AudioFocusRequestCompat import androidx.media.AudioManagerCompat import com.google.android.exoplayer2.metadata.Metadata -import com.google.android.exoplayer2.metadata.flac.VorbisComment import com.google.android.exoplayer2.metadata.id3.TextInformationFrame +import com.google.android.exoplayer2.metadata.xiph.VorbisComment import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.util.getSystemServiceSafe diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index cae752440..32331bf5d 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -39,6 +39,7 @@ import com.google.android.exoplayer2.RenderersFactory import com.google.android.exoplayer2.TracksInfo import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer +import com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory import com.google.android.exoplayer2.mediacodec.MediaCodecSelector import com.google.android.exoplayer2.source.DefaultMediaSourceFactory @@ -46,9 +47,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import org.oxycblt.auxio.BuildConfig @@ -371,7 +371,8 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac // battery/apk size/cache size val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ -> arrayOf( - MediaCodecAudioRenderer(this, MediaCodecSelector.DEFAULT, handler, audioListener) + MediaCodecAudioRenderer(this, MediaCodecSelector.DEFAULT, handler, audioListener), + LibflacAudioRenderer(handler, audioListener) ) } diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/info/FAQ.md b/info/FAQ.md index 103d3c7fe..28d95654a 100644 --- a/info/FAQ.md +++ b/info/FAQ.md @@ -27,8 +27,8 @@ ability to be extended to music sources outside of local files. You can read mor #### What formats does Auxio support? As per the [Supported ExoPlayer Formats](https://exoplayer.dev/supported-formats.html), Auxio supports -MP4, MP3, MKA, OGG, WAV, MPEG, ACC on all versions of Android. However, FLAC files can only be played -on Android 8.1 and above. Below that, Auxio must be patched with the [FLAC Extension](https://github.com/google/ExoPlayer/tree/release-v2/extensions/flac). +MP4, MP3, MKA, OGG, WAV, MPEG, AAC on all versions of Android. Auxio also supports FLAC on all versions +of Android through the use of the ExoPlayer FLAC extension. #### Why are accents lighter/less saturated in dark mode? diff --git a/prebuild.py b/prebuild.py new file mode 100755 index 000000000..ab3e1b2aa --- /dev/null +++ b/prebuild.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# This script automatically installs exoplayer with the necessary components. +# This is written in version-agnostic python3, because I'd rather +# not have to deal with the insanity of bash. +import os +import platform +import sys +import subprocess +import re + +FLAC_VERSION = "1.3.2" + +FATAL="\033[1;31m" +WARN="\033[1;91m" +INFO="\033[1;94m" +OK="\033[1;92m" +NC="\033[0m" + +system = platform.system() + +# We do some shell scripting later on, so we can't support windows. +if system not in ["Linux", "Darwin"]: + print("fatal: unsupported platform " + system) + sys.exit(1) + +def sh(cmd): + code = subprocess.call(["sh", "-c", "set -e; " + cmd]) + + if code != 0: + print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code)) + sys.exit(1) + +exoplayer_path = os.path.join(os.path.abspath(os.curdir), "deps", "exoplayer") + +if os.path.exists(exoplayer_path): + reinstall = input(INFO + "info:" + NC + " ExoPlayer is already installed. Would you like to reinstall it? [y/n] ") + + if not re.match("[yY][eE][sS]|[yY]", reinstall): + sys.exit(0) + +ndk_path = os.getenv("NDK_PATH") + +if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk_build")): + # We don't have a proper path. Do some digging on the Android SDK directory + # to see if we can find it. + if system == "Linux": + ndk_root = os.path.join(os.getenv("HOME"), "Android", "Sdk", "ndk") + elif system == "Darwin": + ndk_root = os.path.join(os.getenv("HOME"), "Library", "Android", "sdk", "ndk") + + candidates = [] + + for entry in os.scandir(ndk_root): + if entry.is_dir(): + candidates.append(entry.path) + + if len(candidates) > 0: + print(WARN + "warn:" + NC + " NDK_PATH was not set or invalid. Multiple candidates were found however:") + + for i, candidate in enumerate(candidates): + print("[" + str(i) + "] " + candidate) + + try: + ndk_path = candidates[int(input("Enter the NDK to use [Default 0]: "))] + except: + ndk_path = candidates[0] + else: + print(FATAL + "fatal:" + NC + " NDK_PATH is either invalid, or the Android NDK was not installed at a recognized location.") + system.exit(1) + +# Now try to install ExoPlayer. +sh("rm -rf deps") + +print(INFO + "info:" + NC + " Cloning ExoPlayer...") +sh("git clone https://github.com/oxygencobalt/ExoPlayer.git " + exoplayer_path) +os.chdir(exoplayer_path) +sh("git checkout release-v2") + +flac_ext_jni_path = os.path.join("extensions", "flac", "src", "main", "jni") +print(INFO + "info:" + NC + " Installing FLAC extension...") +os.chdir(flac_ext_jni_path) +sh('curl "https://ftp.osuosl.org/pub/xiph/releases/flac/flac-' + FLAC_VERSION + '.tar.xz" | tar xJ && mv "flac-' + FLAC_VERSION + '" flac') +sh(ndk_path + "/ndk-build APP_ABI=all -j4") + +print(OK + "Prebuild successful." + NC) diff --git a/settings.gradle b/settings.gradle index f5596f2ad..37f1807a0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,4 @@ include ':app' -rootProject.name = "Auxio" \ No newline at end of file +rootProject.name = "Auxio" +gradle.ext.exoplayerModulePrefix = 'exoplayer-' +apply from: file("deps/exoplayer/core_settings.gradle") \ No newline at end of file