playback: switch to ffmpeg extension
Switch to the FFMpeg ExoPlayer extension, which should enable support for ALAC while also retaining support for FLAC below Android 8.
This commit is contained in:
parent
c9ddda2ebd
commit
213409924b
15 changed files with 40 additions and 33 deletions
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#### What's New
|
#### What's New
|
||||||
- Added support for disc subtitles
|
- Added support for disc subtitles
|
||||||
|
- Added support for ALAC files
|
||||||
|
|
||||||
#### What's Improved
|
#### What's Improved
|
||||||
- Auxio will now accept zeroed track/disc numbers in the presence of non-zero total
|
- Auxio will now accept zeroed track/disc numbers in the presence of non-zero total
|
||||||
|
@ -11,6 +12,7 @@ track/disc fields
|
||||||
- Music loading has been made slightly faster
|
- Music loading has been made slightly faster
|
||||||
- Improved sort menu usability
|
- Improved sort menu usability
|
||||||
- Fall back to `TXXX:RELEASETYPE` on ID3v2 files
|
- Fall back to `TXXX:RELEASETYPE` on ID3v2 files
|
||||||
|
- Switches and checkboxes have been mildly visually refreshed
|
||||||
|
|
||||||
#### What's Fixed
|
#### What's Fixed
|
||||||
- Fixed non-functioning "repeat all" repeat mode
|
- Fixed non-functioning "repeat all" repeat mode
|
||||||
|
|
|
@ -127,7 +127,6 @@ dependencies {
|
||||||
kapt "com.google.dagger:dagger-compiler:$dagger_version"
|
kapt "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.9.1"
|
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.9.1"
|
||||||
testImplementation "junit:junit:4.13.2"
|
testImplementation "junit:junit:4.13.2"
|
||||||
|
|
|
@ -47,13 +47,13 @@ interface CachedSongsDao {
|
||||||
@TypeConverters(CachedSong.Converters::class)
|
@TypeConverters(CachedSong.Converters::class)
|
||||||
data class CachedSong(
|
data class CachedSong(
|
||||||
/**
|
/**
|
||||||
* The ID of the [Song]'s audio file, obtained from MediaStore. Note that this ID is highly
|
* The ID of the [RawSong]'s audio file, obtained from MediaStore. Note that this ID is highly
|
||||||
* unstable and should only be used for accessing the audio file.
|
* unstable and should only be used for accessing the audio file.
|
||||||
*/
|
*/
|
||||||
@PrimaryKey var mediaStoreId: Long,
|
@PrimaryKey var mediaStoreId: Long,
|
||||||
/** @see RawSong.dateAdded */
|
/** @see RawSong.dateAdded */
|
||||||
var dateAdded: Long,
|
var dateAdded: Long,
|
||||||
/** The latest date the [Song]'s audio file was modified, as a unix epoch timestamp. */
|
/** The latest date the [RawSong]'s audio file was modified, as a unix epoch timestamp. */
|
||||||
var dateModified: Long,
|
var dateModified: Long,
|
||||||
/** @see RawSong.size */
|
/** @see RawSong.size */
|
||||||
var size: Long? = null,
|
var size: Long? = null,
|
||||||
|
|
|
@ -26,8 +26,6 @@ import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import org.oxycblt.auxio.music.extractor.CacheRepository
|
|
||||||
import org.oxycblt.auxio.music.extractor.CacheRepositoryImpl
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
|
|
|
@ -15,11 +15,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.music.extractor
|
package org.oxycblt.auxio.music.cache
|
||||||
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.music.cache.CachedSong
|
|
||||||
import org.oxycblt.auxio.music.cache.CachedSongsDao
|
|
||||||
import org.oxycblt.auxio.music.model.RawSong
|
import org.oxycblt.auxio.music.model.RawSong
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,8 @@ data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||||
MediaFormat.MIMETYPE_AUDIO_VORBIS -> R.string.cdc_vorbis
|
MediaFormat.MIMETYPE_AUDIO_VORBIS -> R.string.cdc_vorbis
|
||||||
MediaFormat.MIMETYPE_AUDIO_OPUS -> R.string.cdc_opus
|
MediaFormat.MIMETYPE_AUDIO_OPUS -> R.string.cdc_opus
|
||||||
MediaFormat.MIMETYPE_AUDIO_FLAC -> R.string.cdc_flac
|
MediaFormat.MIMETYPE_AUDIO_FLAC -> R.string.cdc_flac
|
||||||
|
// TODO: Add ALAC to this as soon as I can stop using MediaFormat for
|
||||||
|
// extracting metadata and just use ExoPlayer.
|
||||||
// We don't give a name to more unpopular formats.
|
// We don't give a name to more unpopular formats.
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import java.io.File
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.oxycblt.auxio.music.MusicSettings
|
import org.oxycblt.auxio.music.MusicSettings
|
||||||
import org.oxycblt.auxio.music.extractor.Cache
|
import org.oxycblt.auxio.music.cache.Cache
|
||||||
import org.oxycblt.auxio.music.metadata.Date
|
import org.oxycblt.auxio.music.metadata.Date
|
||||||
import org.oxycblt.auxio.music.metadata.parseId3v2PositionField
|
import org.oxycblt.auxio.music.metadata.parseId3v2PositionField
|
||||||
import org.oxycblt.auxio.music.metadata.transformPositionField
|
import org.oxycblt.auxio.music.metadata.transformPositionField
|
||||||
|
|
|
@ -35,7 +35,7 @@ import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
import org.oxycblt.auxio.music.extractor.*
|
import org.oxycblt.auxio.music.cache.CacheRepository
|
||||||
import org.oxycblt.auxio.music.metadata.TagExtractor
|
import org.oxycblt.auxio.music.metadata.TagExtractor
|
||||||
import org.oxycblt.auxio.music.model.Library
|
import org.oxycblt.auxio.music.model.Library
|
||||||
import org.oxycblt.auxio.music.model.RawSong
|
import org.oxycblt.auxio.music.model.RawSong
|
||||||
|
|
|
@ -56,8 +56,10 @@ import org.oxycblt.auxio.util.logD
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
||||||
@Inject lateinit var indexer: Indexer
|
@Inject lateinit var imageLoader: ImageLoader
|
||||||
@Inject lateinit var musicRepository: MusicRepository
|
@Inject lateinit var musicRepository: MusicRepository
|
||||||
|
@Inject lateinit var indexer: Indexer
|
||||||
|
@Inject lateinit var musicSettings: MusicSettings
|
||||||
@Inject lateinit var playbackManager: PlaybackStateManager
|
@Inject lateinit var playbackManager: PlaybackStateManager
|
||||||
private val serviceJob = Job()
|
private val serviceJob = Job()
|
||||||
private val indexScope = CoroutineScope(serviceJob + Dispatchers.IO)
|
private val indexScope = CoroutineScope(serviceJob + Dispatchers.IO)
|
||||||
|
@ -67,8 +69,6 @@ class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
||||||
private lateinit var observingNotification: ObservingNotification
|
private lateinit var observingNotification: ObservingNotification
|
||||||
private lateinit var wakeLock: PowerManager.WakeLock
|
private lateinit var wakeLock: PowerManager.WakeLock
|
||||||
private lateinit var indexerContentObserver: SystemContentObserver
|
private lateinit var indexerContentObserver: SystemContentObserver
|
||||||
@Inject lateinit var musicSettings: MusicSettings
|
|
||||||
@Inject lateinit var imageLoader: ImageLoader
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
|
@ -34,7 +34,7 @@ import com.google.android.exoplayer2.RenderersFactory
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||||
import com.google.android.exoplayer2.audio.AudioCapabilities
|
import com.google.android.exoplayer2.audio.AudioCapabilities
|
||||||
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
|
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
|
||||||
import com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer
|
import com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer
|
||||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||||
|
@ -128,7 +128,7 @@ class PlaybackService :
|
||||||
audioListener,
|
audioListener,
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
||||||
replayGainProcessor),
|
replayGainProcessor),
|
||||||
LibflacAudioRenderer(handler, audioListener, replayGainProcessor))
|
FfmpegAudioRenderer(handler, audioListener, replayGainProcessor))
|
||||||
}
|
}
|
||||||
|
|
||||||
player =
|
player =
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.annotation.AttrRes
|
import androidx.annotation.AttrRes
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.util.fixDoubleRipple
|
import org.oxycblt.auxio.util.fixDoubleRipple
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,8 +31,11 @@ import org.oxycblt.auxio.util.fixDoubleRipple
|
||||||
*/
|
*/
|
||||||
open class RippleFixMaterialButton
|
open class RippleFixMaterialButton
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
constructor(
|
||||||
MaterialButton(context, attrs, defStyleAttr) {
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
@AttrRes defStyleAttr: Int = R.attr.materialButtonStyle
|
||||||
|
) : MaterialButton(context, attrs, defStyleAttr) {
|
||||||
init {
|
init {
|
||||||
fixDoubleRipple()
|
fixDoubleRipple()
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,6 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
/**
|
/**
|
||||||
* Set up the control bar in a [RemoteViews] layout that contains one. This is a kind of
|
* Set up the control bar in a [RemoteViews] layout that contains one. This is a kind of
|
||||||
* "floating" drawable that sits in front of the cover and contains the controls.
|
* "floating" drawable that sits in front of the cover and contains the controls.
|
||||||
* @param context [Context] required to set up the view.
|
|
||||||
*/
|
*/
|
||||||
private fun RemoteViews.setupBar(): RemoteViews {
|
private fun RemoteViews.setupBar(): RemoteViews {
|
||||||
// Below API 31, enable a rounded bar only if round mode is enabled.
|
// Below API 31, enable a rounded bar only if round mode is enabled.
|
||||||
|
@ -214,7 +213,6 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
/**
|
/**
|
||||||
* Set up the background in a [RemoteViews] layout that contains one. This is largely
|
* Set up the background in a [RemoteViews] layout that contains one. This is largely
|
||||||
* self-explanatory, being a solid-color background that sits behind the cover and controls.
|
* self-explanatory, being a solid-color background that sits behind the cover and controls.
|
||||||
* @param context [Context] required to set up the view.
|
|
||||||
*/
|
*/
|
||||||
private fun RemoteViews.setupBackground(): RemoteViews {
|
private fun RemoteViews.setupBackground(): RemoteViews {
|
||||||
// Below API 31, enable a rounded background only if round mode is enabled.
|
// Below API 31, enable a rounded background only if round mode is enabled.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.materialswitch.MaterialSwitch xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/switchWidget"
|
android:id="@+id/switchWidget"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
|
@ -5,6 +5,7 @@
|
||||||
<style name="Widget.Auxio.AppBarLayout" parent="Widget.Material3.AppBarLayout">
|
<style name="Widget.Auxio.AppBarLayout" parent="Widget.Material3.AppBarLayout">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">match_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<!-- Fix flickering lift animation when scrolling quickly -->
|
||||||
<item name="android:stateListAnimator">@null</item>
|
<item name="android:stateListAnimator">@null</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
32
prebuild.py
32
prebuild.py
|
@ -17,10 +17,8 @@ import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FFMPEG EXTENSION AND
|
||||||
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||||
# EXO_VERSION = "2.18.2"
|
|
||||||
FLAC_VERSION = "1.3.2"
|
|
||||||
|
|
||||||
OK="\033[1;32m" # Bold green
|
OK="\033[1;32m" # Bold green
|
||||||
FATAL="\033[1;31m" # Bold red
|
FATAL="\033[1;31m" # Bold red
|
||||||
|
@ -102,18 +100,26 @@ sh("git clone https://github.com/OxygenCobalt/ExoPlayer.git " + exoplayer_path)
|
||||||
os.chdir(exoplayer_path)
|
os.chdir(exoplayer_path)
|
||||||
sh("git checkout auxio")
|
sh("git checkout auxio")
|
||||||
|
|
||||||
print(INFO + "info:" + NC + " assembling flac extension...")
|
print(INFO + "info:" + NC + " assembling ffmpeg extension...")
|
||||||
flac_ext_aar_path = os.path.join(exoplayer_path, "extensions", "flac",
|
if system == "Linux":
|
||||||
"buildout", "outputs", "aar", "extension-flac-release.aar")
|
host = "linux-x86_64"
|
||||||
flac_ext_jni_path = os.path.join("extensions", "flac", "src", "main", "jni")
|
elif system == "Darwin":
|
||||||
|
host = "darwin-x86_64"
|
||||||
|
|
||||||
os.chdir(flac_ext_jni_path)
|
ffmpeg_ext_path = os.path.join(exoplayer_path, "extensions", "ffmpeg", "src", "main")
|
||||||
sh('curl "https://ftp.osuosl.org/pub/xiph/releases/flac/flac-' + FLAC_VERSION +
|
ffmpeg_ext_aar_path = os.path.join(exoplayer_path, "extensions", "ffmpeg", "buildout", "outputs", "aar", "extension-ffmpeg-release.aar")
|
||||||
'.tar.xz" | tar xJ && mv "flac-' + FLAC_VERSION + '" flac')
|
ffmpeg_ext_jni_path = os.path.join(exoplayer_path, "extensions", "ffmpeg", "src", "main", "jni")
|
||||||
sh(ndk_build_path + " APP_ABI=all -j4")
|
ffmpeg_src_path = os.path.join(ffmpeg_ext_jni_path, "ffmpeg")
|
||||||
|
|
||||||
|
os.chdir(ffmpeg_ext_jni_path)
|
||||||
|
sh("git clone git://source.ffmpeg.org/ffmpeg ffmpeg")
|
||||||
|
os.chdir(ffmpeg_src_path)
|
||||||
|
sh("git checkout release/4.2")
|
||||||
|
os.chdir(ffmpeg_ext_jni_path)
|
||||||
|
sh('./build_ffmpeg.sh "' + ffmpeg_ext_path + '" "' + ndk_path + '" "' + host + '" "' + "flac" + '" "' + "alac" + '"')
|
||||||
|
|
||||||
os.chdir(exoplayer_path)
|
os.chdir(exoplayer_path)
|
||||||
sh("./gradlew extension-flac:bundleReleaseAar")
|
sh("./gradlew extension-ffmpeg:bundleReleaseAar")
|
||||||
|
|
||||||
print(INFO + "info:" + NC + " assembling extractor component...")
|
print(INFO + "info:" + NC + " assembling extractor component...")
|
||||||
|
|
||||||
|
@ -124,7 +130,7 @@ sh("./gradlew library-extractor:bundleReleaseAar")
|
||||||
|
|
||||||
os.chdir(start_path)
|
os.chdir(start_path)
|
||||||
sh("mkdir " + libs_path)
|
sh("mkdir " + libs_path)
|
||||||
sh("cp " + flac_ext_aar_path + " " + libs_path)
|
sh("cp " + ffmpeg_ext_aar_path + " " + libs_path)
|
||||||
sh("cp " + extractor_aar_path + " " + libs_path)
|
sh("cp " + extractor_aar_path + " " + libs_path)
|
||||||
|
|
||||||
print(OK + "success:" + NC + " completed pre-build")
|
print(OK + "success:" + NC + " completed pre-build")
|
||||||
|
|
Loading…
Reference in a new issue