Skip to content

Commit

Permalink
Prevent ExoPlayer skipping beginning audio.
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanheise committed Dec 19, 2020
1 parent c0c2f7b commit 4322b36
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
private IcyInfo icyInfo;
private IcyHeaders icyHeaders;
private int errorCount;
private AudioAttributes pendingAudioAttributes;

private SimpleExoPlayer player;
private Integer audioSessionId;
Expand Down Expand Up @@ -211,6 +212,10 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
response.put("duration", getDuration() == C.TIME_UNSET ? null : (1000 * getDuration()));
prepareResult.success(response);
prepareResult = null;
if (pendingAudioAttributes != null) {
player.setAudioAttributes(pendingAudioAttributes);
pendingAudioAttributes = null;
}
} else {
transition(ProcessingState.ready);
}
Expand Down Expand Up @@ -500,7 +505,8 @@ private DataSource.Factory buildDataSourceFactory() {

private void load(final MediaSource mediaSource, final long initialPosition, final Integer initialIndex, final Result result) {
this.initialPos = initialPosition;
this.initialIndex = currentIndex = initialIndex;
this.initialIndex = initialIndex;
currentIndex = initialIndex != null ? initialIndex : 0;
switch (processingState) {
case none:
break;
Expand Down Expand Up @@ -535,7 +541,14 @@ private void setAudioAttributes(int contentType, int flags, int usage) {
builder.setFlags(flags);
builder.setUsage(usage);
//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() {
Expand Down Expand Up @@ -652,7 +665,8 @@ public void setVolume(final float volume) {
}

public void setSpeed(final float speed) {
player.setPlaybackParameters(new PlaybackParameters(speed));
if (player.getPlaybackParameters().speed != speed)
player.setPlaybackParameters(new PlaybackParameters(speed));
broadcastPlaybackEvent();
}

Expand Down
68 changes: 49 additions & 19 deletions just_audio/lib/just_audio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class AudioPlayer {
AudioSource _audioSource;
Map<String, AudioSource> _audioSources = {};
bool _disposed = false;
_InitialSeekValues _initialSeekValues;

PlaybackEvent _playbackEvent;
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
Expand All @@ -84,6 +85,7 @@ class AudioPlayer {
BehaviorSubject<Duration> _positionSubject;
bool _automaticallyWaitsToMinimizeStalling = true;
bool _playInterrupted = false;
AndroidAudioAttributes _androidAudioAttributes;

/// Creates an [AudioPlayer]. The player will automatically pause/duck and
/// resume/unduck when audio interruptions occur (e.g. a phone call) or when
Expand Down Expand Up @@ -466,7 +468,7 @@ class AudioPlayer {
Future<Duration> setUrl(
String url, {
Map headers,
Duration initialPosition = Duration.zero,
Duration initialPosition,
bool preload = true,
}) =>
setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers),
Expand All @@ -483,7 +485,7 @@ class AudioPlayer {
/// See [setAudioSource] for a detailed explanation of the options.
Future<Duration> setFilePath(
String filePath, {
Duration initialPosition = Duration.zero,
Duration initialPosition,
bool preload = true,
}) =>
setAudioSource(AudioSource.uri(Uri.file(filePath)),
Expand Down Expand Up @@ -530,14 +532,16 @@ class AudioPlayer {
Future<Duration> setAudioSource(
AudioSource source, {
bool preload = true,
int initialIndex = 0,
Duration initialPosition = Duration.zero,
int initialIndex,
Duration initialPosition,
}) async {
if (_disposed) return null;
// Idea: always keep the idle player around and make it possible
// to switch between idle and active players without disposing either
// one.
_audioSource = null;
_initialSeekValues =
_InitialSeekValues(position: initialPosition, index: initialIndex);
_playbackEventSubject.add(_playbackEvent = PlaybackEvent(
currentIndex: initialIndex, updatePosition: initialPosition));
_broadcastSequence();
Expand Down Expand Up @@ -596,8 +600,6 @@ class AudioPlayer {

Future<Duration> _load(AudioPlayerPlatform platform, AudioSource source,
{Duration initialPosition, int initialIndex}) async {
initialIndex ??= 0;
initialPosition ??= Duration.zero;
try {
if (!kIsWeb && source._requiresHeaders) {
if (_proxy == null) {
Expand Down Expand Up @@ -790,6 +792,7 @@ class AudioPlayer {
/// an audio source has been loaded.
Future<void> seek(final Duration position, {int index}) async {
if (_disposed) return;
_initialSeekValues = null;
switch (processingState) {
case ProcessingState.loading:
return;
Expand Down Expand Up @@ -824,11 +827,17 @@ class AudioPlayer {
AndroidAudioAttributes audioAttributes) async {
if (_disposed) return;
if (audioAttributes == null) return;
await (await _platform).setAndroidAudioAttributes(
SetAndroidAudioAttributesRequest(
contentType: audioAttributes.contentType.index,
flags: audioAttributes.flags.value,
usage: audioAttributes.usage.value));
if (audioAttributes == _androidAudioAttributes) return;
_androidAudioAttributes = audioAttributes;
await _internalSetAndroidAudioAttributes(await _platform, audioAttributes);
}

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
Expand Down Expand Up @@ -922,13 +931,16 @@ class AudioPlayer {
if (active) {
final automaticallyWaitsToMinimizeStalling =
this.automaticallyWaitsToMinimizeStalling;
final setAndroidAudioAttributesRequest =
_idlePlatform.setAndroidAudioAttributesRequest;
if (setAndroidAudioAttributesRequest != null) {
// Only set if there was an unfulfilled pending request.
await platform
.setAndroidAudioAttributes(setAndroidAudioAttributesRequest);
_idlePlatform.setAndroidAudioAttributesRequest = null;
// To avoid a glitch in ExoPlayer, ensure that any requested audio
// attributes are set before loading the audio source.
final audioSession = await AudioSession.instance;
if (_androidAudioAttributes == null) {
_androidAudioAttributes =
audioSession.configuration?.androidAudioAttributes;
}
if (_androidAudioAttributes != null) {
await _internalSetAndroidAudioAttributes(
platform, _androidAudioAttributes);
}
if (!automaticallyWaitsToMinimizeStalling) {
// Only set if different from default.
Expand All @@ -947,8 +959,17 @@ class AudioPlayer {
}
if (audioSource != null) {
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,
initialPosition: position, initialIndex: currentIndex);
initialPosition: initialPosition, initialIndex: initialIndex);
// Wait for loading state to pass.
await processingStateStream
.firstWhere((state) => state != ProcessingState.loading);
Expand Down Expand Up @@ -2054,3 +2075,12 @@ class _IdleAudioPlayer extends AudioPlayerPlatform {
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});
}

0 comments on commit 4322b36

Please sign in to comment.