Add setAndroidAudioAttributes and androidAudioSessionIdStream.
This commit is contained in:
parent
ed9a5ffdce
commit
64d55345f8
|
@ -9,6 +9,8 @@ import com.google.android.exoplayer2.PlaybackParameters;
|
|||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.audio.AudioListener;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.MetadataOutput;
|
||||
import com.google.android.exoplayer2.metadata.icy.IcyHeaders;
|
||||
|
@ -46,7 +48,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class AudioPlayer implements MethodCallHandler, Player.EventListener, MetadataOutput {
|
||||
public class AudioPlayer implements MethodCallHandler, Player.EventListener, AudioListener, MetadataOutput {
|
||||
|
||||
static final String TAG = "AudioPlayer";
|
||||
|
||||
|
@ -76,6 +78,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
|||
private int errorCount;
|
||||
|
||||
private SimpleExoPlayer player;
|
||||
private Integer audioSessionId;
|
||||
private MediaSource mediaSource;
|
||||
private Integer currentIndex;
|
||||
private Map<LoopingMediaSource, MediaSource> loopingChildren = new HashMap<>();
|
||||
|
@ -136,6 +139,15 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
|||
handler.post(bufferWatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioSessionId(int audioSessionId) {
|
||||
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
||||
this.audioSessionId = null;
|
||||
} else {
|
||||
this.audioSessionId = audioSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadata(Metadata metadata) {
|
||||
for (int i = 0; i < metadata.length(); i++) {
|
||||
|
@ -347,6 +359,10 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
|||
case "concatenating.clear":
|
||||
concatenating(args.get(0)).clear(handler, () -> result.success(null));
|
||||
break;
|
||||
case "setAndroidAudioAttributes":
|
||||
setAudioAttributes((Map<?, ?>)args.get(0));
|
||||
result.success(null);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
|
@ -532,9 +548,19 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
|||
player = new SimpleExoPlayer.Builder(context).build();
|
||||
player.addMetadataOutput(this);
|
||||
player.addListener(this);
|
||||
player.addAudioListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAudioAttributes(Map<?, ?> json) {
|
||||
AudioAttributes.Builder builder = new AudioAttributes.Builder();
|
||||
builder.setContentType((Integer)json.get("contentType"));
|
||||
builder.setFlags((Integer)json.get("flags"));
|
||||
builder.setUsage((Integer)json.get("usage"));
|
||||
builder.setAllowedCapturePolicy((Integer)json.get("allowedCapturePolicy"));
|
||||
player.setAudioAttributes(builder.build());
|
||||
}
|
||||
|
||||
private void broadcastPlaybackEvent() {
|
||||
final Map<String, Object> event = new HashMap<String, Object>();
|
||||
event.put("processingState", processingState.ordinal());
|
||||
|
@ -544,6 +570,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Met
|
|||
event.put("icyMetadata", collectIcyMetadata());
|
||||
event.put("duration", duration = getDuration());
|
||||
event.put("currentIndex", currentIndex);
|
||||
event.put("androidAudioSessionId", audioSessionId);
|
||||
|
||||
if (eventSink != null) {
|
||||
eventSink.success(event);
|
||||
|
|
|
@ -138,6 +138,8 @@
|
|||
} else if ([@"concatenating.clear" isEqualToString:call.method]) {
|
||||
[self concatenatingClear:(NSString*)args[0]];
|
||||
result(nil);
|
||||
} else if ([@"setAndroidAudioAttributes" isEqualToString:call.method]) {
|
||||
result(nil);
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ class AudioPlayer {
|
|||
final _sequenceStateSubject = BehaviorSubject<SequenceState>();
|
||||
final _loopModeSubject = BehaviorSubject<LoopMode>();
|
||||
final _shuffleModeEnabledSubject = BehaviorSubject<bool>();
|
||||
final _androidAudioSessionIdSubject = BehaviorSubject<int>();
|
||||
BehaviorSubject<Duration> _positionSubject;
|
||||
bool _automaticallyWaitsToMinimizeStalling = true;
|
||||
|
||||
|
@ -97,6 +98,7 @@ class AudioPlayer {
|
|||
duration: null,
|
||||
icyMetadata: null,
|
||||
currentIndex: null,
|
||||
androidAudioSessionId: null,
|
||||
);
|
||||
_playbackEventSubject.add(_playbackEvent);
|
||||
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
||||
|
@ -121,6 +123,7 @@ class AudioPlayer {
|
|||
? null
|
||||
: IcyMetadata.fromJson(data['icyMetadata']),
|
||||
currentIndex: data['currentIndex'],
|
||||
androidAudioSessionId: data['androidAudioSessionId'],
|
||||
);
|
||||
//print("created event object with state: ${_playbackEvent.state}");
|
||||
return _playbackEvent;
|
||||
|
@ -146,6 +149,10 @@ class AudioPlayer {
|
|||
.map((event) => event.currentIndex)
|
||||
.distinct()
|
||||
.handleError((err, stack) {/* noop */}));
|
||||
_androidAudioSessionIdSubject.addStream(playbackEventStream
|
||||
.map((event) => event.androidAudioSessionId)
|
||||
.distinct()
|
||||
.handleError((err, stack) {/* noop */}));
|
||||
_sequenceStateSubject.addStream(
|
||||
Rx.combineLatest2<List<IndexedAudioSource>, int, SequenceState>(
|
||||
sequenceStream,
|
||||
|
@ -274,6 +281,13 @@ class AudioPlayer {
|
|||
Stream<bool> get shuffleModeEnabledStream =>
|
||||
_shuffleModeEnabledSubject.stream;
|
||||
|
||||
/// The current Android AudioSession ID or `null` if not set.
|
||||
int get androidAudioSessionId => _playbackEvent.androidAudioSessionId;
|
||||
|
||||
/// Broadcasts the current Android AudioSession ID or `null` if not set.
|
||||
Stream<int> get androidAudioSessionIdStream =>
|
||||
_androidAudioSessionIdSubject.stream;
|
||||
|
||||
/// Whether the player should automatically delay playback in order to
|
||||
/// minimize stalling. (iOS 10.0 or later only)
|
||||
bool get automaticallyWaitsToMinimizeStalling =>
|
||||
|
@ -591,6 +605,14 @@ class AudioPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the Android audio attributes for this player. Has no effect on other
|
||||
/// platforms. This will cause a new Android AudioSession ID to be generated.
|
||||
Future<void> setAndroidAudioAttributes(
|
||||
AndroidAudioAttributes audioAttributes) async {
|
||||
await _invokeMethod(
|
||||
'setAndroidAudioAttributes', [audioAttributes.toJson()]);
|
||||
}
|
||||
|
||||
/// Release all resources associated with this player. You must invoke this
|
||||
/// after you are done with the player.
|
||||
Future<void> dispose() async {
|
||||
|
@ -670,6 +692,9 @@ class PlaybackEvent {
|
|||
/// The index of the currently playing item.
|
||||
final int currentIndex;
|
||||
|
||||
/// The current Android AudioSession ID.
|
||||
final int androidAudioSessionId;
|
||||
|
||||
PlaybackEvent({
|
||||
@required this.processingState,
|
||||
@required this.updateTime,
|
||||
|
@ -678,6 +703,7 @@ class PlaybackEvent {
|
|||
@required this.duration,
|
||||
@required this.icyMetadata,
|
||||
@required this.currentIndex,
|
||||
@required this.androidAudioSessionId,
|
||||
});
|
||||
|
||||
PlaybackEvent copyWith({
|
||||
|
@ -689,6 +715,7 @@ class PlaybackEvent {
|
|||
Duration duration,
|
||||
IcyMetadata icyMetadata,
|
||||
UriAudioSource currentIndex,
|
||||
int androidAudioSessionId,
|
||||
}) =>
|
||||
PlaybackEvent(
|
||||
processingState: processingState ?? this.processingState,
|
||||
|
@ -698,6 +725,8 @@ class PlaybackEvent {
|
|||
duration: duration ?? this.duration,
|
||||
icyMetadata: icyMetadata ?? this.icyMetadata,
|
||||
currentIndex: currentIndex ?? this.currentIndex,
|
||||
androidAudioSessionId:
|
||||
androidAudioSessionId ?? this.androidAudioSessionId,
|
||||
);
|
||||
|
||||
@override
|
||||
|
@ -853,6 +882,56 @@ enum IosCategory {
|
|||
multiRoute,
|
||||
}
|
||||
|
||||
/// The Android AudioAttributes to use with a player.
|
||||
class AndroidAudioAttributes {
|
||||
static const FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
|
||||
final AndroidAudioContentType contentType;
|
||||
final int flags;
|
||||
final AndroidAudioUsage usage;
|
||||
final AndroidAudioAllowedCapturePolicy allowedCapturePolicy;
|
||||
|
||||
AndroidAudioAttributes({
|
||||
this.contentType = AndroidAudioContentType.unknown,
|
||||
this.flags = 0,
|
||||
this.usage = AndroidAudioUsage.unknown,
|
||||
this.allowedCapturePolicy = AndroidAudioAllowedCapturePolicy.all,
|
||||
});
|
||||
|
||||
Map toJson() => {
|
||||
'contentType': contentType.index,
|
||||
'flags': flags,
|
||||
'usage': usage.index,
|
||||
// The Android constant values for this enum are 1-indexed
|
||||
'allowedCapturePolicy': allowedCapturePolicy.index + 1,
|
||||
};
|
||||
}
|
||||
|
||||
/// The content type options for [AndroidAudioAttributes].
|
||||
enum AndroidAudioContentType { unknown, speech, music, movie, sonification }
|
||||
|
||||
/// The usage options for [AndroidAudioAttributes].
|
||||
enum AndroidAudioUsage {
|
||||
unknown,
|
||||
media,
|
||||
voiceCommunication,
|
||||
voiceCommunicationSignalling,
|
||||
alarm,
|
||||
notification,
|
||||
notificationRingtone,
|
||||
notificationCommunicationRequest,
|
||||
notificationCommunicationInstant,
|
||||
notificationCommunicationDelayed,
|
||||
notificationEvent,
|
||||
assistanceAccessibility,
|
||||
assistanceNavigationGuidance,
|
||||
assistanceSonification,
|
||||
unused_1,
|
||||
assistant,
|
||||
}
|
||||
|
||||
/// The allowed capture policy options for [AndroidAudioAttributes].
|
||||
enum AndroidAudioAllowedCapturePolicy { all, system, none }
|
||||
|
||||
/// A local proxy HTTP server for making remote GET requests with headers.
|
||||
///
|
||||
/// TODO: Recursively attach headers to items in playlists like m3u8.
|
||||
|
|
|
@ -96,6 +96,8 @@ abstract class JustAudioPlayer {
|
|||
return await concatenatingMove(args[0], args[1], args[2]);
|
||||
case "concatenating.clear":
|
||||
return await concatenatingClear(args[0]);
|
||||
case "setAndroidAudioAttributes":
|
||||
return null;
|
||||
default:
|
||||
throw PlatformException(code: 'Unimplemented');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue