From ee0c4cd7cd59aa70e62f15eb563610614d278aff Mon Sep 17 00:00:00 2001 From: LKHO Date: Wed, 3 Jun 2020 18:38:08 +0800 Subject: [PATCH] support dynamic duration (#104) * add duration in PlaybackEvent * emit duration updates on dynamic length sources * Merge branch 'master' of https://github.com/ryanheise/just_audio into pr/android-duration # Conflicts: # lib/just_audio.dart * fix merge * update _durationFuture --- .../com/ryanheise/just_audio/AudioPlayer.java | 33 ++++--- example/pubspec.lock | 95 ++++++++++++++----- lib/just_audio.dart | 64 +++++++------ pubspec.lock | 11 ++- 4 files changed, 137 insertions(+), 66 deletions(-) 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 7bd6110..977e4dc 100644 --- a/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java +++ b/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java @@ -1,11 +1,13 @@ package com.ryanheise.just_audio; +import android.content.Context; +import android.net.Uri; import android.os.Handler; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataOutput; @@ -21,10 +23,15 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.util.Util; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import io.flutter.Log; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel.EventSink; @@ -34,14 +41,6 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.Registrar; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import android.content.Context; -import android.net.Uri; - -import java.util.List; - public class AudioPlayer implements MethodCallHandler, Player.EventListener, MetadataOutput { static final String TAG = "AudioPlayer"; @@ -76,12 +75,11 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met private final Runnable bufferWatcher = new Runnable() { @Override public void run() { - long newBufferedPosition = Math.min(duration, player.getBufferedPosition()); + long newBufferedPosition = 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) { @@ -161,7 +159,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met switch (playbackState) { case Player.STATE_READY: if (prepareResult != null) { - duration = player.getDuration(); + duration = getDuration(); justConnected = true; transition(PlaybackState.stopped); prepareResult.success(duration); @@ -301,6 +299,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met event.add(updateTime = System.currentTimeMillis()); event.add(Math.max(updatePosition, bufferedPosition)); event.add(collectIcyMetadata()); + event.add(duration = getDuration()); if (eventSink != null) { eventSink.success(event); @@ -344,6 +343,14 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met } } + private long getDuration() { + if (state == PlaybackState.none || state == PlaybackState.connecting) { + return C.TIME_UNSET; + } else { + return player.getDuration(); + } + } + private void setError(String errorCode, String errorMsg) { if (prepareResult != null) { prepareResult.error(errorCode, errorMsg, null); diff --git a/example/pubspec.lock b/example/pubspec.lock index 71e6fbc..f2b2bae 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,41 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.4.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "1.0.5" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" + version: "1.1.2" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" + version: "1.14.11" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" cupertino_icons: dependency: "direct main" description: @@ -43,13 +64,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -65,13 +79,20 @@ packages: description: flutter source: sdk version: "0.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" just_audio: dependency: "direct dev" description: path: ".." relative: true source: path - version: "0.1.6" + version: "0.1.10" matcher: dependency: transitive description: @@ -92,7 +113,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.6.4" path_provider: dependency: transitive description: @@ -100,6 +121,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0+1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0" platform: dependency: transitive description: @@ -107,13 +142,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" rxdart: dependency: "direct main" description: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.24.0" + version: "0.24.1" sky_engine: dependency: transitive description: flutter @@ -125,7 +167,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.5.5" stack_trace: dependency: transitive description: @@ -160,7 +202,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.11" typed_data: dependency: transitive description: @@ -175,6 +217,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.8" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "3.5.0" sdks: dart: ">=2.6.0 <3.0.0" flutter: ">=1.12.8 <2.0.0" diff --git a/lib/just_audio.dart b/lib/just_audio.dart index 08043f9..e63e499 100644 --- a/lib/just_audio.dart +++ b/lib/just_audio.dart @@ -74,8 +74,6 @@ class AudioPlayer { final int _id; - Duration _duration; - Future _durationFuture; final _durationSubject = BehaviorSubject(); @@ -131,26 +129,32 @@ class AudioPlayer { AudioPlayer._internal(this._id) : _channel = _init(_id) { _eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id') .receiveBroadcastStream() - .map((data) => _audioPlaybackEvent = AudioPlaybackEvent( - state: AudioPlaybackState.values[data[0]], - buffering: data[1], - updatePosition: Duration(milliseconds: data[2]), - updateTime: Duration(milliseconds: data[3]), - bufferedPosition: Duration(milliseconds: data[4]), - speed: _speed, - duration: _duration, - icyMetadata: data.length < 6 || data[5] == null - ? null - : IcyMetadata( - info: IcyInfo(title: data[5][0][0], url: data[5][0][1]), - headers: IcyHeaders( - bitrate: data[5][1][0], - genre: data[5][1][1], - name: data[5][1][2], - metadataInterval: data[5][1][3], - url: data[5][1][4], - isPublic: data[5][1][5])), - )); + .map((data) { + final duration = + Duration(milliseconds: data.length < 7 || data[6] < 0 ? -1 : data[6]); + _durationFuture = Future.value(duration); + _durationSubject.add(duration); + return _audioPlaybackEvent = AudioPlaybackEvent( + state: AudioPlaybackState.values[data[0]], + buffering: data[1], + updatePosition: Duration(milliseconds: data[2]), + updateTime: Duration(milliseconds: data[3]), + bufferedPosition: Duration(milliseconds: data[4]), + speed: _speed, + duration: duration, + icyMetadata: data.length < 6 || data[5] == null + ? null + : IcyMetadata( + info: IcyInfo(title: data[5][0][0], url: data[5][0][1]), + headers: IcyHeaders( + bitrate: data[5][1][0], + genre: data[5][1][1], + name: data[5][1][2], + metadataInterval: data[5][1][3], + url: data[5][1][4], + isPublic: data[5][1][5])), + ); + }); _eventChannelStreamSubscription = _eventChannelStream.listen( _playbackEventSubject.add, onError: _playbackEventSubject.addError); @@ -253,11 +257,13 @@ class AudioPlayer { /// https://somewhere.com/somestream?x=etc#.m3u8 Future setUrl(final String url) async { try { - _durationFuture = _invokeMethod('setUrl', [url]) - .then((ms) => ms == null ? null : Duration(milliseconds: ms)); - _duration = await _durationFuture; - _durationSubject.add(_duration); - return _duration; + _durationFuture = _invokeMethod('setUrl', [url]).then((ms) => + (ms == null || ms < 0) + ? const Duration(milliseconds: -1) + : Duration(milliseconds: ms)); + final duration = await _durationFuture; + _durationSubject.add(duration); + return duration; } on PlatformException catch (e) { return Future.error(e.message); } @@ -294,7 +300,9 @@ class AudioPlayer { Future setClip({Duration start, Duration end}) async { _durationFuture = _invokeMethod('setClip', [start?.inMilliseconds, end?.inMilliseconds]) - .then((ms) => ms == null ? null : Duration(milliseconds: ms)); + .then((ms) => (ms == null || ms < 0) + ? const Duration(milliseconds: -1) + : Duration(milliseconds: ms)); final duration = await _durationFuture; _durationSubject.add(duration); return duration; diff --git a/pubspec.lock b/pubspec.lock index 2b617d5..cdc912d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -107,6 +107,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0+1" petitparser: dependency: transitive description: @@ -134,7 +141,7 @@ packages: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.24.0" + version: "0.24.1" sky_engine: dependency: transitive description: flutter @@ -181,7 +188,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.14" + version: "0.2.11" typed_data: dependency: transitive description: