Add completed state
This commit is contained in:
parent
ff20386e96
commit
dfd1a193ed
|
@ -199,8 +199,8 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUrl(final String url, final Result result) throws IOException {
|
public void setUrl(final String url, final Result result) throws IOException {
|
||||||
if (state != PlaybackState.none && state != PlaybackState.stopped) {
|
if (state != PlaybackState.none && state != PlaybackState.stopped && state != PlaybackState.completed) {
|
||||||
throw new IllegalStateException("Can call setUrl only from none and stopped states");
|
throw new IllegalStateException("Can call setUrl only from none/stopped/completed states");
|
||||||
}
|
}
|
||||||
transition(PlaybackState.connecting);
|
transition(PlaybackState.connecting);
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
@ -231,6 +231,7 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
this.untilPosition = untilPosition;
|
this.untilPosition = untilPosition;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case stopped:
|
case stopped:
|
||||||
|
case completed:
|
||||||
ensureStopped();
|
ensureStopped();
|
||||||
transition(PlaybackState.playing);
|
transition(PlaybackState.playing);
|
||||||
playThread = new PlayThread();
|
playThread = new PlayThread();
|
||||||
|
@ -243,7 +244,7 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Can call play only from stopped and paused states (" + state + ")");
|
throw new IllegalStateException("Can call play only from stopped, completed and paused states (" + state + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +277,9 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case stopped:
|
case stopped:
|
||||||
break;
|
break;
|
||||||
|
case completed:
|
||||||
|
transition(PlaybackState.stopped);
|
||||||
|
break;
|
||||||
// TODO: Allow stopping from buffered state.
|
// TODO: Allow stopping from buffered state.
|
||||||
case playing:
|
case playing:
|
||||||
case paused:
|
case paused:
|
||||||
|
@ -350,8 +354,8 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (state != PlaybackState.stopped && state != PlaybackState.none) {
|
if (state != PlaybackState.stopped && state != PlaybackState.completed && state != PlaybackState.none) {
|
||||||
throw new IllegalStateException("Can call dispose only from stopped and none states");
|
throw new IllegalStateException("Can call dispose only from stopped/completed/none states");
|
||||||
}
|
}
|
||||||
if (extractor != null) {
|
if (extractor != null) {
|
||||||
ensureStopped();
|
ensureStopped();
|
||||||
|
@ -416,7 +420,7 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
boolean reachedEnd = false;
|
||||||
int encoding = AudioFormat.ENCODING_PCM_16BIT;
|
int encoding = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
int channelFormat = channelCount==1?AudioFormat.CHANNEL_OUT_MONO:AudioFormat.CHANNEL_OUT_STEREO;
|
int channelFormat = channelCount==1?AudioFormat.CHANNEL_OUT_MONO:AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
int minSize = AudioTrack.getMinBufferSize(sampleRate, channelFormat, encoding);
|
int minSize = AudioTrack.getMinBufferSize(sampleRate, channelFormat, encoding);
|
||||||
|
@ -532,6 +536,7 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
} else {
|
} else {
|
||||||
audioTrack.pause();
|
audioTrack.pause();
|
||||||
finishedDecoding = true;
|
finishedDecoding = true;
|
||||||
|
reachedEnd = true;
|
||||||
}
|
}
|
||||||
} else if (untilPosition != null && currentPosition >= untilPosition) {
|
} else if (untilPosition != null && currentPosition >= untilPosition) {
|
||||||
// NOTE: When streaming audio over bluetooth, it clips off
|
// NOTE: When streaming audio over bluetooth, it clips off
|
||||||
|
@ -570,8 +575,9 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
synchronized (monitor) {
|
synchronized (monitor) {
|
||||||
start = 0;
|
start = 0;
|
||||||
untilPosition = null;
|
untilPosition = null;
|
||||||
bgTransition(PlaybackState.stopped);
|
bgTransition(reachedEnd ? PlaybackState.completed : PlaybackState.stopped);
|
||||||
extractor.seekTo(0L, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
extractor.seekTo(0L, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
||||||
|
handler.post(() -> broadcastPlaybackEvent());
|
||||||
playThread = null;
|
playThread = null;
|
||||||
monitor.notifyAll();
|
monitor.notifyAll();
|
||||||
}
|
}
|
||||||
|
@ -624,7 +630,8 @@ public class AudioPlayer implements MethodCallHandler {
|
||||||
paused,
|
paused,
|
||||||
playing,
|
playing,
|
||||||
buffering,
|
buffering,
|
||||||
connecting
|
connecting,
|
||||||
|
completed
|
||||||
}
|
}
|
||||||
|
|
||||||
class SeekRequest {
|
class SeekRequest {
|
||||||
|
|
|
@ -12,5 +12,6 @@ enum PlaybackState {
|
||||||
paused,
|
paused,
|
||||||
playing,
|
playing,
|
||||||
buffering,
|
buffering,
|
||||||
connecting
|
connecting,
|
||||||
|
completed
|
||||||
};
|
};
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
queue:nil
|
queue:nil
|
||||||
usingBlock:^(NSNotification* note) {
|
usingBlock:^(NSNotification* note) {
|
||||||
NSLog(@"Reached play end time");
|
NSLog(@"Reached play end time");
|
||||||
[self stop];
|
[self complete];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
if (_player) {
|
if (_player) {
|
||||||
|
@ -258,6 +258,14 @@
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)complete {
|
||||||
|
[_player pause];
|
||||||
|
[_player seekToTime:CMTimeMake(0, 1000)
|
||||||
|
completionHandler:^(BOOL finished) {
|
||||||
|
[self setPlaybackState:completed];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setVolume:(float)volume {
|
- (void)setVolume:(float)volume {
|
||||||
[_player setVolume:volume];
|
[_player setVolume:volume];
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,7 @@ import 'package:rxdart/rxdart.dart';
|
||||||
///
|
///
|
||||||
/// * [AudioPlaybackState.none]: immediately after instantiation.
|
/// * [AudioPlaybackState.none]: immediately after instantiation.
|
||||||
/// * [AudioPlaybackState.stopped]: eventually after [setUrl], [setFilePath] or
|
/// * [AudioPlaybackState.stopped]: eventually after [setUrl], [setFilePath] or
|
||||||
/// [setAsset] completes, immediately after [stop], and immediately after
|
/// [setAsset] completes, and immediately after [stop].
|
||||||
/// playback naturally reaches the end of the media.
|
|
||||||
/// * [AudioPlaybackState.paused]: after [pause] and after reaching the end of
|
/// * [AudioPlaybackState.paused]: after [pause] and after reaching the end of
|
||||||
/// the requested [play] segment.
|
/// the requested [play] segment.
|
||||||
/// * [AudioPlaybackState.playing]: after [play] and after sufficiently
|
/// * [AudioPlaybackState.playing]: after [play] and after sufficiently
|
||||||
|
@ -40,6 +39,8 @@ import 'package:rxdart/rxdart.dart';
|
||||||
/// during normal playback when the next buffer is not ready to be played.
|
/// during normal playback when the next buffer is not ready to be played.
|
||||||
/// * [AudioPlaybackState.connecting]: immediately after [setUrl],
|
/// * [AudioPlaybackState.connecting]: immediately after [setUrl],
|
||||||
/// [setFilePath] and [setAsset] while waiting for the media to load.
|
/// [setFilePath] and [setAsset] while waiting for the media to load.
|
||||||
|
/// * [AudioPlaybackState.completed]: immediately after playback reaches the
|
||||||
|
/// end of the media.
|
||||||
///
|
///
|
||||||
/// Additionally, after a [seek] request completes, the state will return to
|
/// Additionally, after a [seek] request completes, the state will return to
|
||||||
/// whatever state the player was in prior to the seek request.
|
/// whatever state the player was in prior to the seek request.
|
||||||
|
@ -128,7 +129,12 @@ class AudioPlayer {
|
||||||
/// The current speed of the player.
|
/// The current speed of the player.
|
||||||
double get speed => _speed;
|
double get speed => _speed;
|
||||||
|
|
||||||
/// Loads audio media from a URL and returns the duration of that audio.
|
/// Loads audio media from a URL and returns the duration of that audio. It
|
||||||
|
/// is legal to invoke this method only from one of the following states:
|
||||||
|
///
|
||||||
|
/// * [AudioPlaybackState.none]
|
||||||
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
Future<Duration> setUrl(final String url) async {
|
Future<Duration> setUrl(final String url) async {
|
||||||
_durationFuture =
|
_durationFuture =
|
||||||
_invokeMethod('setUrl', [url]).then((ms) => Duration(milliseconds: ms));
|
_invokeMethod('setUrl', [url]).then((ms) => Duration(milliseconds: ms));
|
||||||
|
@ -137,11 +143,21 @@ class AudioPlayer {
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads audio media from a file and returns the duration of that audio.
|
/// Loads audio media from a file and returns the duration of that audio. It
|
||||||
|
/// is legal to invoke this method only from one of the following states:
|
||||||
|
///
|
||||||
|
/// * [AudioPlaybackState.none]
|
||||||
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
Future<Duration> setFilePath(final String filePath) =>
|
Future<Duration> setFilePath(final String filePath) =>
|
||||||
setUrl('file://$filePath');
|
setUrl('file://$filePath');
|
||||||
|
|
||||||
/// Loads audio media from an asset and returns the duration of that audio.
|
/// Loads audio media from an asset and returns the duration of that audio.
|
||||||
|
/// It is legal to invoke this method only from one of the following states:
|
||||||
|
///
|
||||||
|
/// * [AudioPlaybackState.none]
|
||||||
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
Future<Duration> setAsset(final String assetPath) async {
|
Future<Duration> setAsset(final String assetPath) async {
|
||||||
final file = await _cacheFile;
|
final file = await _cacheFile;
|
||||||
if (!file.existsSync()) {
|
if (!file.existsSync()) {
|
||||||
|
@ -161,6 +177,7 @@ class AudioPlayer {
|
||||||
/// invoke this method only from one of the following states:
|
/// invoke this method only from one of the following states:
|
||||||
///
|
///
|
||||||
/// * [AudioPlaybackState.stopped]
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
/// * [AudioPlaybackState.paused]
|
/// * [AudioPlaybackState.paused]
|
||||||
Future<void> play({final Duration untilPosition}) async {
|
Future<void> play({final Duration untilPosition}) async {
|
||||||
StreamSubscription subscription;
|
StreamSubscription subscription;
|
||||||
|
@ -194,6 +211,7 @@ class AudioPlayer {
|
||||||
/// * [AudioPlaybackState.playing]
|
/// * [AudioPlaybackState.playing]
|
||||||
/// * [AudioPlaybackState.paused]
|
/// * [AudioPlaybackState.paused]
|
||||||
/// * [AudioPlaybackState.stopped]
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
await _invokeMethod('stop');
|
await _invokeMethod('stop');
|
||||||
}
|
}
|
||||||
|
@ -210,14 +228,20 @@ class AudioPlayer {
|
||||||
await _invokeMethod('setSpeed', [speed]);
|
await _invokeMethod('setSpeed', [speed]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seeks to a particular position. It is legal to invoke this method
|
/// Seeks to a particular position. It is legal to invoke this method from
|
||||||
/// from any state except for [AudioPlaybackState.none].
|
/// any state except for [AudioPlaybackState.none] and
|
||||||
|
/// [AudioPlaybackState.connecting].
|
||||||
Future<void> seek(final Duration position) async {
|
Future<void> seek(final Duration position) async {
|
||||||
await _invokeMethod('seek', [position.inMilliseconds]);
|
await _invokeMethod('seek', [position.inMilliseconds]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Release all resources associated with this player. You must invoke
|
/// Release all resources associated with this player. You must invoke this
|
||||||
/// this after you are done with the player.
|
/// after you are done with the player. It is legal to invoke this method
|
||||||
|
/// only from the following states:
|
||||||
|
///
|
||||||
|
/// * [AudioPlaybackState.stopped]
|
||||||
|
/// * [AudioPlaybackState.completed]
|
||||||
|
/// * [AudioPlaybackState.none]
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
if ((await _cacheFile).existsSync()) {
|
if ((await _cacheFile).existsSync()) {
|
||||||
(await _cacheFile).deleteSync();
|
(await _cacheFile).deleteSync();
|
||||||
|
@ -275,4 +299,5 @@ enum AudioPlaybackState {
|
||||||
playing,
|
playing,
|
||||||
buffering,
|
buffering,
|
||||||
connecting,
|
connecting,
|
||||||
|
completed,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue