fix(player): serialize live stream switching
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
@@ -90,9 +92,12 @@ class ServicioAudio {
|
||||
// AudioHandler
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
class PluriWaveAudioHandler extends BaseAudioHandler with SeekHandler {
|
||||
static const _timeoutCambioFuente = Duration(seconds: 12);
|
||||
|
||||
final AndroidEqualizer _eq = AndroidEqualizer();
|
||||
|
||||
late final AudioPlayer _player = AudioPlayer(
|
||||
userAgent: 'PluriWave/0.1.0 (es.freetimelab.pluriwave)',
|
||||
audioPipeline: AudioPipeline(androidAudioEffects: [_eq]),
|
||||
);
|
||||
|
||||
@@ -107,10 +112,28 @@ class PluriWaveAudioHandler extends BaseAudioHandler with SeekHandler {
|
||||
PresetEcualizador _presetActual = PresetEcualizador.flat;
|
||||
PresetEcualizador get presetActual => _presetActual;
|
||||
|
||||
Future<void> _colaCambiosFuente = Future<void>.value();
|
||||
int _revisionFuente = 0;
|
||||
bool _cambiandoFuente = false;
|
||||
|
||||
PluriWaveAudioHandler() {
|
||||
unawaited(_configurarSesionAudio());
|
||||
_setupStreams();
|
||||
}
|
||||
|
||||
Future<void> _configurarSesionAudio() async {
|
||||
try {
|
||||
final session = await AudioSession.instance;
|
||||
await session.configure(const AudioSessionConfiguration.music());
|
||||
} catch (e) {
|
||||
developer.log(
|
||||
'[PluriWave] No se pudo configurar AudioSession: $e',
|
||||
name: 'ServicioAudio',
|
||||
level: 800,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _setupStreams() {
|
||||
_player.playerStateStream.listen((state) {
|
||||
final playing = state.playing;
|
||||
@@ -136,6 +159,15 @@ class PluriWaveAudioHandler extends BaseAudioHandler with SeekHandler {
|
||||
_player.playbackEventStream.listen(
|
||||
(_) {},
|
||||
onError: (Object error, StackTrace stackTrace) {
|
||||
if (_cambiandoFuente) {
|
||||
developer.log(
|
||||
'[PluriWave] Error ignorado durante cambio de emisora: $error',
|
||||
name: 'ServicioAudio',
|
||||
level: 800,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
return;
|
||||
}
|
||||
_gestionarErrorReproduccion(error);
|
||||
},
|
||||
);
|
||||
@@ -214,33 +246,83 @@ class PluriWaveAudioHandler extends BaseAudioHandler with SeekHandler {
|
||||
|
||||
@override
|
||||
Future<void> playMediaItem(MediaItem mediaItem) async {
|
||||
final revision = ++_revisionFuente;
|
||||
_colaCambiosFuente = _colaCambiosFuente
|
||||
.catchError((_) {})
|
||||
.then((_) => _cambiarFuente(mediaItem, revision));
|
||||
return _colaCambiosFuente;
|
||||
}
|
||||
|
||||
Future<void> _cambiarFuente(MediaItem mediaItem, int revision) async {
|
||||
final emisora = _emisoraDesdeMediaItem(mediaItem);
|
||||
this.mediaItem.add(mediaItem);
|
||||
emisoraActual = emisora;
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
processingState: AudioProcessingState.loading,
|
||||
playing: false,
|
||||
errorMessage: null,
|
||||
));
|
||||
|
||||
_cambiandoFuente = true;
|
||||
try {
|
||||
await _player.stop();
|
||||
await _player.setUrl(mediaItem.id);
|
||||
await _player.play();
|
||||
emisoraActual = _emisoraDesdeMediaItem(mediaItem);
|
||||
await _player.stop().timeout(_timeoutCambioFuente);
|
||||
if (revision != _revisionFuente) return;
|
||||
|
||||
await _player
|
||||
.setAudioSource(
|
||||
AudioSource.uri(Uri.parse(mediaItem.id), tag: mediaItem),
|
||||
preload: false,
|
||||
)
|
||||
.timeout(_timeoutCambioFuente);
|
||||
if (revision != _revisionFuente) return;
|
||||
|
||||
await _activarEcualizador();
|
||||
_reproducirSinBloquear(mediaItem, revision);
|
||||
} on PlayerException catch (e) {
|
||||
_gestionarErrorReproduccion(e);
|
||||
if (revision == _revisionFuente) {
|
||||
_gestionarErrorReproduccion(e);
|
||||
}
|
||||
throw Exception(_mensajeAmigable(e));
|
||||
} on Exception catch (e) {
|
||||
} on Exception catch (e, stackTrace) {
|
||||
developer.log(
|
||||
'[PluriWave] Error inesperado en playMediaItem: $e',
|
||||
name: 'ServicioAudio',
|
||||
level: 900,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
processingState: AudioProcessingState.error,
|
||||
playing: false,
|
||||
errorMessage: 'Error inesperado al reproducir',
|
||||
));
|
||||
emisoraActual = null;
|
||||
this.mediaItem.add(null);
|
||||
if (revision == _revisionFuente) {
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
processingState: AudioProcessingState.error,
|
||||
playing: false,
|
||||
errorMessage: 'Error inesperado al reproducir',
|
||||
));
|
||||
emisoraActual = null;
|
||||
this.mediaItem.add(null);
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
if (revision == _revisionFuente) {
|
||||
_cambiandoFuente = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _reproducirSinBloquear(MediaItem mediaItem, int revision) {
|
||||
unawaited(
|
||||
_player.play().catchError((Object error, StackTrace stackTrace) {
|
||||
developer.log(
|
||||
'[PluriWave] Error al arrancar ${mediaItem.title}: $error',
|
||||
name: 'ServicioAudio',
|
||||
level: 900,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
if (revision == _revisionFuente) {
|
||||
_gestionarErrorReproduccion(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _activarEcualizador() async {
|
||||
try {
|
||||
final params = await _eq.parameters;
|
||||
@@ -295,6 +377,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler with SeekHandler {
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
_revisionFuente++;
|
||||
await _player.stop();
|
||||
emisoraActual = null;
|
||||
mediaItem.add(null);
|
||||
|
||||
Reference in New Issue
Block a user