diff --git a/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java b/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java index 581929c..03138f8 100644 --- a/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java +++ b/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java @@ -40,7 +40,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { private volatile PlaybackState state; private long updateTime; private long updatePosition; - + private long bufferedPosition; private long duration; private Long start; private Long end; @@ -51,9 +51,31 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { private Result seekResult; private boolean seekProcessed; private boolean buffering; + private boolean justConnected; private MediaSource mediaSource; - private SimpleExoPlayer player; + private final SimpleExoPlayer player; + private final Handler handler = new Handler(); + private final Runnable bufferWatcher = new Runnable() { + @Override + public void run() { + long newBufferedPosition = Math.min(duration, player.getBufferedPosition()); + if (newBufferedPosition != bufferedPosition) { + bufferedPosition = newBufferedPosition; + broadcastPlaybackEvent(); + } + if (duration > 0 && newBufferedPosition >= duration) return; + if (buffering) { + handler.postDelayed(this, 200); + } else if (state == PlaybackState.playing) { + handler.postDelayed(this, 500); + } else if (state == PlaybackState.paused) { + handler.postDelayed(this, 1000); + } else if (justConnected) { + handler.postDelayed(this, 1000); + } + } + }; public AudioPlayer(final Registrar registrar, final String id) { this.registrar = registrar; @@ -79,12 +101,18 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { player.addListener(this); } + private void startWatchingBuffer() { + handler.removeCallbacks(bufferWatcher); + handler.post(bufferWatcher); + } + @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { switch (playbackState) { case Player.STATE_READY: if (prepareResult != null) { duration = player.getDuration(); + justConnected = true; prepareResult.success(duration); prepareResult = null; transition(PlaybackState.stopped); @@ -105,6 +133,9 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { if (notifyBuffering && (buffering != this.buffering)) { this.buffering = buffering; broadcastPlaybackEvent(); + if (buffering) { + startWatchingBuffer(); + } } } @@ -194,6 +225,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { event.add(buffering); event.add(updatePosition = getCurrentPosition()); event.add(updateTime = System.currentTimeMillis()); + event.add(Math.max(updatePosition, bufferedPosition)); eventSink.success(event); } @@ -214,6 +246,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { } public void setUrl(final String url, final Result result) throws IOException { + justConnected = false; abortExistingConnection(); prepareResult = result; transition(PlaybackState.connecting); @@ -253,7 +286,9 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener { case stopped: case completed: case paused: + justConnected = false; transition(PlaybackState.playing); + startWatchingBuffer(); player.setPlayWhenReady(true); break; default: diff --git a/ios/Classes/AudioPlayer.m b/ios/Classes/AudioPlayer.m index 0fb3ddf..9aa80a6 100644 --- a/ios/Classes/AudioPlayer.m +++ b/ios/Classes/AudioPlayer.m @@ -124,14 +124,12 @@ _updatePosition = [self getCurrentPosition]; _updateTime = now; _eventSink(@[ - // state @(_state), - // buffering @(_buffering), - // updatePosition @(_updatePosition), - // updateTime @(_updateTime), + // TODO: buffer position + @(_updatePosition), ]); } diff --git a/lib/just_audio.dart b/lib/just_audio.dart index 652556e..0372dab 100644 --- a/lib/just_audio.dart +++ b/lib/just_audio.dart @@ -66,6 +66,7 @@ class AudioPlayer { buffering: false, updatePosition: Duration.zero, updateTime: Duration.zero, + bufferedPosition: Duration.zero, speed: 1.0, duration: Duration.zero, ); @@ -80,6 +81,8 @@ class AudioPlayer { final _bufferingSubject = BehaviorSubject(); + final _bufferedPositionSubject = BehaviorSubject(); + final _fullPlaybackStateSubject = BehaviorSubject(); double _volume = 1.0; @@ -102,6 +105,7 @@ class AudioPlayer { buffering: data[1], updatePosition: Duration(milliseconds: data[2]), updateTime: Duration(milliseconds: data[3]), + bufferedPosition: Duration(milliseconds: data[4]), speed: _speed, duration: _duration, )); @@ -111,6 +115,8 @@ class AudioPlayer { .addStream(playbackEventStream.map((state) => state.state).distinct()); _bufferingSubject.addStream( playbackEventStream.map((state) => state.buffering).distinct()); + _bufferedPositionSubject.addStream( + playbackEventStream.map((state) => state.bufferedPosition).distinct()); _fullPlaybackStateSubject.addStream( Rx.combineLatest2( playbackStateStream, @@ -145,6 +151,10 @@ class AudioPlayer { /// A stream of buffering state changes. Stream get bufferingStream => _bufferingSubject.stream; + /// A stream of buffered positions. + Stream get bufferedPositionStream => + _bufferedPositionSubject.stream; + /// A stream of [FullAudioPlaybackState]s. Stream get fullPlaybackStateStream => _fullPlaybackStateSubject.stream; @@ -325,6 +335,9 @@ class AudioPlaybackEvent { /// The position at [updateTime]. final Duration updatePosition; + /// The buffer position. + final Duration bufferedPosition; + /// The playback speed. final double speed; @@ -336,6 +349,7 @@ class AudioPlaybackEvent { @required this.buffering, @required this.updateTime, @required this.updatePosition, + @required this.bufferedPosition, @required this.speed, @required this.duration, });