Skip to content

Commit

Permalink
Merge branch 'feature/exoplayer_media3' into CornCobsApps-migrate_to_…
Browse files Browse the repository at this point in the history
…media3
  • Loading branch information
ryanheise committed Jan 12, 2025
2 parents a4d4e84 + 150fd3a commit 54b7f11
Show file tree
Hide file tree
Showing 17 changed files with 114 additions and 50 deletions.
13 changes: 12 additions & 1 deletion just_audio/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
## 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

* Fix dealloc crash on iOS/macOS (@cristian1980).
* Fix Dart memory leak on dispose (@MinSeungHyun).
* Bump gradle to 8.5.0.

## 0.9.41

* Migrate from ExoPlayer to Media3 (@hansvdwd)
* Fix stop() to cause play() to return on iOS.

## 0.9.40

Expand Down
2 changes: 1 addition & 1 deletion just_audio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final duration = await player.setUrl( // Load a URL
player.play(); // Play without waiting for completion
await player.play(); // Play while waiting for completion
await player.pause(); // Pause but remain ready to play
await player.seek(Duration(second: 10)); // Jump to the 10 second position
await player.seek(Duration(seconds: 10)); // Jump to the 10 second position
await player.setSpeed(2.0); // Twice as fast
await player.setVolume(0.5); // Half as loud
await player.stop(); // Stop and free resources
Expand Down
6 changes: 3 additions & 3 deletions just_audio/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.android.tools.build:gradle:8.5.0'
}
}

Expand Down Expand Up @@ -39,7 +39,7 @@ android {
}

lintOptions {
disable 'InvalidPackage'
disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable'
}

compileOptions {
Expand All @@ -48,7 +48,7 @@ android {
}

dependencies {
def exoplayer_version = "1.0.2"
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"
Expand Down
5 changes: 0 additions & 5 deletions just_audio/android/gradle/wrapper/gradle-wrapper.properties

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import androidx.media3.common.C;
import androidx.media3.exoplayer.DefaultLivePlaybackSpeedControl;
import androidx.media3.exoplayer.DefaultLoadControl;
import androidx.media3.exoplayer.DefaultRenderersFactory;
import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.LivePlaybackSpeedControl;
import androidx.media3.exoplayer.LoadControl;
Expand All @@ -23,22 +22,27 @@
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;
import androidx.media3.exoplayer.source.ConcatenatingMediaSource;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
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;
import androidx.media3.exoplayer.source.SilenceMediaSource; // Deprecated
import androidx.media3.common.TrackGroup;
import androidx.media3.exoplayer.dash.DashMediaSource;
import androidx.media3.exoplayer.hls.HlsMediaSource;
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;
Expand Down Expand Up @@ -512,10 +516,10 @@ public void onMethodCall(final MethodCall call, final Result result) {
}
} catch (IllegalStateException e) {
e.printStackTrace();
result.error("Illegal state: " + e.getMessage(), null, null);
result.error("Illegal state: " + e.getMessage(), e.toString(), null);
} catch (Exception e) {
e.printStackTrace();
result.error("Error: " + e, null, null);
result.error("Error: " + e, e.toString(), null);
} finally {
broadcastPendingPlaybackEvent();
}
Expand Down Expand Up @@ -555,18 +559,21 @@ 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");
MediaSource mediaSource = mediaSources.get(id);
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<Object> children = mapGet(map, "children");
for (Object child : children) {
Expand Down Expand Up @@ -609,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");
Expand Down Expand Up @@ -639,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")),
Expand All @@ -657,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"));
}
Expand Down Expand Up @@ -764,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);
}
Expand Down
17 changes: 14 additions & 3 deletions just_audio/darwin/Classes/AudioPlayer.m
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,8 @@ - (void)load:(NSDictionary *)source initialPosition:(CMTime)initialPosition init
[_indexedAudioSources[i] attach:_player initialPos:(i == _index ? initialPosition : kCMTimeInvalid)];
}

