feat(playback): use assets_audio_player to fix macos double duration problems and android high loading latency

This commit is contained in:
Kingkor Roy Tirtho 2023-05-04 22:10:02 +06:00
parent be91e33828
commit 1fff0f1bd0
5 changed files with 108 additions and 24 deletions

View File

@ -28,7 +28,7 @@ class PlayPauseAction extends Action<PlayPauseIntent> {
if (playlist == null) { if (playlist == null) {
return null; return null;
} else if (!audioPlayer.isPlaying) { } else if (!audioPlayer.isPlaying) {
if (audioPlayer.hasSource && !audioPlayer.isCompleted) { if (audioPlayer.hasSource && !await audioPlayer.isCompleted) {
await playlistNotifier.resume(); await playlistNotifier.resume();
} else { } else {
await playlistNotifier.play(); await playlistNotifier.play();

View File

@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:audioplayers/audioplayers.dart' as ap; import 'package:audioplayers/audioplayers.dart' as ap;
import 'package:assets_audio_player/assets_audio_player.dart' as aap;
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
final audioPlayer = SpotubeAudioPlayer(); final audioPlayer = SpotubeAudioPlayer();
@ -24,6 +26,17 @@ enum PlayerState {
} }
} }
static PlayerState fromAapPlayerState(aap.PlayerState state) {
switch (state) {
case aap.PlayerState.play:
return PlayerState.playing;
case aap.PlayerState.pause:
return PlayerState.paused;
case aap.PlayerState.stop:
return PlayerState.stopped;
}
}
ap.PlayerState get asAudioPlayerPlayerState { ap.PlayerState get asAudioPlayerPlayerState {
switch (this) { switch (this) {
case PlayerState.playing: case PlayerState.playing:
@ -42,19 +55,26 @@ enum PlayerState {
class SpotubeAudioPlayer { class SpotubeAudioPlayer {
final ap.AudioPlayer? _audioPlayer; final ap.AudioPlayer? _audioPlayer;
final aap.AssetsAudioPlayer? _assetsAudioPlayer;
SpotubeAudioPlayer() SpotubeAudioPlayer()
: _audioPlayer = apSupportedPlatform ? ap.AudioPlayer() : null; : _audioPlayer = apSupportedPlatform ? ap.AudioPlayer() : null,
_assetsAudioPlayer =
!apSupportedPlatform ? aap.AssetsAudioPlayer.newPlayer() : null;
/// Whether the current platform supports the audioplayers plugin /// Whether the current platform supports the audioplayers plugin
static const bool apSupportedPlatform = true; static final bool apSupportedPlatform =
DesktopTools.platform.isWindows || DesktopTools.platform.isLinux;
// stream getters // stream getters
Stream<Duration> get durationStream { Stream<Duration> get durationStream {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.onDurationChanged.asBroadcastStream(); return _audioPlayer!.onDurationChanged.asBroadcastStream();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.onReadyToPlay
.where((event) => event != null)
.map((event) => event!.duration)
.asBroadcastStream();
} }
} }
@ -62,7 +82,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.onPositionChanged.asBroadcastStream(); return _audioPlayer!.onPositionChanged.asBroadcastStream();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.currentPosition.asBroadcastStream();
} }
} }
@ -71,7 +91,7 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to get buffered position // audioplayers doesn't have the capability to get buffered position
return const Stream<Duration>.empty().asBroadcastStream(); return const Stream<Duration>.empty().asBroadcastStream();
} else { } else {
throw UnimplementedError(); return const Stream<Duration>.empty().asBroadcastStream();
} }
} }
@ -79,7 +99,20 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.onPlayerComplete.asBroadcastStream(); return _audioPlayer!.onPlayerComplete.asBroadcastStream();
} else { } else {
throw UnimplementedError(); int lastValue = 0;
return positionStream.where(
(pos) {
final posS = pos.inSeconds;
final duration = _assetsAudioPlayer
?.current.valueOrNull?.audio.duration.inSeconds ??
0;
final isComplete =
posS > 0 && duration > 0 && posS == duration && posS != lastValue;
if (isComplete) lastValue = posS;
return isComplete;
},
).asBroadcastStream();
} }
} }
@ -89,7 +122,7 @@ class SpotubeAudioPlayer {
return state == ap.PlayerState.playing; return state == ap.PlayerState.playing;
}).asBroadcastStream(); }).asBroadcastStream();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.isPlaying.asBroadcastStream();
} }
} }
@ -97,14 +130,21 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return Stream.value(false).asBroadcastStream(); return Stream.value(false).asBroadcastStream();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.isBuffering.asBroadcastStream();
} }
} }
Stream<PlayerState> get playerStateStream => Stream<PlayerState> get playerStateStream {
_audioPlayer!.onPlayerStateChanged if (apSupportedPlatform) {
return _audioPlayer!.onPlayerStateChanged
.map((state) => PlayerState.fromApPlayerState(state)) .map((state) => PlayerState.fromApPlayerState(state))
.asBroadcastStream(); .asBroadcastStream();
} else {
return _assetsAudioPlayer!.playerState
.map(PlayerState.fromAapPlayerState)
.asBroadcastStream();
}
}
// regular info getter // regular info getter
@ -112,7 +152,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return await _audioPlayer!.getDuration(); return await _audioPlayer!.getDuration();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.current.valueOrNull?.audio.duration;
} }
} }
@ -120,7 +160,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return await _audioPlayer!.getCurrentPosition(); return await _audioPlayer!.getCurrentPosition();
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.currentPosition.valueOrNull;
} }
} }
@ -129,7 +169,7 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to get buffered position // audioplayers doesn't have the capability to get buffered position
return null; return null;
} else { } else {
throw UnimplementedError(); return null;
} }
} }
@ -137,7 +177,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.source != null; return _audioPlayer!.source != null;
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.current.valueOrNull != null;
} }
} }
@ -146,7 +186,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.playing; return _audioPlayer!.state == ap.PlayerState.playing;
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.isPlaying.valueOrNull ?? false;
} }
} }
@ -154,7 +194,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.paused; return _audioPlayer!.state == ap.PlayerState.paused;
} else { } else {
throw UnimplementedError(); return !isPlaying && hasSource;
} }
} }
@ -162,15 +202,15 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.stopped; return _audioPlayer!.state == ap.PlayerState.stopped;
} else { } else {
throw UnimplementedError(); return !isPlaying && !hasSource;
} }
} }
bool get isCompleted { Future<bool> get isCompleted async {
if (apSupportedPlatform) { if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.completed; return _audioPlayer!.state == ap.PlayerState.completed;
} else { } else {
throw UnimplementedError(); return !isPlaying && hasSource && await position == await duration;
} }
} }
@ -179,7 +219,7 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to get buffering state // audioplayers doesn't have the capability to get buffering state
return false; return false;
} else { } else {
throw UnimplementedError(); return _assetsAudioPlayer!.isBuffering.valueOrNull ?? false;
} }
} }
@ -191,7 +231,11 @@ class SpotubeAudioPlayer {
return ap.DeviceFileSource(url); return ap.DeviceFileSource(url);
} }
} else { } else {
throw UnimplementedError(); if (url.startsWith("https")) {
return aap.Audio.network(url);
} else {
return aap.Audio.file(url);
}
} }
} }
@ -201,7 +245,7 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to preload // audioplayers doesn't have the capability to preload
return; return;
} else { } else {
throw UnimplementedError(); return;
} }
} }
@ -210,35 +254,54 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform && urlType is ap.Source) { if (apSupportedPlatform && urlType is ap.Source) {
await _audioPlayer?.play(urlType); await _audioPlayer?.play(urlType);
} else { } else {
throw UnimplementedError(); await _assetsAudioPlayer?.stop();
await _assetsAudioPlayer?.open(
urlType as aap.Playable,
autoStart: true,
audioFocusStrategy: const aap.AudioFocusStrategy.request(
resumeAfterInterruption: true,
),
loopMode: aap.LoopMode.none,
playInBackground: aap.PlayInBackground.enabled,
headPhoneStrategy: aap.HeadPhoneStrategy.pauseOnUnplugPlayOnPlug,
showNotification: false,
respectSilentMode: true,
);
} }
} }
Future<void> pause() async { Future<void> pause() async {
await _audioPlayer?.pause(); await _audioPlayer?.pause();
await _assetsAudioPlayer?.pause();
} }
Future<void> resume() async { Future<void> resume() async {
await _audioPlayer?.resume(); await _audioPlayer?.resume();
await _assetsAudioPlayer?.play();
} }
Future<void> stop() async { Future<void> stop() async {
await _audioPlayer?.stop(); await _audioPlayer?.stop();
await _assetsAudioPlayer?.stop();
} }
Future<void> seek(Duration position) async { Future<void> seek(Duration position) async {
await _audioPlayer?.seek(position); await _audioPlayer?.seek(position);
await _assetsAudioPlayer?.seek(position);
} }
Future<void> setVolume(double volume) async { Future<void> setVolume(double volume) async {
await _audioPlayer?.setVolume(volume); await _audioPlayer?.setVolume(volume);
await _assetsAudioPlayer?.setVolume(volume);
} }
Future<void> setSpeed(double speed) async { Future<void> setSpeed(double speed) async {
await _audioPlayer?.setPlaybackRate(speed); await _audioPlayer?.setPlaybackRate(speed);
await _assetsAudioPlayer?.setPlaySpeed(speed);
} }
Future<void> dispose() async { Future<void> dispose() async {
await _audioPlayer?.dispose(); await _audioPlayer?.dispose();
await _assetsAudioPlayer?.dispose();
} }
} }

View File

@ -5,6 +5,8 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import assets_audio_player
import assets_audio_player_web
import audio_service import audio_service
import audio_session import audio_session
import audioplayers_darwin import audioplayers_darwin
@ -24,6 +26,8 @@ import window_manager
import window_size import window_size
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AssetsAudioPlayerPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerPlugin"))
AssetsAudioPlayerWebPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerWebPlugin"))
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))

View File

@ -97,6 +97,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.2"
assets_audio_player:
dependency: "direct main"
description:
name: assets_audio_player
sha256: dcea8cd9c11cd9c34586f2446bfcdf099362159c56f97517ba941ac151974ea9
url: "https://pub.dev"
source: hosted
version: "3.0.6"
assets_audio_player_web:
dependency: transitive
description:
name: assets_audio_player_web
sha256: "4575ec40033d818ff022d48f7d46e24c01bc632bd1cd36a4f8b58b38e9aa4a81"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
async: async:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -10,6 +10,7 @@ environment:
dependencies: dependencies:
args: ^2.3.2 args: ^2.3.2
assets_audio_player: ^3.0.6
async: ^2.9.0 async: ^2.9.0
audio_service: ^0.18.9 audio_service: ^0.18.9
audio_session: ^0.1.13 audio_session: ^0.1.13