Add playback event stream to platform interface.

This commit is contained in:
Ryan Heise 2020-09-26 23:30:17 +10:00
parent ab6c8b2a8a
commit 3a44f844e2
2 changed files with 153 additions and 28 deletions

View File

@ -46,6 +46,7 @@ abstract class JustAudioPlatform extends PlatformInterface {
/// A nested platform interface for communicating with a particular player
/// instance.
abstract class AudioPlayerPlatform {
Stream<PlaybackEventMessage> get playbackEventMessageStream;
Future<LoadResponse> load(LoadRequest request);
Future<PlayResponse> play(PlayRequest request);
Future<PauseResponse> pause(PauseRequest request);
@ -68,6 +69,112 @@ abstract class AudioPlayerPlatform {
ConcatenatingMoveRequest request);
}
class PlaybackEventMessage {
final ProcessingStateMessage processingState;
final DateTime updateTime;
final Duration updatePosition;
final Duration bufferedPosition;
final Duration duration;
final IcyMetadataMessage icyMetadata;
final int currentIndex;
final int androidAudioSessionId;
PlaybackEventMessage({
@required this.processingState,
@required this.updateTime,
@required this.updatePosition,
@required this.bufferedPosition,
@required this.duration,
@required this.icyMetadata,
@required this.currentIndex,
@required this.androidAudioSessionId,
});
static PlaybackEventMessage fromMap(Map<dynamic, dynamic> map) =>
PlaybackEventMessage(
processingState: ProcessingStateMessage.values[map['processingState']],
updateTime: DateTime.fromMillisecondsSinceEpoch(map['updateTime']),
// TODO: Ensure all platforms pass a microsecond value.
updatePosition: Duration(microseconds: map['updatePosition']),
// TODO: Ensure all platforms pass a microsecond value.
bufferedPosition: Duration(microseconds: map['bufferedPosition']),
// TODO: Ensure all platforms pass a microsecond value.
duration: map['duration'] == null || map['duration'] < 0
? null
: Duration(microseconds: map['duration']),
icyMetadata: map['icyMetadata'] == null
? null
: IcyMetadataMessage.fromMap(map['icyMetadata']),
currentIndex: map['currentIndex'],
androidAudioSessionId: map['androidAudioSessionId'],
);
}
enum ProcessingStateMessage {
none,
loading,
buffering,
ready,
completed,
}
class IcyMetadataMessage {
final IcyInfoMessage info;
final IcyHeadersMessage headers;
IcyMetadataMessage({
@required this.info,
@required this.headers,
});
static IcyMetadataMessage fromMap(Map<dynamic, dynamic> json) =>
IcyMetadataMessage(
info:
json['info'] == null ? null : IcyInfoMessage.fromMap(json['info']),
headers: json['headers'] == null
? null
: IcyHeadersMessage.fromMap(json['headers']),
);
}
class IcyInfoMessage {
final String title;
final String url;
IcyInfoMessage({@required this.title, @required this.url});
static IcyInfoMessage fromMap(Map<dynamic, dynamic> json) =>
IcyInfoMessage(title: json['title'], url: json['url']);
}
class IcyHeadersMessage {
final int bitrate;
final String genre;
final String name;
final int metadataInterval;
final String url;
final bool isPublic;
IcyHeadersMessage({
@required this.bitrate,
@required this.genre,
@required this.name,
@required this.metadataInterval,
@required this.url,
@required this.isPublic,
});
static IcyHeadersMessage fromMap(Map<dynamic, dynamic> json) =>
IcyHeadersMessage(
bitrate: json['bitrate'],
genre: json['genre'],
name: json['name'],
metadataInterval: json['metadataInterval'],
url: json['url'],
isPublic: json['isPublic'],
);
}
class InitRequest {
final String id;
@ -344,6 +451,7 @@ class ProgressiveAudioSourceMessage extends UriAudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'progressive',
'id': id,
'uri': uri,
'headers': headers,
@ -359,6 +467,7 @@ class DashAudioSourceMessage extends UriAudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'dash',
'id': id,
'uri': uri,
'headers': headers,
@ -374,6 +483,7 @@ class HlsAudioSourceMessage extends UriAudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'hls',
'id': id,
'uri': uri,
'headers': headers,
@ -392,7 +502,9 @@ class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'concatenating',
'id': id,
// TODO: ensure platform implementation uses this key
'children': children.map((child) => child.toMap()).toList(),
'useLazyPreparation': useLazyPreparation,
};
@ -412,9 +524,13 @@ class ClippingAudioSourceMessage extends IndexedAudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'clipping',
'id': id,
// TODO: ensure platform implementation uses this key
'child': child.toMap(),
// TODO: ensure platform implementation interprets in Us.
'start': start.inMicroseconds,
// TODO: ensure platform implementation interprets in Us.
'end': end.inMicroseconds,
};
}
@ -431,7 +547,9 @@ class LoopingAudioSourceMessage extends AudioSourceMessage {
@override
Map<dynamic, dynamic> toMap() => {
'type': 'looping',
'id': id,
// TODO: ensure platform implementation uses this key
'child': child.toMap(),
'count': count,
};

View File

@ -22,94 +22,101 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform {
MethodChannelAudioPlayer(this.id)
: _channel = MethodChannel('com.ryanheise.just_audio.methods.$id');
@override
Stream<PlaybackEventMessage> get playbackEventMessageStream =>
EventChannel('com.ryanheise.just_audio.events.$id')
.receiveBroadcastStream()
.map((map) => PlaybackEventMessage.fromMap(map));
@override
Future<LoadResponse> load(LoadRequest request) async {
return (await _channel.invokeMethod('load', request?.toMap()))?.fromMap();
return LoadResponse.fromMap(
await _channel.invokeMethod('load', request?.toMap()));
}
@override
Future<PlayResponse> play(PlayRequest request) async {
return (await _channel.invokeMethod('play', request?.toMap()))?.fromMap();
return PlayResponse.fromMap(
await _channel.invokeMethod('play', request?.toMap()));
}
@override
Future<PauseResponse> pause(PauseRequest request) async {
return (await _channel.invokeMethod('pause', request?.toMap()))?.fromMap();
return PauseResponse.fromMap(
await _channel.invokeMethod('pause', request?.toMap()));
}
@override
Future<SetVolumeResponse> setVolume(SetVolumeRequest request) async {
return (await _channel.invokeMethod('setVolume', request?.toMap()))
?.fromMap();
return SetVolumeResponse.fromMap(
await _channel.invokeMethod('setVolume', request?.toMap()));
}
@override
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request) async {
return (await _channel.invokeMethod('setSpeed', request?.toMap()))
?.fromMap();
return SetSpeedResponse.fromMap(
await _channel.invokeMethod('setSpeed', request?.toMap()));
}
@override
Future<SetLoopModeResponse> setLoopMode(SetLoopModeRequest request) async {
return (await _channel.invokeMethod('setLoopMode', request?.toMap()))
?.fromMap();
return SetLoopModeResponse.fromMap(
await _channel.invokeMethod('setLoopMode', request?.toMap()));
}
@override
Future<SetShuffleModeResponse> setShuffleMode(
SetShuffleModeRequest request) async {
return (await _channel.invokeMethod('setShuffleMode', request?.toMap()))
?.fromMap();
return SetShuffleModeResponse.fromMap(
await _channel.invokeMethod('setShuffleMode', request?.toMap()));
}
@override
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
setAutomaticallyWaitsToMinimizeStalling(
SetAutomaticallyWaitsToMinimizeStallingRequest request) async {
return (await _channel.invokeMethod(
'setAutomaticallyWaitsToMinimizeStalling', request?.toMap()))
?.fromMap();
return SetAutomaticallyWaitsToMinimizeStallingResponse.fromMap(
await _channel.invokeMethod(
'setAutomaticallyWaitsToMinimizeStalling', request?.toMap()));
}
@override
Future<SeekResponse> seek(SeekRequest request) async {
return (await _channel.invokeMethod('seek', request?.toMap()))?.fromMap();
return SeekResponse.fromMap(
await _channel.invokeMethod('seek', request?.toMap()));
}
@override
Future<SetAndroidAudioAttributesResponse> setAndroidAudioAttributes(
SetAndroidAudioAttributesRequest request) async {
return (await _channel.invokeMethod(
'setAndroidAudioAttributes', request?.toMap()))
?.fromMap();
return SetAndroidAudioAttributesResponse.fromMap(await _channel
.invokeMethod('setAndroidAudioAttributes', request?.toMap()));
}
@override
Future<DisposeResponse> dispose(DisposeRequest request) async {
return (await _channel.invokeMethod('dispose', request?.toMap()))
?.fromMap();
return DisposeResponse.fromMap(
await _channel.invokeMethod('dispose', request?.toMap()));
}
@override
Future<ConcatenatingInsertAllResponse> concatenatingInsertAll(
ConcatenatingInsertAllRequest request) async {
return (await _channel.invokeMethod(
'concatenatingInsertAll', request?.toMap()))
?.fromMap();
return ConcatenatingInsertAllResponse.fromMap(await _channel.invokeMethod(
'concatenatingInsertAll', request?.toMap()));
}
@override
Future<ConcatenatingRemoveRangeResponse> concatenatingRemoveRange(
ConcatenatingRemoveRangeRequest request) async {
return (await _channel.invokeMethod(
'concatenatingRemoveRange', request?.toMap()))
?.fromMap();
return ConcatenatingRemoveRangeResponse.fromMap(await _channel.invokeMethod(
'concatenatingRemoveRange', request?.toMap()));
}
@override
Future<ConcatenatingMoveResponse> concatenatingMove(
ConcatenatingMoveRequest request) async {
return (await _channel.invokeMethod('concatenatingMove', request?.toMap()))
?.fromMap();
return ConcatenatingMoveResponse.fromMap(
await _channel.invokeMethod('concatenatingMove', request?.toMap()));
}
}