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
This commit is contained in:
LKHO 2020-06-03 18:38:08 +08:00 committed by GitHub
parent bc766e834c
commit ee0c4cd7cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 66 deletions

View File

@ -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);

View File

@ -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"

View File

@ -74,8 +74,6 @@ class AudioPlayer {
final int _id;
Duration _duration;
Future<Duration> _durationFuture;
final _durationSubject = BehaviorSubject<Duration>();
@ -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<Duration> 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<Duration> 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;

View File

@ -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: