Prevent ExoPlayer skipping beginning audio.
This commit is contained in:
parent
c0c2f7b592
commit
4322b36518
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue