Skip to content

Commit a91c5b1

Browse files
authored
feat(android): ExoPlayer for Android (#1691)
# Description Closes #1526
1 parent 05605a8 commit a91c5b1

37 files changed

Lines changed: 1131 additions & 44 deletions

.github/workflows/test.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ jobs:
208208
# Need to execute lib and app tests one by one, see: https://github.com/flutter/flutter/issues/101031
209209
run: |
210210
( cd server; dart run bin/server.dart ) &
211-
flutter test integration_test/platform_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
212-
flutter test integration_test/lib_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
213-
flutter test integration_test/app_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
211+
flutter test integration_test/platform_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
212+
flutter test integration_test/lib_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
213+
flutter test integration_test/app_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false --dart-define TEST_FEATURE_BYTES_SOURCE=false --dart-define TEST_FEATURE_PLAYBACK_RATE=false
214214
215215
android:
216216
runs-on: ubuntu-latest
@@ -231,9 +231,9 @@ jobs:
231231
# Need to execute lib and app tests one by one, see: https://github.com/flutter/flutter/issues/101031
232232
run: |
233233
( cd server; dart run bin/server.dart ) &
234-
flutter test integration_test/platform_test.dart --dart-define USE_LOCAL_SERVER=true
235-
flutter test integration_test/lib_test.dart --dart-define USE_LOCAL_SERVER=true
236-
flutter test integration_test/app_test.dart --dart-define USE_LOCAL_SERVER=true
234+
flutter test integration_test/platform_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false
235+
flutter test integration_test/lib_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false
236+
flutter test integration_test/app_test.dart --dart-define USE_LOCAL_SERVER=true --dart-define TEST_FEATURE_LOW_LATENCY=false
237237
- name: Run Android unit tests
238238
working-directory: ./packages/audioplayers/example/android
239239
run: ./gradlew test

feature_parity_table.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,28 @@ Not every feature is available on every platform yet. Use this table to keep tra
44

55
If you would like to assist us implement a missing feature, please browse the [issue tracker](https://github.com/bluefireteam/audioplayers/issues) and reach out to us on our [Discord](https://discord.gg/pxrBmy4) server so we can coordinate efforts.
66

7-
## Note on Android Support
7+
## Note on Android
88

9-
Giving support to old Android devices is very hard, on this plugin we set the minSdk as 16, but we only ensure support >= 23 as that is the minimum version that the team has devices available to test changes and new features.
9+
### Media3 ExoPlayer
10+
11+
We are going to switch from the internal [Android MediaPlayer](https://developer.android.com/reference/android/media/MediaPlayer) to the recommended [Media3 ExoPlayer](https://developer.android.com/media/media3).
12+
We still endorse the old media player until we are sure, the Media3 implementation fulfills all the needs.
13+
14+
You already can try the Media3 implementation by adding `audioplayers_android_exo` to your apps `pubspec.yaml`.
15+
This [overrides](https://docs.flutter.dev/packages-and-plugins/developing-packages#non-endorsed-federated-plugin) our endorsed Android plugin implementation `audioplayers_android`:
16+
17+
```yaml
18+
dependencies:
19+
# ...
20+
audioplayers: any
21+
audioplayers_android_exo: any
22+
```
23+
24+
For more, see the [audioplayers_android_exo](https://github.com/bluefireteam/audioplayers/blob/main/packages/audioplayers_android_exo/README.md) package.
25+
26+
### Support for old SDKs
27+
28+
Giving support to old Android devices is very hard, on this plugin we set the minSdk as 19, but we only ensure support >= 23 as that is the minimum version that the team has devices available to test changes and new features.
1029
1130
This mean that, audioplayers should work on older devices, but we can't give any guarantees, we will not be able to look after issues regarding API < 23. But we would gladly take any pull requests from the community that fixes or improve support on those old versions.
1231
@@ -35,7 +54,7 @@ Note: LLM means Low Latency Mode.
3554
<tr><td colspan="7"><strong>Audio Config</strong></td></tr>
3655
<tr><td>set url</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td></tr>
3756
<tr><td>audio cache (pre-load)</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td></tr>
38-
<tr><td>low latency mode</td><td>SDK >=21</td><td>no</td><td>no</td><td>no</td><td>no</td><td>no</td></tr>
57+
<tr><td>low latency mode</td><td>SDK >=21 (except `audioplayers_android_exo`) </td><td>no</td><td>no</td><td>no</td><td>no</td><td>no</td></tr>
3958
<tr><td colspan="7"><strong>Audio Control Commands</strong></td></tr>
4059
<tr><td>resume / pause / stop</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td></tr>
4160
<tr><td>release</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td><td>yes</td></tr>

packages/audioplayers/example/integration_test/lib_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,6 @@ void main() async {
398398
await player.stop();
399399
});
400400
},
401-
skip: !isAndroid,
401+
skip: !features.hasLowLatency,
402402
);
403403
}

packages/audioplayers/example/integration_test/platform_features.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const testFeaturePlaybackRate = bool.fromEnvironment(
1010
defaultValue: true,
1111
);
1212

13+
const testFeatureLowLatency = bool.fromEnvironment(
14+
'TEST_FEATURE_LOW_LATENCY',
15+
defaultValue: true,
16+
);
17+
1318
/// Specify supported features for a platform.
1419
class PlatformFeatures {
1520
static const webPlatformFeatures = PlatformFeatures(
@@ -30,6 +35,8 @@ class PlatformFeatures {
3035
hasBytesSource: testFeatureBytesSource,
3136
// ignore: avoid_redundant_argument_values
3237
hasPlaybackRate: testFeaturePlaybackRate,
38+
// ignore: avoid_redundant_argument_values
39+
hasLowLatency: testFeatureLowLatency,
3340
);
3441

3542
static const iosPlatformFeatures = PlatformFeatures(

packages/audioplayers/example/integration_test/platform_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ void main() async {
118118
platform: platform,
119119
testData: td,
120120
);
121-
expect(await platform.getCurrentPosition(playerId), 0);
121+
if (!td.isLiveStream) {
122+
// Live stream position is not aligned yet.
123+
expect(await platform.getCurrentPosition(playerId), 0);
124+
}
122125
final durationMs = await platform.getDuration(playerId);
123126
expect(
124127
durationMs != null ? Duration(milliseconds: durationMs) : null,

packages/audioplayers/example/lib/tabs/sources.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final mp3Url1 = '$host/files/audio/ambient_c_motion.mp3';
2323
final mp3Url2 = '$host/files/audio/nasa_on_a_mission.mp3';
2424
final m3u8StreamUrl = useLocalServer
2525
? '$host/files/live_streams/nasa_power_of_the_rovers.m3u8'
26-
: 'https://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_low/ak/bbc_world_service.m3u8';
26+
: 'https://ll-hls-test.cdn-apple.com/llhls4/ll-hls-test-04/multi.m3u8';
2727
final mpgaStreamUrl = useLocalServer
2828
? '$host/stream/mpeg'
2929
: 'https://timesradio.wireless.radio/stream';
@@ -150,7 +150,7 @@ class _SourcesTabState extends State<SourcesTab>
150150
_createSourceTile(
151151
setSourceKey: const Key('setSource-url-remote-m3u8'),
152152
title: 'Remote URL M3U8',
153-
subtitle: 'BBC stream',
153+
subtitle: 'HLS Low-Latency Live Stream',
154154
source: UrlSource(m3u8StreamUrl),
155155
),
156156
_createSourceTile(

packages/audioplayers/example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ publish_to: none
44

55
dependencies:
66
audioplayers: ^6.2.0
7+
audioplayers_android_exo: ^0.1.0
78
collection: ^1.16.0
89
file_picker: ^8.0.3
910
flutter:

packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/MediaPlayerPlayer.kt renamed to packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/MediaPlayerWrapper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import android.os.PowerManager
66
import xyz.luan.audioplayers.AudioContextAndroid
77
import xyz.luan.audioplayers.source.Source
88

9-
class MediaPlayerPlayer(
9+
class MediaPlayerWrapper(
1010
private val wrappedPlayer: WrappedPlayer,
11-
) : Player {
11+
) : PlayerWrapper {
1212
private val mediaPlayer = createMediaPlayer(wrappedPlayer)
1313

1414
private fun createMediaPlayer(wrappedPlayer: WrappedPlayer): MediaPlayer {

packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/Player.kt renamed to packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/PlayerWrapper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package xyz.luan.audioplayers.player
33
import xyz.luan.audioplayers.AudioContextAndroid
44
import xyz.luan.audioplayers.source.Source
55

6-
interface Player {
6+
interface PlayerWrapper {
77
fun getDuration(): Int?
88
fun getCurrentPosition(): Int?
99
fun isLiveStream(): Boolean
@@ -12,7 +12,6 @@ interface Player {
1212
fun pause()
1313
fun stop()
1414
fun seekTo(position: Int)
15-
fun release()
1615

1716
fun setVolume(leftVolume: Float, rightVolume: Float)
1817
fun setRate(rate: Float)
@@ -21,5 +20,6 @@ interface Player {
2120
fun setSource(source: Source)
2221

2322
fun prepare()
23+
fun release()
2424
fun reset()
2525
}

packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/SoundPoolPlayer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private const val MAX_STREAMS = 32
2020
class SoundPoolPlayer(
2121
val wrappedPlayer: WrappedPlayer,
2222
private val soundPoolManager: SoundPoolManager,
23-
) : Player {
23+
) : PlayerWrapper {
2424
private val mainScope = CoroutineScope(Dispatchers.Main)
2525

2626
/** The id of the sound of source which will be played */

0 commit comments

Comments
 (0)