Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediaPlayer throws (sometimes) IllegalStateException when playing sound #1331

Closed
2 tasks done
pitazzo opened this issue Nov 23, 2022 · 4 comments · Fixed by #1425
Closed
2 tasks done

MediaPlayer throws (sometimes) IllegalStateException when playing sound #1331

pitazzo opened this issue Nov 23, 2022 · 4 comments · Fixed by #1425
Labels
bug platform-android Affects the android platform

Comments

@pitazzo
Copy link

pitazzo commented Nov 23, 2022

Checklist

  • I read the troubleshooting guide before raising this issue
  • I made sure that the issue I am raising doesn't already exist

Sometimes, at the time of playing a sound, a Platform exception regarding MediaPlayer is raised.

Current bug behaviour

At the time of playing a sound, following exception is thrown:

Fatal Exception: java.lang.IllegalStateException:
       at android.media.MediaPlayer._prepare(MediaPlayer.java)
       at android.media.MediaPlayer.prepare(MediaPlayer.java:1332)
       at xyz.luan.audioplayers.player.MediaPlayerPlayer.prepare(MediaPlayerPlayer.kt:89)
       at xyz.luan.audioplayers.player.WrappedPlayer.stop(WrappedPlayer.kt:185)
       at xyz.luan.audioplayers.player.WrappedPlayer.onCompletion(WrappedPlayer.kt:248)
       at xyz.luan.audioplayers.player.MediaPlayerPlayer.createMediaPlayer$lambda-5$lambda-1(MediaPlayerPlayer.kt:17)
       at xyz.luan.audioplayers.player.MediaPlayerPlayer.$r8$lambda$3fK1i48Yert5dbg2Q8ZiB5tiKHg(MediaPlayerPlayer.kt)
       at xyz.luan.audioplayers.player.MediaPlayerPlayer$$InternalSyntheticLambda$0$296514634a2b58642f47b4fbcf9ba89cf596f2246040028ce119ddff1b7069d4$1.onCompletion(MediaPlayerPlayer.java:2)
       at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:3521)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:236)
       at android.app.ActivityThread.main(ActivityThread.java:7912)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:620)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1011)

The stack trace was this:

#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296)
<asynchronous suspension>
#2      AudioPlayer.play (package:audioplayers/src/audioplayer.dart:123)
<asynchronous suspension>
#3      AudioplayersSoundService.play (package:mxa_flutter_core/services/sound/audioplayers_sound_service.dart:76)
<asynchronous suspension>

As it was reported by our remote crash reporting tool, we do not know whether the sound is actually played.

Expected behaviour

No exception should be thrown. Sound should play.

Steps to reproduce

Unfortunately, we have not been able to reproduce the issue at the office. From the metrics we get from our crash reporting tool, the issue is quite rare compared to the number of sounds played with no problems. I've attached the code of our real service in charge of playing the sound.

Code sample
import 'package:audioplayers/audioplayers.dart';
import 'package:injectable/injectable.dart';

import 'package:mxa_flutter_core/services/logger/logger_service.dart';
import 'package:mxa_flutter_core/services/persistence/persistence_service.dart';
import 'package:mxa_flutter_core/services/sound/sound_config.dart';
import 'package:mxa_flutter_core/services/sound/sound_service.dart';

@Singleton(as: SoundService)
class AudioplayersSoundService extends SoundService {
  final PersistenceService _persistenceService;
  final LoggerService _loggerService;
  final SoundConfig _soundConfig;

  final AudioPlayer _player = AudioPlayer();
  final Map<String, Source> _sources = {};

  AudioplayersSoundService(
    this._persistenceService,
    this._loggerService,
    this._soundConfig,
  ) : super(_soundConfig) {
    AudioPlayer.global.setGlobalAudioContext(
      AudioContext(
        iOS: AudioContextIOS(
          defaultToSpeaker: true,
          category: AVAudioSessionCategory.playback,
          options: [],
        ),
        android: AudioContextAndroid(
          isSpeakerphoneOn: true,
          stayAwake: true,
          contentType: AndroidContentType.sonification,
          usageType: AndroidUsageType.notificationCommunicationInstant,
          audioFocus: null,
        ),
      ),
    );
  }

  @factoryMethod
  static Future<AudioplayersSoundService> setup(
    SoundConfig soundConfig,
    PersistenceService persistenceService,
    LoggerService loggerService,
  ) async {
    final service = AudioplayersSoundService(
      persistenceService,
      loggerService,
      soundConfig,
    );
    await service._loadAssetSounds();
    await service._loadLocalSounds();
    return service;
  }

  @override
  Future<void> play(String name, {bool loop = false, String? fallback}) async {
    if (!_sources.containsKey(name)) {
      _loggerService.error(
        'AudioplayersSoundService',
        'Trying to play unknown sound $name, will try playing $fallback',
      );

      if (fallback != null) {
        play(fallback, loop: loop);
      }

      return;
    }

    await _player.stop();

    _player.setReleaseMode(loop ? ReleaseMode.loop : ReleaseMode.stop);

    await _player.play(
      _sources[name]!,
      volume: 1,
      mode: PlayerMode.mediaPlayer,
    );
  }

