ryanheise / just_audio

Audio Player
1.05k stars 675 forks source link

The built-in player does not skip to the next song, even using the same AudioPlayer #865

Closed giovannilima262 closed 2 years ago

giovannilima262 commented 2 years ago

Which API doesn't behave as documented, and how does it misbehave? When using the built-in player and switching from one song to another, it disappears for half a second. On android this happens infrequently, but on IOS this transition happens more often. It's probably less than 1 s

Obs: The situation only occurs when the app is in background mode or with the phone locked

Minimal reproduction project

  1. The example In the example below as soon as the skipToNext is called a new mediaItem is added to the BehaviorSubject which is in the AudioPlayerHandler. And the _audioPlayer instance has its new value set (setAsset) without the stop being called.
_loadUrl({
    @required String urlString,
  }) async {
    print('LOAR URL AUDIO SOURCE...');
    _isPlaying = true;
    await _audioPlayer.setAsset(localExperienceElement?.mediaPath);
    _audioPlayer.play();
}
**<initAudioHandle>**
_mediaItem = MediaItem(
      id:mediaPath
      title: Formatting.multiLanguageStr(
        context,
        experienceElement.multiLanguageName,
      ),
      artist: guideName,
      duration: mediaDuration
      artUri: Uri.parse(imageUrl),
    );
    if (_audioPlayerHandler == null) {
      _audioPlayerHandler = AudioPlayerHandler(
        item: _mediaItem,
        audioPlayerService: this,
      );
    } else {
      _audioPlayerHandler.addMediaItem(_mediaItem);
    }
    if (_audioHandler == null) {
      _audioHandler = await AudioService.init(
        builder: () => _audioPlayerHandler,
        config: AudioServiceConfig(
          androidNotificationChannelId: 'com.app.channel.audio',
          androidNotificationChannelName: 'Audio Example',
          androidNotificationOngoing: true,
          androidNotificationIcon: 'drawable/ic_stat_ic_notification',
        ),
      );
    }
**<AudioPlayerHandler.class>**

import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';

import 'package:just_audio/just_audio.dart';
import 'package:mobile_app/Services/AudioPlayerService.dart';

class MediaState {
  final MediaItem mediaItem;
  final Duration position;

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

class PositionData {
  final Duration position;
  final Duration bufferedPosition;
  final Duration duration;

  PositionData(this.position, this.bufferedPosition, this.duration);
}

/// An [AudioHandler] for playing a single item.
class AudioPlayerHandler extends BaseAudioHandler with SeekHandler {
  AudioPlayerService _audioPlayerService;

  /// Initialise our audio handler.
  AudioPlayerHandler({
    @required MediaItem item,
    @required AudioPlayerService audioPlayerService,
  }) {
    _audioPlayerService = audioPlayerService;
    // 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...
    _audioPlayerService.audioPlayer.playbackEventStream
        .map(_transformEvent)
        .pipe(playbackState);
    // ... and also the current media item via mediaItem.
    mediaItem.add(item);
  }

  void addMediaItem(MediaItem item) {
    mediaItem.add(item);
  }

  // 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() => _audioPlayerService.resumeOrPause(callHandle: false);

  @override
  Future<void> pause() => _audioPlayerService.resumeOrPause(callHandle: false);

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

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

  @override
  Future<void> skipToNext() => _audioPlayerService.skipToNext(isClick: true);

  @override
  Future<void> skipToPrevious() =>
      _audioPlayerService.skipToPrevious(isClick: true);

  // TODO replacement by the like
  @override
  Future<void> setShuffleMode(AudioServiceShuffleMode shuffleMode) async {
    _audioPlayerService.updateLikeExperienceElement();
  }

  /// 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) {
    AudioPlayer player = _audioPlayerService.audioPlayer;

    return PlaybackState(
      controls: [
        _audioPlayerService.isLike
            ? MediaControl(
                androidIcon: 'drawable/like_on',
                label: 'Like',
                action: MediaAction.setShuffleMode,
              )
            : MediaControl(
                androidIcon: 'drawable/like_off',
                label: 'Like',
                action: MediaAction.setShuffleMode,
              ),
        if (_audioPlayerService.isMusicOrBook) ...[
          MediaControl.skipToPrevious,
        ] else
          MediaControl(
            androidIcon: 'drawable/fast_backward',
            label: 'Rewind',
            action: MediaAction.rewind,
          ),
        if (player.playing) MediaControl.pause else MediaControl.play,
        if (_audioPlayerService.isMusicOrBook) ...[
          MediaControl.skipToNext,
        ] else
          MediaControl(
            androidIcon: 'drawable/fast_forward',
            label: 'Fast Forward',
            action: MediaAction.fastForward,
          )
      ],
      systemActions: const {
        MediaAction.seek,
        MediaAction.setShuffleMode,
        MediaAction.seekForward,
        MediaAction.seekBackward,
        MediaAction.skipToPrevious,
        MediaAction.skipToNext,
      },
      androidCompactActionIndices: [1, 2, 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,
    );
  }
}

To Reproduce (i.e. user steps, not code) Steps to reproduce the behavior:

  1. Play the audio
  2. Click next

Smartphone (please complete the following information):

Flutter SDK version

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.5.1 21G83 darwin-x64, locale pt-BR)
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] VS Code (version 1.73.0)
Scanning for devices is taking a long time...[✓] Connected device (3 available)
[✓] HTTP Host Availability

! Doctor found issues in 1 category.
ryanheise commented 2 years ago

Minimal reproduction project

Which of the 2 options do you think you chose?

ryanheise commented 2 years ago

@giovannilima262 can you please select one of the two options?

giovannilima262 commented 2 years ago

Click next

ryanheise commented 2 years ago

I'll ask again, can you please select one of the two options?

giovannilima262 commented 2 years ago

What options? I did not understand

ryanheise commented 2 years ago

Under the section "Minimal reproduction project" the instructions gave you two options. Did you not see them? Please take a look at the instructions for submitting a new issue as it is explained there.

giovannilima262 commented 2 years ago

I believe this is the case

  1. If the unmodified official example already reproduces the bug, just write "The example".
ryanheise commented 2 years ago

Then please edit your issue to follow the instructions. You have copied a huge amount of code into your issue, but I don't need that code if "The example" already exhibits the issue.

giovannilima262 commented 2 years ago

But do you need to? I tried to describe the problem as best as possible. Need video or something?

ryanheise commented 2 years ago

Yes, I need you to follow the instructions. I'm closing this issue for now. If you agree to edit your issue to conform to the instructions AS REQUIRED, comment below and I'll reopen the issue.

github-actions[bot] commented 1 year ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with just_audio.