diff --git a/just_audio/CHANGELOG.md b/just_audio/CHANGELOG.md index 550615a5..ecbffc36 100644 --- a/just_audio/CHANGELOG.md +++ b/just_audio/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.9.43 * Fix NPE in load on iOS/macOS. +* Migrate to media3 ExoPlayer 1.4.1 on Android (@hansvdwd and @ryanheise). ## 0.9.42 diff --git a/just_audio/android/build.gradle b/just_audio/android/build.gradle index 10795e2f..ac46ef64 100644 --- a/just_audio/android/build.gradle +++ b/just_audio/android/build.gradle @@ -48,11 +48,10 @@ android { } dependencies { - def exoplayer_version = "2.18.7" - implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" - implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" - implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayer_version" - implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$exoplayer_version" + def exoplayer_version = "1.4.1" + implementation "androidx.media3:media3-exoplayer:$exoplayer_version" + implementation "androidx.media3:media3-exoplayer-dash:$exoplayer_version" + implementation "androidx.media3:media3-exoplayer-hls:$exoplayer_version" + implementation "androidx.media3:media3-exoplayer-smoothstreaming:$exoplayer_version" } } - diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java index dc8a8e66..590bab34 100644 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java +++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java @@ -8,43 +8,46 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.DefaultLivePlaybackSpeedControl; -import com.google.android.exoplayer2.DefaultLoadControl; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.LivePlaybackSpeedControl; -import com.google.android.exoplayer2.LoadControl; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.Player.PositionInfo; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.Tracks; -import com.google.android.exoplayer2.audio.AudioAttributes; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer2.metadata.Metadata; -import com.google.android.exoplayer2.metadata.MetadataOutput; -import com.google.android.exoplayer2.metadata.icy.IcyHeaders; -import com.google.android.exoplayer2.metadata.icy.IcyInfo; -import com.google.android.exoplayer2.source.ClippingMediaSource; -import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.source.ShuffleOrder; -import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder; -import com.google.android.exoplayer2.source.SilenceMediaSource; -import com.google.android.exoplayer2.source.TrackGroup; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSource; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.util.MimeTypes; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.common.C; +import androidx.media3.exoplayer.DefaultLivePlaybackSpeedControl; +import androidx.media3.exoplayer.DefaultLoadControl; +import androidx.media3.exoplayer.ExoPlaybackException; +import androidx.media3.exoplayer.LivePlaybackSpeedControl; +import androidx.media3.exoplayer.LoadControl; +import androidx.media3.common.MediaItem; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; +import androidx.media3.common.Player.PositionInfo; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.common.Timeline; +import androidx.media3.common.Tracks; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences; +import androidx.media3.common.AudioAttributes; +import androidx.media3.extractor.DefaultExtractorsFactory; +import androidx.media3.common.Metadata; +import androidx.media3.exoplayer.metadata.MetadataOutput; +import androidx.media3.extractor.metadata.icy.IcyHeaders; +import androidx.media3.extractor.metadata.icy.IcyInfo; +import androidx.media3.exoplayer.source.ClippingMediaSource; // Deprecated +// For some reason, this import triggers the [deprecation] warning, despite the +// warnings being suppressed at each use. +// import androidx.media3.exoplayer.source.ConcatenatingMediaSource; // Deprecated +import androidx.media3.exoplayer.source.MediaSource; // Deprecated +import androidx.media3.exoplayer.source.ProgressiveMediaSource; // Deprecated +import androidx.media3.exoplayer.source.ShuffleOrder; +import androidx.media3.exoplayer.source.ShuffleOrder.DefaultShuffleOrder; +import androidx.media3.exoplayer.source.SilenceMediaSource; // Deprecated +import androidx.media3.common.TrackGroup; +import androidx.media3.exoplayer.dash.DashMediaSource; // Deprecated +import androidx.media3.exoplayer.hls.HlsMediaSource; // Deprecated +import androidx.media3.exoplayer.trackselection.TrackSelectionArray; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Util; import io.flutter.Log; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; @@ -556,10 +559,13 @@ private ShuffleOrder createShuffleOrder(int length, Integer firstIndex) { return new DefaultShuffleOrder(shuffleIndices, random.nextLong()); } - private ConcatenatingMediaSource concatenating(final Object index) { - return (ConcatenatingMediaSource)mediaSources.get((String)index); + + @SuppressWarnings("deprecation") + private androidx.media3.exoplayer.source.ConcatenatingMediaSource concatenating(final Object index) { + return (androidx.media3.exoplayer.source.ConcatenatingMediaSource)mediaSources.get((String)index); } + @SuppressWarnings("deprecation") private void setShuffleOrder(final Object json) { Map map = (Map)json; String id = mapGet(map, "id"); @@ -567,7 +573,7 @@ private void setShuffleOrder(final Object json) { if (mediaSource == null) return; switch ((String)mapGet(map, "type")) { case "concatenating": - ConcatenatingMediaSource concatenatingMediaSource = (ConcatenatingMediaSource)mediaSource; + androidx.media3.exoplayer.source.ConcatenatingMediaSource concatenatingMediaSource = (androidx.media3.exoplayer.source.ConcatenatingMediaSource)mediaSource; concatenatingMediaSource.setShuffleOrder(decodeShuffleOrder(mapGet(map, "shuffleOrder"))); List children = mapGet(map, "children"); for (Object child : children) { @@ -610,6 +616,7 @@ private DefaultExtractorsFactory buildExtractorsFactory(Map options) { return extractorsFactory; } + @SuppressWarnings("deprecation") private MediaSource decodeAudioSource(final Object json) { Map map = (Map)json; String id = (String)map.get("id"); @@ -640,7 +647,7 @@ private MediaSource decodeAudioSource(final Object json) { .createMediaSource(); case "concatenating": MediaSource[] mediaSources = getAudioSourcesArray(map.get("children")); - return new ConcatenatingMediaSource( + return new androidx.media3.exoplayer.source.ConcatenatingMediaSource( false, // isAtomic (Boolean)map.get("useLazyPreparation"), decodeShuffleOrder(mapGet(map, "shuffleOrder")), @@ -658,7 +665,7 @@ private MediaSource decodeAudioSource(final Object json) { for (int i = 0; i < looperChildren.length; i++) { looperChildren[i] = looperChild; } - return new ConcatenatingMediaSource(looperChildren); + return new androidx.media3.exoplayer.source.ConcatenatingMediaSource(looperChildren); default: throw new IllegalArgumentException("Unknown AudioSource type: " + map.get("type")); } @@ -765,11 +772,30 @@ private void ensurePlayerInitialized() { if (livePlaybackSpeedControl != null) { builder.setLivePlaybackSpeedControl(livePlaybackSpeedControl); } - if (offloadSchedulingEnabled) { - builder.setRenderersFactory(new DefaultRenderersFactory(context).setEnableAudioOffload(true)); - } player = builder.build(); - player.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); + // The latest ExoPlayer enables offload scheduling by default but + // it doesn't support gapless playback below SDK level 33 or speec + // changing. To maintain backwards compatibility within just_audio, + // we set gapless playback and speed changing support as required, + // and let ExoPlayer choose whether it can enable offload + // scheduling depending on device support. If the app passes in + // androidOffloadSchedulingEnabled: true, we simply remove these + // requirements which may prevent gapless and speed changing from + // working, but will allow offload to work. A future release may + // expose more parameters to the app concerning offloading + // preferences. + player.setTrackSelectionParameters( + player.getTrackSelectionParameters() + .buildUpon() + .setAudioOffloadPreferences( + new AudioOffloadPreferences.Builder() + .setIsGaplessSupportRequired(!offloadSchedulingEnabled) + .setIsSpeedChangeSupportRequired(!offloadSchedulingEnabled) + .setAudioOffloadMode(AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED) + .build() + ) + .build() + ); setAudioSessionId(player.getAudioSessionId()); player.addListener(this); }