  @override
  Future<void> reload() async {
    await _loadLocalSounds();
  }

  @override
  Future<void> stop(String name) async {
    await _player.stop();
  }

  Future<void> _loadAssetSounds() async {
    for (final sound in _soundConfig.assetSounds.entries) {
      final bytes = await AudioCache.instance.loadAsBytes(
        'sounds/${sound.value}',
      );
      _sources[sound.key] = BytesSource(bytes);
    }
  }

  Future<void> _loadLocalSounds() async {
    for (final directory in _soundConfig.localSoundsDirectories) {
      final existsResult = await _persistenceService.existsDirectory(directory);
      if (!(existsResult.data ?? false)) {
        continue;
      }
      final pathsResult = await _persistenceService.filesAtDirectory(directory);
      if (pathsResult.isFailure) {
        _loggerService.error(
          'AudioplayersSoundService',
          'Unable to get the files at $directory due to ${pathsResult.error}',
        );
        continue;
      }
      for (final path in pathsResult.data!) {
        final fileName = path.split('/').last;

        final dataResult = await _persistenceService.readFile(
          '$directory/$fileName',
        );

        if (dataResult.isFailure) {
          _loggerService.error(
            'AudioplayersSoundService',
            'Unable to read file $path due to ${dataResult.error}',
          );
          continue;
        }

        final soundName = fileName.split('.').first;
        _sources[soundName] = BytesSource(dataResult.data!);
        _loggerService.info(
          'AudioplayersSoundService',
          'Successfully loaded sound $soundName from $path',
        );
      }
    }
  }
}

Logs

Full Logs

Flutter doctor:

[✓] Flutter (Channel stable, 3.3.4, on macOS 13.0.1 22A400 darwin-arm, locale es-ES)
  • Flutter version 3.3.4 on channel stable at /Users/pitazzo/SDKs/flutter
  • Upstream repository https://github.com/flutter/flutter.git
  • Framework revision eb6d86ee27 (7 weeks ago), 2022-10-04 22:31:45 -0700
  • Engine revision c08d7d5efc
  • Dart version 2.18.2
  • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
  • Android SDK at /Users/pitazzo/SDKs/android
  • Platform android-33, build-tools 30.0.3
  • ANDROID_HOME = /Users/pitazzo/SDKs/android
  • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
  • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
  • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
  • Xcode at /Applications/Xcode.app/Contents/Developer
  • Build 14B47b
  • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
  • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
  • Android Studio at /Applications/Android Studio.app/Contents
  • Flutter plugin can be installed from:
    🔨 https://plugins.jetbrains.com/plugin/9212-flutter
  • Dart plugin can be installed from:
    🔨 https://plugins.jetbrains.com/plugin/6351-dart
  • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] VS Code (version 1.73.1)
  • VS Code at /Applications/Visual Studio Code.app/Contents
  • Flutter extension version 3.52.0

[✓] Connected device (3 available)
  • M2006C3LG (mobile) • RWYDO7Z9IRAQMBDU • android-arm    • Android 10 (API 29)
  • macOS (desktop)    • macos            • darwin-arm64   • macOS 13.0.1 22A400 darwin-arm
  • Chrome (web)       • chrome           • web-javascript • Google Chrome 106.0.5249.119

[✓] HTTP Host Availability
  • All required HTTP hosts are available

• No issues found!

Environment information

  • audioplayers version: ^1.1.1

Platform 1: android

  • OS name and version: Android 10
  • Device: Xiaomi Redmi 9A
  • build mode: release
@pitazzo pitazzo added the bug label Nov 23, 2022
@Gustl22 Gustl22 added the platform-android Affects the android platform label Nov 23, 2022
@Gustl22
Copy link
Collaborator

Gustl22 commented Nov 23, 2022

Thank you for your detailed report! Unfortunately I don't have the time to try until the error is thrown.
If you have clear reproduction steps, when the error occurs, we can help you much better and fix it.

FYI: #1322

@pitazzo
Copy link
Author

pitazzo commented Nov 24, 2022

Yeah, I know that with no steps, this is like looking for a needle in a haystack. Will follow the other issue, as seems to be the same problem. I do not know how I did not find out that issue without opening this one. Thanks!

@pitazzo pitazzo closed this as completed Nov 24, 2022
@Zhengbledor
Copy link

This error will be triggered when playing audio with play () for several times in a short time.

@Gustl22
Copy link
Collaborator

Gustl22 commented Feb 13, 2023

This is most probably exactly the same issue I experienced here

Gustl22 added a commit that referenced this issue Feb 14, 2023
In the rare case, that an media player error occurs (like when playing
unsupported media sources), the player still is in the "prepared" state,
although the internal MediaPlayer state isn't anymore. Then it could keep
calling methods, which lead to an illegal MediaPlayer state. By
resetting this variable on a media error, this should not make the
library crash anymore, but still emit the error.

Fixes #1260
Fixes #1331
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug platform-android Affects the android platform
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants