audio_service icon indicating copy to clipboard operation
audio_service copied to clipboard

Problem with android 15 but not with android 13

Open csacchetti opened this issue 4 months ago • 4 comments

Platforms exhibiting the bug

  • [x] Android
  • [ ] iOS
  • [ ] web

Devices exhibiting the bug

Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 3.35.1, on macOS 15.6 24G84 darwin-arm64, locale it-IT) [✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 16.4) [✓] Chrome - develop for the web [✓] Android Studio (version 2023.1) [✓] VS Code (version 1.102.3) [✓] Connected device (6 available) [✓] Network resources

I have the problem with Samsung S24FE with android 15 but not with Samsung S20FE with android 13

Minimal reproduction project

Minimal reproduction project
// ignore_for_file: public_member_api_docs

// FOR MORE EXAMPLES, VISIT THE GITHUB REPOSITORY AT:
//
//  https://github.com/ryanheise/audio_service
//
// This example implements a minimal audio handler that renders the current
// media item and playback state to the system notification and responds to 4
// media actions:
//
// - play
// - pause
// - seek
// - stop
//
// To run this example, use:
//
// flutter run

import 'dart:async';

import 'package:audio_service/audio_service.dart';
import 'package:audio_service_example/common.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart';

// You might want to provide this using dependency injection rather than a
// global variable.
late AudioHandler _audioHandler;

Future<void> main() async {
  _audioHandler = await AudioService.init(
    builder: () => AudioPlayerHandler(),
    config: const AudioServiceConfig(
      androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio',
      androidNotificationChannelName: 'Audio playback',
      androidNotificationOngoing: true,
    ),
  );
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Audio Service Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MainScreen(),
    );
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Audio Service Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Show media item title
            StreamBuilder<MediaItem?>(
              stream: _audioHandler.mediaItem,
              builder: (context, snapshot) {
                final mediaItem = snapshot.data;
                return Text(mediaItem?.title ?? '');
              },
            ),
            // Play/pause/stop buttons.
            StreamBuilder<bool>(
              stream: _audioHandler.playbackState
                  .map((state) => state.playing)
                  .distinct(),
              builder: (context, snapshot) {
                final playing = snapshot.data ?? false;
                return Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    _button(Icons.fast_rewind, _audioHandler.rewind),
                    if (playing)
                      _button(Icons.pause, _audioHandler.pause)
                    else
                      _button(Icons.play_arrow, _audioHandler.play),
                    _button(Icons.stop, _audioHandler.stop),
                    _button(Icons.fast_forward, _audioHandler.fastForward),
                  ],
                );
              },
            ),
            // A seek bar.
            StreamBuilder<MediaState>(
              stream: _mediaStateStream,
              builder: (context, snapshot) {
                final mediaState = snapshot.data;
                return SeekBar(
                  duration: mediaState?.mediaItem?.duration ?? Duration.zero,
                  position: mediaState?.position ?? Duration.zero,
                  onChangeEnd: (newPosition) {
                    _audioHandler.seek(newPosition);
                  },
                );
              },
            ),
            // Display the processing state.
            StreamBuilder<AudioProcessingState>(
              stream: _audioHandler.playbackState
                  .map((state) => state.processingState)
                  .distinct(),
              builder: (context, snapshot) {
                final processingState =
                    snapshot.data ?? AudioProcessingState.idle;
                return Text(
                    // ignore: deprecated_member_use
                    "Processing state: ${describeEnum(processingState)}");
              },
            ),
          ],
        ),
      ),
    );
  }

  /// A stream reporting the combined state of the current media item and its
  /// current position.
  Stream<MediaState> get _mediaStateStream =>
      Rx.combineLatest2<MediaItem?, Duration, MediaState>(
          _audioHandler.mediaItem,
          AudioService.position,
          (mediaItem, position) => MediaState(mediaItem, position));

  IconButton _button(IconData iconData, VoidCallback onPressed) => IconButton(
        icon: Icon(iconData),
        iconSize: 64.0,
        onPressed: onPressed,
      );
}

class MediaState {
  final MediaItem? mediaItem;
  final Duration position;

  MediaState(this.mediaItem, this.position);
}

/// An [AudioHandler] for playing a single item.
class AudioPlayerHandler extends BaseAudioHandler with SeekHandler {
  static final _item = MediaItem(
    id: 'https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3',
    album: "Science Friday",
    title: "A Salute To Head-Scratching Science",
    artist: "Science Friday and WNYC Studios",
    duration: const Duration(milliseconds: 5739820),
    artUri: Uri.parse(
        'https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg'),
  );

  final _player = AudioPlayer();

  /// Initialise our audio handler.
  AudioPlayerHandler() {
    // So that our clients (the Flutter UI and the system notification) know
    // what state to display, here we set up our audio handler to broadcast all
    // playback state changes as they happen via playbackState...
    _player.playbackEventStream.map(_transformEvent).pipe(playbackState);
    // ... and also the current media item via mediaItem.
    mediaItem.add(_item);

    // Load the player.
    _player.setAudioSource(AudioSource.uri(Uri.parse(_item.id)));
  }

  // In this simple example, we handle only 4 actions: play, pause, seek and
  // stop. Any button press from the Flutter UI, notification, lock screen or
  // headset will be routed through to these 4 methods so that you can handle
  // your audio playback logic in one place.

  @override
  Future<void> play() => _player.play();

  @override
  Future<void> pause() => _player.pause();

  @override
  Future<void> seek(Duration position) => _player.seek(position);

  @override
  Future<void> stop() => _player.stop();

  /// Transform a just_audio event into an audio_service state.
  ///
  /// This method is used from the constructor. Every event received from the
  /// just_audio player will be transformed into an audio_service state so that
  /// it can be broadcast to audio_service clients.
  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,
    );
  }
}

Steps to reproduce

I wanted to report a problem I encountered when I upgraded to Flutter 3.35.1 and Dart 3.9.0

My configuration of audio_service 0.18.18, just_audio 0.10.4, audio_session 0.2.2 The app works perfectly with foreground word and background music with my Samsung S20 FE with android 13. With the Samsung S24FE with android 15 it has problems. When I insert the background it creates like a conflict and it has like an endless loop, sometimes it stops and then starts again.

Problem with android 15 but not with android 13

Translated with DeepL.com (free version)

The problem can also be reproduced in the example file if you use main.dart. If you launch it in Android 13 with the above configuration it works fine. If you launch it with Android 15 it does not work and gives the same error that it gives me too https://github.com/ryanheise/audio_service/blob/minor/audio_service/example/lib/main.dart

E/IAudioFlinger( 4249): Function: getRenderPosition Line: 509 Failed E/ExoPlayerImplInternal( 4249): Playback error E/ExoPlayerImplInternal( 4249): androidx.media3.exoplayer.ExoPlaybackException: Unexpected runtime error E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:720) E/ExoPlayerImplInternal( 4249): at android.os.Handler.dispatchMessage(Handler.java:103) E/ExoPlayerImplInternal( 4249): at android.os.Looper.loopOnce(Looper.java:257) E/ExoPlayerImplInternal( 4249): at android.os.Looper.loop(Looper.java:342) E/ExoPlayerImplInternal( 4249): at android.os.HandlerThread.run(HandlerThread.java:85) E/ExoPlayerImplInternal( 4249): Caused by: java.lang.IllegalArgumentException E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.audio.DefaultAudioSink.getFramesPerEncodedSample(DefaultAudioSink.java:1779) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.audio.DefaultAudioSink.handleBuffer(DefaultAudioSink.java:970) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.audio.MediaCodecAudioRenderer.processOutputBuffer(MediaCodecAudioRenderer.java:773) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.bypassRender(MediaCodecRenderer.java:2358) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:868) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1136) E/ExoPlayerImplInternal( 4249): at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:561) E/ExoPlayerImplInternal( 4249): ... 4 more

Expected results

I expect it to simply play correctly as it does with Android 13

Actual results

It does not sound correctly

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
E/IAudioFlinger( 4249): Function: getRenderPosition Line: 509 Failed 
E/ExoPlayerImplInternal( 4249): Playback error
E/ExoPlayerImplInternal( 4249):   androidx.media3.exoplayer.ExoPlaybackException: Unexpected runtime error
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:720)
E/ExoPlayerImplInternal( 4249):       at android.os.Handler.dispatchMessage(Handler.java:103)
E/ExoPlayerImplInternal( 4249):       at android.os.Looper.loopOnce(Looper.java:257)
E/ExoPlayerImplInternal( 4249):       at android.os.Looper.loop(Looper.java:342)
E/ExoPlayerImplInternal( 4249):       at android.os.HandlerThread.run(HandlerThread.java:85)
E/ExoPlayerImplInternal( 4249):   Caused by: java.lang.IllegalArgumentException
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.audio.DefaultAudioSink.getFramesPerEncodedSample(DefaultAudioSink.java:1779)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.audio.DefaultAudioSink.handleBuffer(DefaultAudioSink.java:970)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.audio.MediaCodecAudioRenderer.processOutputBuffer(MediaCodecAudioRenderer.java:773)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.bypassRender(MediaCodecRenderer.java:2358)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:868)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1136)
E/ExoPlayerImplInternal( 4249):       at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:561)
E/ExoPlayerImplInternal( 4249):       ... 4 more

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.35.1, on macOS 15.6 24G84 darwin-arm64, locale it-IT)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] VS Code (version 1.102.3)
[✓] Connected device (6 available)
[✓] Network resources

• No issues found!

csacchetti avatar Aug 18 '25 22:08 csacchetti

Any news Ryanheise?

csacchetti avatar Aug 24 '25 13:08 csacchetti

I am currently in the middle of another migration, so I can't upgrade the SDK just yet. If example/lib/main.dart fails as you say, then it should be easy for me to reproduce when I can try it.

ryanheise avatar Aug 24 '25 13:08 ryanheise

There's news, Rayan. Android is pushing for my app to be updated to a target higher than 34, and until this issue is resolved, I'm staying put. Thank you, as always.

csacchetti avatar Sep 25 '25 19:09 csacchetti

Good news, Rayan. With the latest update you made to just_audio 0.10.5, the problem has been resolved. So the problem was related to just_audio and not to audio_service. Keep up the good work.

csacchetti avatar Oct 02 '25 08:10 csacchetti