My app is having different channels and different m3u8 urls (non ending music stream) for the player.I am using HlsAudioSource
to set the m3u8 url and it is working fine for few minutes.after the player is stopped and I am getting exception PlatformException(-11819, Cannot Complete Action, null, null))
inside the playbackState stream.
My audioHandler code is as follows.
Future<AudioHandler> initAudioService() async {
return await AudioService.init(
builder: () => MyAudioHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'io.test.testMusic',
androidNotificationChannelName: 'testMusic',
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
),
);
}
class MyAudioHandler extends BaseAudioHandler {
final AudioPlayer _player = AudioPlayer();
MyAudioHandler() {
_player.playbackEventStream.map(_transformEvent).pipe(playbackState);
}
@override
Future<void> playFromUri(Uri uri, [Map<String, dynamic>? extras]) async {
mediaItem.add(MediaItem(id: uri.toString(), title: 'MoodStream'));
print('url=====${uri.toString()}');
try {
_player.setAudioSource(
HlsAudioSource(uri
// Uri.parse(
// 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8',
// ),
),
);
} on PlayerException catch (e) {
print('exception==$e');
return;
}
return; //_player.playing ? _player.pause() : _player.play();
}
@override
Future<void> click([MediaButton button = MediaButton.media]) async {
print('button===${button}');
switch (button) {
case MediaButton.media:
{
if (playbackState.valueOrNull?.playing == true) {
await _player.pause();
} else {
await _player.play();
}
break;
}
case MediaButton.next:
// TODO: Handle this case.
case MediaButton.previous:
// TODO: Handle this case.
}
return super.click(button);
}
@override
Future<void> play() async {
await _player.play();
return super.play();
}
@override
Future<void> pause() async {
await _player.pause();
return super.pause();
}
@override
Future<void> stop() async {
await _player.stop();
return super.stop();
}
PlaybackState _transformEvent(PlaybackEvent event) {
return PlaybackState(
controls: [
// MediaControl.rewind,
if (_player.playing) MediaControl.pause else MediaControl.play,
//MediaControl.stop,
// MediaControl.fastForward,
],
// systemActions: const {
// MediaAction.seek,
// MediaAction.seekForward,
// MediaAction.seekBackward,
// },
androidCompactActionIndices: const [
0,
1,
3
],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[_player.processingState]!,
playing: _player.playing,
updatePosition: _player.position,
bufferedPosition: _player.bufferedPosition,
speed: _player.speed,
queueIndex: event.currentIndex,
errorMessage: 'error');
}
}
playerHandler in bloc is
initPlayerHandler(Uri playerUri) async {
bool isValid = await isValidPlaylist();
if (isValid) {
try {
if (homeBloc.state.selectedRoom != null) {
await playerHandler.playFromUri(playerUri);
if (state.isPlaying) {
_resumePlay();
}
_subscribeToStream();
}
} on PlayerException catch (e) {
print('set player error====$e');
GlobalSnackBar.show('Error in playlist content');
emit(state.copyWith(
playbackStatus: PlaybackStatus.error, isPlaying: false));
//setPlayer(isFromException: true);
}
} else {
GlobalSnackBar.show('This room does\'t has any playlist.');
}
}
playbackState listnener is
playerHandler.playbackState.listen(
(event) async {
if (event.processingState == AudioProcessingState.buffering) {
/// bufferring
emit(state.copyWith(playbackStatus: PlaybackStatus.buffering));
}
if (event.processingState == AudioProcessingState.ready) {
emit(state.copyWith(playbackStatus: PlaybackStatus.playing));
}
if (event.playing) {
if (event.processingState == AudioProcessingState.completed) {
await setPlayer();
} else {
if (event.processingState == AudioProcessingState.idle) {
} else if (event.processingState == AudioProcessingState.ready) {
if (state.isPlaying) {
return;
}
emit(state.copyWith(
playbackStatus: PlaybackStatus.playing, isPlaying: true));
if (state.isPlaying) {
_resumePlay();
}
}
}
}
},
onError: (e) async {
print('_subscribeToStream==onError==$e');
GlobalSnackBar.show(e.message);
emit(state.copyWith(
playbackStatus: PlaybackStatus.playing, isPlaying: false));
},
onDone: () async {
await setPlayer();
},
);
other player functions are
Future<void> _stopPlayer() async {
try {
await playerHandler.stop();
return Future<void>.value();
} catch (e) {
print('player error _stopPlayer====$e');
Future<void>.value();
}
}
_resumePlay() async {
try {
await playerHandler.play();
_onPlayStausChange(3);
} catch (e) {
print('player error _resumePlay====$e');
emit(state.copyWith(
playbackStatus: PlaybackStatus.error, isPlaying: false));
GlobalSnackBar.show('Player content exception');
}
}
_pausePlay() async {
try {
await playerHandler.pause();
_onPlayStausChange(1);
emit(state.copyWith(
playbackStatus: PlaybackStatus.paused, isPlaying: false));
} catch (e) {
print('player error _pausePlay====$e');
emit(state.copyWith(
playbackStatus: PlaybackStatus.error, isPlaying: false));
}
}
flutter doctor is given below
[✓] Flutter (Channel stable, 3.13.9, on macOS 14.1 23B74 darwin-arm64, locale en-AE)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.3)
[✓] VS Code (version 1.84.0)
[✓] Connected device (4 available)
[✓] Network resources
From android device ,when the player stops ,I could able to get the player state as bufferring and the logs ,but no exceptions or errors.
D/BufferPoolAccessor2.0( 5497): bufferpool2 0xb4000073b9599e28 : 5(40960 size) total buffers - 1(8192 size) used buffers - 18216/18221 (recycle/alloc) - 5/36438 (fetch/transfer)
D/BufferPoolAccessor2.0( 5497): bufferpool2 0xb4000073b9599e28 : 5(40960 size) total buffers - 4(32768 size) used buffers - 18246/18251 (recycle/alloc) - 5/36492 (fetch/transfer)
D/BufferPoolAccessor2.0( 5497): evictor expired: 1, evicted: 1
D/TrafficStats( 5497): tagSocket(171) with statsTag=0xffffffff, statsUid=-1