Prevent ExoPlayer skipping beginning audio.

This commit is contained in:
Ryan Heise 2020-12-19 13:14:35 +11:00
parent c0c2f7b592
commit 4322b36518
2 changed files with 66 additions and 22 deletions

View File

@ -75,6 +75,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
private IcyInfo icyInfo; private IcyInfo icyInfo;
private IcyHeaders icyHeaders; private IcyHeaders icyHeaders;
private int errorCount; private int errorCount;
private AudioAttributes pendingAudioAttributes;
private SimpleExoPlayer player; private SimpleExoPlayer player;
private Integer audioSessionId; private Integer audioSessionId;
@ -211,6 +212,10 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
response.put("duration", getDuration() == C.TIME_UNSET ? null : (1000 * getDuration())); response.put("duration", getDuration() == C.TIME_UNSET ? null : (1000 * getDuration()));
prepareResult.success(response); prepareResult.success(response);
prepareResult = null; prepareResult = null;
if (pendingAudioAttributes != null) {
player.setAudioAttributes(pendingAudioAttributes);
pendingAudioAttributes = null;
}
} else { } else {
transition(ProcessingState.ready); transition(ProcessingState.ready);
} }
@ -500,7 +505,8 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
private void load(final MediaSource mediaSource, final long initialPosition, final Integer initialIndex, final Result result) { private void load(final MediaSource mediaSource, final long initialPosition, final Integer initialIndex, final Result result) {
this.initialPos = initialPosition; this.initialPos = initialPosition;
this.initialIndex = currentIndex = initialIndex; this.initialIndex = initialIndex;
currentIndex = initialIndex != null ? initialIndex : 0;
switch (processingState) { switch (processingState) {
case none: case none:
break; break;
@ -535,7 +541,14 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
builder.setFlags(flags); builder.setFlags(flags);
builder.setUsage(usage); builder.setUsage(usage);
//builder.setAllowedCapturePolicy((Integer)json.get("allowedCapturePolicy")); //builder.setAllowedCapturePolicy((Integer)json.get("allowedCapturePolicy"));
player.setAudioAttributes(builder.build()); AudioAttributes audioAttributes = builder.build();
if (processingState == ProcessingState.loading) {
// audio attributes should be set either before or after loading to
// avoid an ExoPlayer glitch.
pendingAudioAttributes = audioAttributes;
} else {
player.setAudioAttributes(audioAttributes);
}
} }
private void broadcastPlaybackEvent() { private void broadcastPlaybackEvent() {
@ -652,7 +665,8 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
} }
public void setSpeed(final float speed) { public void setSpeed(final float speed) {
player.setPlaybackParameters(new PlaybackParameters(speed)); if (player.getPlaybackParameters().speed != speed)
player.setPlaybackParameters(new PlaybackParameters(speed));
broadcastPlaybackEvent(); broadcastPlaybackEvent();
} }

View File

@ -61,6 +61,7 @@ class AudioPlayer {
AudioSource _audioSource; AudioSource _audioSource;
Map<String, AudioSource> _audioSources = {}; Map<String, AudioSource> _audioSources = {};
bool _disposed = false; bool _disposed = false;
_InitialSeekValues _initialSeekValues;
PlaybackEvent _playbackEvent; PlaybackEvent _playbackEvent;
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true); final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
@ -84,6 +85,7 @@ class AudioPlayer {
BehaviorSubject<Duration> _positionSubject; BehaviorSubject<Duration> _positionSubject;
bool _automaticallyWaitsToMinimizeStalling = true; bool _automaticallyWaitsToMinimizeStalling = true;
bool _playInterrupted = false; bool _playInterrupted = false;
AndroidAudioAttributes _androidAudioAttributes;
/// Creates an [AudioPlayer]. The player will automatically pause/duck and /// Creates an [AudioPlayer]. The player will automatically pause/duck and
/// resume/unduck when audio interruptions occur (e.g. a phone call) or when /// resume/unduck when audio interruptions occur (e.g. a phone call) or when
@ -466,7 +468,7 @@ class AudioPlayer {
Future<Duration> setUrl( Future<Duration> setUrl(
String url, { String url, {
Map headers, Map headers,
Duration initialPosition = Duration.zero, Duration initialPosition,
bool preload = true, bool preload = true,
}) => }) =>
setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers), setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers),
@ -483,7 +485,7 @@ class AudioPlayer {
/// See [setAudioSource] for a detailed explanation of the options. /// See [setAudioSource] for a detailed explanation of the options.
Future<Duration> setFilePath( Future<Duration> setFilePath(
String filePath, { String filePath, {
Duration initialPosition = Duration.zero, Duration initialPosition,
bool preload = true, bool preload = true,
}) => }) =>
setAudioSource(AudioSource.uri(Uri.file(filePath)), setAudioSource(AudioSource.uri(Uri.file(filePath)),
@ -530,14 +532,16 @@ class AudioPlayer {
Future<Duration> setAudioSource( Future<Duration> setAudioSource(
AudioSource source, { AudioSource source, {
bool preload = true, bool preload = true,
int initialIndex = 0, int initialIndex,
Duration initialPosition = Duration.zero, Duration initialPosition,
}) async { }) async {
if (_disposed) return null; if (_disposed) return null;
// Idea: always keep the idle player around and make it possible // Idea: always keep the idle player around and make it possible
// to switch between idle and active players without disposing either // to switch between idle and active players without disposing either
// one. // one.
_audioSource = null; _audioSource = null;
_initialSeekValues =
_InitialSeekValues(position: initialPosition, index: initialIndex);
_playbackEventSubject.add(_playbackEvent = PlaybackEvent( _playbackEventSubject.add(_playbackEvent = PlaybackEvent(
currentIndex: initialIndex, updatePosition: initialPosition)); currentIndex: initialIndex, updatePosition: initialPosition));
_broadcastSequence(); _broadcastSequence();
@ -596,8 +600,6 @@ class AudioPlayer {
Future<Duration> _load(AudioPlayerPlatform platform, AudioSource source, Future<Duration> _load(AudioPlayerPlatform platform, AudioSource source,
{Duration initialPosition, int initialIndex}) async { {Duration initialPosition, int initialIndex}) async {
initialIndex ??= 0;
initialPosition ??= Duration.zero;
try { try {
if (!kIsWeb && source._requiresHeaders) { if (!kIsWeb && source._requiresHeaders) {
if (_proxy == null) { if (_proxy == null) {
@ -790,6 +792,7 @@ class AudioPlayer {
/// an audio source has been loaded. /// an audio source has been loaded.
Future<void> seek(final Duration position, {int index}) async { Future<void> seek(final Duration position, {int index}) async {
if (_disposed) return; if (_disposed) return;
_initialSeekValues = null;
switch (processingState) { switch (processingState) {
case ProcessingState.loading: case ProcessingState.loading:
return; return;
@ -824,11 +827,17 @@ class AudioPlayer {
AndroidAudioAttributes audioAttributes) async { AndroidAudioAttributes audioAttributes) async {
if (_disposed) return; if (_disposed) return;
if (audioAttributes == null) return; if (audioAttributes == null) return;
await (await _platform).setAndroidAudioAttributes( if (audioAttributes == _androidAudioAttributes) return;
SetAndroidAudioAttributesRequest( _androidAudioAttributes = audioAttributes;
contentType: audioAttributes.contentType.index, await _internalSetAndroidAudioAttributes(await _platform, audioAttributes);
flags: audioAttributes.flags.value, }
usage: audioAttributes.usage.value));
Future<void> _internalSetAndroidAudioAttributes(AudioPlayerPlatform platform,
AndroidAudioAttributes audioAttributes) async {
await platform.setAndroidAudioAttributes(SetAndroidAudioAttributesRequest(
contentType: audioAttributes.contentType.index,
flags: audioAttributes.flags.value,
usage: audioAttributes.usage.value));
} }
/// Release all resources associated with this player. You must invoke this /// Release all resources associated with this player. You must invoke this
@ -922,13 +931,16 @@ class AudioPlayer {
if (active) { if (active) {
final automaticallyWaitsToMinimizeStalling = final automaticallyWaitsToMinimizeStalling =
this.automaticallyWaitsToMinimizeStalling; this.automaticallyWaitsToMinimizeStalling;
final setAndroidAudioAttributesRequest = // To avoid a glitch in ExoPlayer, ensure that any requested audio
_idlePlatform.setAndroidAudioAttributesRequest; // attributes are set before loading the audio source.
if (setAndroidAudioAttributesRequest != null) { final audioSession = await AudioSession.instance;
// Only set if there was an unfulfilled pending request. if (_androidAudioAttributes == null) {
await platform _androidAudioAttributes =
.setAndroidAudioAttributes(setAndroidAudioAttributesRequest); audioSession.configuration?.androidAudioAttributes;
_idlePlatform.setAndroidAudioAttributesRequest = null; }
if (_androidAudioAttributes != null) {
await _internalSetAndroidAudioAttributes(
platform, _androidAudioAttributes);
} }
if (!automaticallyWaitsToMinimizeStalling) { if (!automaticallyWaitsToMinimizeStalling) {
// Only set if different from default. // Only set if different from default.
@ -947,8 +959,17 @@ class AudioPlayer {
} }
if (audioSource != null) { if (audioSource != null) {
try { try {
Duration initialPosition;
int initialIndex;
if (_initialSeekValues != null) {
initialPosition = _initialSeekValues.position;
initialIndex = _initialSeekValues.index;
} else {
initialPosition = position;
initialIndex = currentIndex;
}
final duration = await _load(platform, _audioSource, final duration = await _load(platform, _audioSource,
initialPosition: position, initialIndex: currentIndex); initialPosition: initialPosition, initialIndex: initialIndex);
// Wait for loading state to pass. // Wait for loading state to pass.
await processingStateStream await processingStateStream
.firstWhere((state) => state != ProcessingState.loading); .firstWhere((state) => state != ProcessingState.loading);
@ -2054,3 +2075,12 @@ class _IdleAudioPlayer extends AudioPlayerPlatform {
return ConcatenatingMoveResponse(); return ConcatenatingMoveResponse();
} }
} }
/// Holds the initial requested position and index for a newly loaded audio
/// source.
class _InitialSeekValues {
final Duration position;
final int index;
_InitialSeekValues({@required this.position, @required this.index});
}