if (_indexedAudioSources.count == 0 || !_player.currentItem ||
_player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
if (_loadResult && (_indexedAudioSources.count == 0 || !_player.currentItem ||
_player.currentItem.status == AVPlayerItemStatusReadyToPlay)) {
_processingState = ready;
_loadResult(@{@"duration": @([self getDurationMicroseconds])});
_loadResult = nil;
Expand Down Expand Up @@ -1326,10 +1326,21 @@ - (void)seek:(CMTime)position index:(NSNumber *)newIndex completionHandler:(void
}
}

- (void)dispose {
- (void)dispose:(BOOL)calledFromDealloc {
if (!_player) return;
if (_processingState != none) {
[_player pause];

[self updatePosition];
if (!calledFromDealloc) {
[self broadcastPlaybackEvent];
}
if (_playResult) {
//NSLog(@"PLAY FINISHED DUE TO STOP");
_playResult(@{});
_playResult = nil;
}

_processingState = none;
// If used just before destroying the current FlutterEngine, this will result in:
// NSInternalInconsistencyException: 'Sending a message before the FlutterEngine has been run.'
Expand Down
6 changes: 3 additions & 3 deletions just_audio/darwin/Classes/JustAudioPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
} else if ([@"disposePlayer" isEqualToString:call.method]) {
NSDictionary *request = (NSDictionary *)call.arguments;
NSString *playerId = request[@"id"];
[_players[playerId] dispose];
[_players[playerId] dispose:NO];
[_players setValue:nil forKey:playerId];
result(@{});
} else if ([@"disposeAllPlayers" isEqualToString:call.method]) {
for (NSString *playerId in _players) {
[_players[playerId] dispose];
[_players[playerId] dispose:NO];
}
[_players removeAllObjects];
result(@{});
Expand All @@ -56,7 +56,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {

- (void)dealloc {
for (NSString *playerId in _players) {
[_players[playerId] dispose];
[_players[playerId] dispose:YES];
}
[_players removeAllObjects];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
2 changes: 1 addition & 1 deletion just_audio/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ environment:
dependencies:
flutter:
sdk: flutter
audio_session: ^0.1.14
audio_session: ^0.1.23
rxdart: ^0.28.0
just_audio_mpv: ^0.1.6
just_audio_windows: ^0.2.0
Expand Down
2 changes: 1 addition & 1 deletion just_audio/ios/Classes/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@property (readonly, nonatomic) float speed;

- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam loadConfiguration:(NSDictionary *)loadConfiguration;
- (void)dispose;
- (void)dispose:(BOOL)calledFromDealloc;

@end

Expand Down
22 changes: 16 additions & 6 deletions just_audio/lib/just_audio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class AudioPlayer {
/// subscribe to the new platform's events.
StreamSubscription<PlayerDataMessage>? _playerDataSubscription;

StreamSubscription<AndroidAudioAttributes>?
_androidAudioAttributesSubscription;
StreamSubscription<void>? _becomingNoisyEventSubscription;
StreamSubscription<AudioInterruptionEvent>? _interruptionEventSubscription;

final String _id;
final _proxy = _ProxyHttpServer();
AudioSource? _audioSource;
Expand Down Expand Up @@ -285,7 +290,7 @@ class AudioPlayer {
// Respond to changes to AndroidAudioAttributes configuration.
if (androidApplyAudioAttributes && _isAndroid()) {
AudioSession.instance.then((audioSession) {
audioSession.configurationStream
_androidAudioAttributesSubscription = audioSession.configurationStream
.map((conf) => conf.androidAudioAttributes)
.where((attributes) => attributes != null)
.cast<AndroidAudioAttributes>()
Expand All @@ -295,10 +300,12 @@ class AudioPlayer {
}
if (handleInterruptions) {
AudioSession.instance.then((session) {
session.becomingNoisyEventStream.listen((_) {
_becomingNoisyEventSubscription =
session.becomingNoisyEventStream.listen((_) {
pause();
});
session.interruptionEventStream.listen((event) {
_interruptionEventSubscription =
session.interruptionEventStream.listen((event) {
if (event.begin) {
switch (event.type) {
case AudioInterruptionType.duck:
Expand Down Expand Up @@ -1255,6 +1262,9 @@ class AudioPlayer {
await _pitchSubject.close();
await _sequenceSubject.close();
await _shuffleIndicesSubject.close();
await _androidAudioAttributesSubscription?.cancel();
await _becomingNoisyEventSubscription?.cancel();
await _interruptionEventSubscription?.cancel();
}

/// Switch to using the native platform when [active] is `true` and using the
Expand Down Expand Up @@ -2523,9 +2533,9 @@ class ConcatenatingAudioSource extends AudioSource {

/// Creates a [ConcatenatingAudioSorce] with the specified [children]. If
/// [useLazyPreparation] is `true`, children will be loaded/buffered as late
/// as possible before needed for playback (currently supported on Android
/// only). When [AudioPlayer.shuffleModeEnabled] is `true`, [shuffleOrder]
/// will be used to determine the playback order (defaulting to
/// as possible before needed for playback (currently supported on Android,
/// iOS, MacOS). When [AudioPlayer.shuffleModeEnabled] is `true`,
/// [shuffleOrder] will be used to determine the playback order (defaulting to
/// [DefaultShuffleOrder]).
ConcatenatingAudioSource({
required this.children,
Expand Down
2 changes: 1 addition & 1 deletion just_audio/macos/Classes/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@property (readonly, nonatomic) float speed;

- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam loadConfiguration:(NSDictionary *)loadConfiguration;
- (void)dispose;
- (void)dispose:(BOOL)calledFromDealloc;

@end

Expand Down
4 changes: 2 additions & 2 deletions just_audio/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: just_audio
description: A feature-rich audio player for Flutter. Loop, clip and concatenate any sound from any source (asset/file/URL/stream) in a variety of audio formats with gapless playback.
version: 0.9.41
version: 0.9.43
repository: https://github.com/ryanheise/just_audio/tree/minor/just_audio
issue_tracker: https://github.com/ryanheise/just_audio/issues
topics:
Expand All @@ -20,7 +20,7 @@ dependencies:
just_audio_web: ^0.4.11
# just_audio_web:
# path: ../just_audio_web
audio_session: ^0.1.14
audio_session: ^0.1.23
rxdart: '>=0.26.0 <0.29.0'
path: ^1.8.0
path_provider: ^2.0.0
Expand Down
4 changes: 4 additions & 0 deletions just_audio_background/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.1-beta.14

* Fix shuffleOrder when mutating ConcatenatingAudioSource (@jonmarkhall).

## 0.0.1-beta.13

* Support rxdart 0.28.x.
Expand Down
2 changes: 1 addition & 1 deletion just_audio_background/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ environment:
dependencies:
flutter:
sdk: flutter
audio_session: ^0.1.14
audio_session: ^0.1.23
rxdart: ^0.27.2
just_audio:
path: ../../just_audio
Expand Down
Loading

0 comments on commit 54b7f11

Please sign in to comment.