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:
parent
bc766e834c
commit
ee0c4cd7cd
|
@ -1,11 +1,13 @@
|
||||||
package com.ryanheise.just_audio;
|
package com.ryanheise.just_audio;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
import com.google.android.exoplayer2.metadata.MetadataOutput;
|
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.trackselection.TrackSelectionArray;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
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.DefaultHttpDataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
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.Log;
|
||||||
import io.flutter.plugin.common.EventChannel;
|
import io.flutter.plugin.common.EventChannel;
|
||||||
import io.flutter.plugin.common.EventChannel.EventSink;
|
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.MethodChannel.Result;
|
||||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
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 {
|
public class AudioPlayer implements MethodCallHandler, Player.EventListener, MetadataOutput {
|
||||||
static final String TAG = "AudioPlayer";
|
static final String TAG = "AudioPlayer";
|
||||||
|
|
||||||
|
@ -76,12 +75,11 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
||||||
private final Runnable bufferWatcher = new Runnable() {
|
private final Runnable bufferWatcher = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long newBufferedPosition = Math.min(duration, player.getBufferedPosition());
|
long newBufferedPosition = player.getBufferedPosition();
|
||||||
if (newBufferedPosition != bufferedPosition) {
|
if (newBufferedPosition != bufferedPosition) {
|
||||||
bufferedPosition = newBufferedPosition;
|
bufferedPosition = newBufferedPosition;
|
||||||
broadcastPlaybackEvent();
|
broadcastPlaybackEvent();
|
||||||
}
|
}
|
||||||
if (duration > 0 && newBufferedPosition >= duration) return;
|
|
||||||
if (buffering) {
|
if (buffering) {
|
||||||
handler.postDelayed(this, 200);
|
handler.postDelayed(this, 200);
|
||||||
} else if (state == PlaybackState.playing) {
|
} else if (state == PlaybackState.playing) {
|
||||||
|
@ -161,7 +159,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
||||||
switch (playbackState) {
|
switch (playbackState) {
|
||||||
case Player.STATE_READY:
|
case Player.STATE_READY:
|
||||||
if (prepareResult != null) {
|
if (prepareResult != null) {
|
||||||
duration = player.getDuration();
|
duration = getDuration();
|
||||||
justConnected = true;
|
justConnected = true;
|
||||||
transition(PlaybackState.stopped);
|
transition(PlaybackState.stopped);
|
||||||
prepareResult.success(duration);
|
prepareResult.success(duration);
|
||||||
|
@ -301,6 +299,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
||||||
event.add(updateTime = System.currentTimeMillis());
|
event.add(updateTime = System.currentTimeMillis());
|
||||||
event.add(Math.max(updatePosition, bufferedPosition));
|
event.add(Math.max(updatePosition, bufferedPosition));
|
||||||
event.add(collectIcyMetadata());
|
event.add(collectIcyMetadata());
|
||||||
|
event.add(duration = getDuration());
|
||||||
|
|
||||||
if (eventSink != null) {
|
if (eventSink != null) {
|
||||||
eventSink.success(event);
|
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) {
|
private void setError(String errorCode, String errorMsg) {
|
||||||
if (prepareResult != null) {
|
if (prepareResult != null) {
|
||||||
prepareResult.error(errorCode, errorMsg, null);
|
prepareResult.error(errorCode, errorMsg, null);
|
||||||
|
|
|
@ -1,41 +1,62 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
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:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "1.0.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.2"
|
||||||
clock:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: clock
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
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:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -43,13 +64,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.2"
|
version: "0.1.2"
|
||||||
fake_async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fake_async
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -65,13 +79,20 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
just_audio:
|
just_audio:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.1.6"
|
version: "0.1.10"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -92,7 +113,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.6.4"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -100,6 +121,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
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:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -107,13 +142,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.1"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: rxdart
|
name: rxdart
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.24.0"
|
version: "0.24.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -125,7 +167,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.5.5"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -160,7 +202,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.15"
|
version: "0.2.11"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -175,6 +217,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.8"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.5.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.6.0 <3.0.0"
|
dart: ">=2.6.0 <3.0.0"
|
||||||
flutter: ">=1.12.8 <2.0.0"
|
flutter: ">=1.12.8 <2.0.0"
|
||||||
|
|
|
@ -74,8 +74,6 @@ class AudioPlayer {
|
||||||
|
|
||||||
final int _id;
|
final int _id;
|
||||||
|
|
||||||
Duration _duration;
|
|
||||||
|
|
||||||
Future<Duration> _durationFuture;
|
Future<Duration> _durationFuture;
|
||||||
|
|
||||||
final _durationSubject = BehaviorSubject<Duration>();
|
final _durationSubject = BehaviorSubject<Duration>();
|
||||||
|
@ -131,26 +129,32 @@ class AudioPlayer {
|
||||||
AudioPlayer._internal(this._id) : _channel = _init(_id) {
|
AudioPlayer._internal(this._id) : _channel = _init(_id) {
|
||||||
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
||||||
.receiveBroadcastStream()
|
.receiveBroadcastStream()
|
||||||
.map((data) => _audioPlaybackEvent = AudioPlaybackEvent(
|
.map((data) {
|
||||||
state: AudioPlaybackState.values[data[0]],
|
final duration =
|
||||||
buffering: data[1],
|
Duration(milliseconds: data.length < 7 || data[6] < 0 ? -1 : data[6]);
|
||||||
updatePosition: Duration(milliseconds: data[2]),
|
_durationFuture = Future.value(duration);
|
||||||
updateTime: Duration(milliseconds: data[3]),
|
_durationSubject.add(duration);
|
||||||
bufferedPosition: Duration(milliseconds: data[4]),
|
return _audioPlaybackEvent = AudioPlaybackEvent(
|
||||||
speed: _speed,
|
state: AudioPlaybackState.values[data[0]],
|
||||||
duration: _duration,
|
buffering: data[1],
|
||||||
icyMetadata: data.length < 6 || data[5] == null
|
updatePosition: Duration(milliseconds: data[2]),
|
||||||
? null
|
updateTime: Duration(milliseconds: data[3]),
|
||||||
: IcyMetadata(
|
bufferedPosition: Duration(milliseconds: data[4]),
|
||||||
info: IcyInfo(title: data[5][0][0], url: data[5][0][1]),
|
speed: _speed,
|
||||||
headers: IcyHeaders(
|
duration: duration,
|
||||||
bitrate: data[5][1][0],
|
icyMetadata: data.length < 6 || data[5] == null
|
||||||
genre: data[5][1][1],
|
? null
|
||||||
name: data[5][1][2],
|
: IcyMetadata(
|
||||||
metadataInterval: data[5][1][3],
|
info: IcyInfo(title: data[5][0][0], url: data[5][0][1]),
|
||||||
url: data[5][1][4],
|
headers: IcyHeaders(
|
||||||
isPublic: data[5][1][5])),
|
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(
|
_eventChannelStreamSubscription = _eventChannelStream.listen(
|
||||||
_playbackEventSubject.add,
|
_playbackEventSubject.add,
|
||||||
onError: _playbackEventSubject.addError);
|
onError: _playbackEventSubject.addError);
|
||||||
|
@ -253,11 +257,13 @@ class AudioPlayer {
|
||||||
/// https://somewhere.com/somestream?x=etc#.m3u8
|
/// https://somewhere.com/somestream?x=etc#.m3u8
|
||||||
Future<Duration> setUrl(final String url) async {
|
Future<Duration> setUrl(final String url) async {
|
||||||
try {
|
try {
|
||||||
_durationFuture = _invokeMethod('setUrl', [url])
|
_durationFuture = _invokeMethod('setUrl', [url]).then((ms) =>
|
||||||
.then((ms) => ms == null ? null : Duration(milliseconds: ms));
|
(ms == null || ms < 0)
|
||||||
_duration = await _durationFuture;
|
? const Duration(milliseconds: -1)
|
||||||
_durationSubject.add(_duration);
|
: Duration(milliseconds: ms));
|
||||||
return _duration;
|
final duration = await _durationFuture;
|
||||||
|
_durationSubject.add(duration);
|
||||||
|
return duration;
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
return Future.error(e.message);
|
return Future.error(e.message);
|
||||||
}
|
}
|
||||||
|
@ -294,7 +300,9 @@ class AudioPlayer {
|
||||||
Future<Duration> setClip({Duration start, Duration end}) async {
|
Future<Duration> setClip({Duration start, Duration end}) async {
|
||||||
_durationFuture =
|
_durationFuture =
|
||||||
_invokeMethod('setClip', [start?.inMilliseconds, end?.inMilliseconds])
|
_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;
|
final duration = await _durationFuture;
|
||||||
_durationSubject.add(duration);
|
_durationSubject.add(duration);
|
||||||
return duration;
|
return duration;
|
||||||
|
|
11
pubspec.lock
11
pubspec.lock
|
@ -107,6 +107,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
|
pedantic:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pedantic
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0+1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -134,7 +141,7 @@ packages:
|
||||||
name: rxdart
|
name: rxdart
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.24.0"
|
version: "0.24.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -181,7 +188,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.14"
|
version: "0.2.11"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
Loading…
Reference in New Issue