Skip to content

Commit

Permalink
playback: use custom exoplayer
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
OxygenCobalt committed Jan 9, 2022
1 parent 5e3569d commit 2f190f1
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 9 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ 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
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
Expand Down Expand Up @@ -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)
)
}

Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
4 changes: 2 additions & 2 deletions info/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down
85 changes: 85 additions & 0 deletions prebuild.py
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 3 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
include ':app'
rootProject.name = "Auxio"
rootProject.name = "Auxio"
gradle.ext.exoplayerModulePrefix = 'exoplayer-'
apply from: file("deps/exoplayer/core_settings.gradle")

0 comments on commit 2f190f1

Please sign in to comment.