@@ -81,6 +81,9 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
);
|
);
|
||||||
addTrackPublication(pub);
|
addTrackPublication(pub);
|
||||||
|
|
||||||
|
// did publish
|
||||||
|
await track.onPublish();
|
||||||
|
|
||||||
[events, room.events].emit(LocalTrackPublishedEvent(
|
[events, room.events].emit(LocalTrackPublishedEvent(
|
||||||
participant: this,
|
participant: this,
|
||||||
publication: pub,
|
publication: pub,
|
||||||
@@ -175,6 +178,9 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
);
|
);
|
||||||
addTrackPublication(pub);
|
addTrackPublication(pub);
|
||||||
|
|
||||||
|
// did publish
|
||||||
|
await track.onPublish();
|
||||||
|
|
||||||
[events, room.events].emit(LocalTrackPublishedEvent(
|
[events, room.events].emit(LocalTrackPublishedEvent(
|
||||||
participant: this,
|
participant: this,
|
||||||
publication: pub,
|
publication: pub,
|
||||||
@@ -215,6 +221,9 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
await room.engine.negotiate();
|
await room.engine.negotiate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// did unpublish
|
||||||
|
await track.onUnpublish();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import '../logger.dart';
|
|||||||
import '../support/native.dart';
|
import '../support/native.dart';
|
||||||
import '../support/native_audio.dart';
|
import '../support/native_audio.dart';
|
||||||
import '../support/platform.dart';
|
import '../support/platform.dart';
|
||||||
import 'local/audio.dart';
|
|
||||||
import 'local/local.dart';
|
import 'local/local.dart';
|
||||||
|
import 'remote/remote.dart';
|
||||||
|
|
||||||
enum AudioTrackState {
|
enum AudioTrackState {
|
||||||
none,
|
none,
|
||||||
@@ -17,29 +17,51 @@ enum AudioTrackState {
|
|||||||
typedef ConfigureNativeAudioFunc = Future<NativeAudioConfiguration> Function(
|
typedef ConfigureNativeAudioFunc = Future<NativeAudioConfiguration> Function(
|
||||||
AudioTrackState state);
|
AudioTrackState state);
|
||||||
|
|
||||||
mixin AudioManagementMixin on AudioTrack {
|
// it's possible to set custom function here to customize audio session configuration
|
||||||
// it's possible to set custom function here to customize audio session configuration
|
ConfigureNativeAudioFunc onConfigureNativeAudio =
|
||||||
static ConfigureNativeAudioFunc nativeAudioConfigurationForAudioTrackState =
|
defaultNativeAudioConfigurationFunc;
|
||||||
defaultNativeAudioConfigurationFunc;
|
|
||||||
|
|
||||||
static final _trackCounterLock = sync.Lock();
|
final _trackCounterLock = sync.Lock();
|
||||||
static AudioTrackState audioTrackState = AudioTrackState.none;
|
AudioTrackState _audioTrackState = AudioTrackState.none;
|
||||||
static int _localTrackCount = 0;
|
int _localTrackCount = 0;
|
||||||
static int _remoteTrackCount = 0;
|
int _remoteTrackCount = 0;
|
||||||
|
|
||||||
|
mixin LocalAudioManagementMixin on LocalTrack, AudioTrack {
|
||||||
|
@override
|
||||||
|
Future<bool> onPublish() async {
|
||||||
|
final didUpdate = await super.onPublish();
|
||||||
|
if (didUpdate) {
|
||||||
|
// update counter
|
||||||
|
await _trackCounterLock.synchronized(() async {
|
||||||
|
_localTrackCount++;
|
||||||
|
await _onAudioTrackCountDidChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return didUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> onUnpublish() async {
|
||||||
|
final didUpdate = await super.onUnpublish();
|
||||||
|
if (didUpdate) {
|
||||||
|
// update counter
|
||||||
|
await _trackCounterLock.synchronized(() async {
|
||||||
|
_localTrackCount--;
|
||||||
|
await _onAudioTrackCountDidChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return didUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mixin RemoteAudioManagementMixin on RemoteTrack, AudioTrack {
|
||||||
/// Start playing audio track. On web platform, create an audio element and
|
/// Start playing audio track. On web platform, create an audio element and
|
||||||
/// start playback
|
/// start playback
|
||||||
@override
|
@override
|
||||||
Future<bool> start() async {
|
Future<bool> start() async {
|
||||||
final didStart = await super.start();
|
final didStart = await super.start();
|
||||||
if (didStart) {
|
if (didStart) {
|
||||||
// update counter
|
|
||||||
await _trackCounterLock.synchronized(() async {
|
await _trackCounterLock.synchronized(() async {
|
||||||
if (this is LocalAudioTrack) {
|
_remoteTrackCount++;
|
||||||
_localTrackCount++;
|
|
||||||
} else if (this is! LocalAudioTrack) {
|
|
||||||
_remoteTrackCount++;
|
|
||||||
}
|
|
||||||
await _onAudioTrackCountDidChange();
|
await _onAudioTrackCountDidChange();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -50,59 +72,52 @@ mixin AudioManagementMixin on AudioTrack {
|
|||||||
Future<bool> stop() async {
|
Future<bool> stop() async {
|
||||||
final didStop = await super.stop();
|
final didStop = await super.stop();
|
||||||
if (didStop) {
|
if (didStop) {
|
||||||
// update counter
|
|
||||||
await _trackCounterLock.synchronized(() async {
|
await _trackCounterLock.synchronized(() async {
|
||||||
if (this is LocalAudioTrack) {
|
_remoteTrackCount--;
|
||||||
_localTrackCount--;
|
|
||||||
} else if (this is! LocalAudioTrack) {
|
|
||||||
_remoteTrackCount--;
|
|
||||||
}
|
|
||||||
await _onAudioTrackCountDidChange();
|
await _onAudioTrackCountDidChange();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return didStop;
|
return didStop;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onAudioTrackCountDidChange() async {
|
Future<void> _onAudioTrackCountDidChange() async {
|
||||||
logger.fine('[$runtimeType] onAudioTrackCountDidChange: '
|
logger.fine('onAudioTrackCountDidChange: '
|
||||||
'local: $_localTrackCount, remote: $_remoteTrackCount');
|
'local: $_localTrackCount, remote: $_remoteTrackCount');
|
||||||
|
|
||||||
final newState = _computeAudioTrackState();
|
final newState = _computeAudioTrackState();
|
||||||
|
|
||||||
if (audioTrackState != newState) {
|
if (_audioTrackState != newState) {
|
||||||
audioTrackState = newState;
|
_audioTrackState = newState;
|
||||||
logger.fine('[$runtimeType] didUpdateSate: $audioTrackState');
|
logger.fine('didUpdateSate: $_audioTrackState');
|
||||||
|
|
||||||
NativeAudioConfiguration? config;
|
NativeAudioConfiguration? config;
|
||||||
if (lkPlatformIs(PlatformType.iOS)) {
|
if (lkPlatformIs(PlatformType.iOS)) {
|
||||||
// Only iOS for now...
|
// Only iOS for now...
|
||||||
config = await nativeAudioConfigurationForAudioTrackState
|
config = await onConfigureNativeAudio.call(_audioTrackState);
|
||||||
.call(audioTrackState);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
logger.fine(
|
logger.fine('configuring for ${_audioTrackState} using ${config}...');
|
||||||
'[$runtimeType] configuring for ${audioTrackState} using ${config}...');
|
try {
|
||||||
try {
|
await Native.configureAudio(config);
|
||||||
await Native.configureAudio(config);
|
} catch (error) {
|
||||||
} catch (error) {
|
logger.warning('failed to configure ${error}');
|
||||||
logger.warning('[$runtimeType] Failed to configure ${error}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static AudioTrackState _computeAudioTrackState() {
|
AudioTrackState _computeAudioTrackState() {
|
||||||
if (_localTrackCount > 0 && _remoteTrackCount == 0) {
|
if (_localTrackCount > 0 && _remoteTrackCount == 0) {
|
||||||
return AudioTrackState.localOnly;
|
return AudioTrackState.localOnly;
|
||||||
} else if (_localTrackCount == 0 && _remoteTrackCount > 0) {
|
} else if (_localTrackCount == 0 && _remoteTrackCount > 0) {
|
||||||
return AudioTrackState.remoteOnly;
|
return AudioTrackState.remoteOnly;
|
||||||
} else if (_localTrackCount > 0 && _remoteTrackCount > 0) {
|
} else if (_localTrackCount > 0 && _remoteTrackCount > 0) {
|
||||||
return AudioTrackState.localAndRemote;
|
return AudioTrackState.localAndRemote;
|
||||||
}
|
|
||||||
// Default
|
|
||||||
return AudioTrackState.none;
|
|
||||||
}
|
}
|
||||||
|
// Default
|
||||||
|
return AudioTrackState.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<NativeAudioConfiguration> defaultNativeAudioConfigurationFunc(
|
Future<NativeAudioConfiguration> defaultNativeAudioConfigurationFunc(
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import '../audio_management.dart';
|
|||||||
import '../options.dart';
|
import '../options.dart';
|
||||||
import 'local.dart';
|
import 'local.dart';
|
||||||
|
|
||||||
class LocalAudioTrack extends LocalTrack with AudioTrack, AudioManagementMixin {
|
class LocalAudioTrack extends LocalTrack
|
||||||
|
with AudioTrack, LocalAudioManagementMixin {
|
||||||
// Options used for this track
|
// Options used for this track
|
||||||
@override
|
@override
|
||||||
covariant AudioCaptureOptions currentOptions;
|
covariant AudioCaptureOptions currentOptions;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../../events.dart';
|
import '../../events.dart';
|
||||||
import '../../exceptions.dart';
|
import '../../exceptions.dart';
|
||||||
|
import '../../extensions.dart';
|
||||||
import '../../logger.dart';
|
import '../../logger.dart';
|
||||||
import '../../participant/remote.dart';
|
import '../../participant/remote.dart';
|
||||||
import '../../proto/livekit_models.pb.dart' as lk_models;
|
import '../../proto/livekit_models.pb.dart' as lk_models;
|
||||||
@@ -46,6 +46,9 @@ abstract class LocalTrack extends Track {
|
|||||||
/// Options used for this track
|
/// Options used for this track
|
||||||
abstract LocalTrackOptions currentOptions;
|
abstract LocalTrackOptions currentOptions;
|
||||||
|
|
||||||
|
bool _published = false;
|
||||||
|
bool get isPublished => _published;
|
||||||
|
|
||||||
LocalTrack(
|
LocalTrack(
|
||||||
String name,
|
String name,
|
||||||
lk_models.TrackType kind,
|
lk_models.TrackType kind,
|
||||||
@@ -170,4 +173,32 @@ abstract class LocalTrack extends Track {
|
|||||||
// mark as started
|
// mark as started
|
||||||
await start();
|
await start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
@mustCallSuper
|
||||||
|
Future<bool> onPublish() async {
|
||||||
|
if (_published) {
|
||||||
|
// already published
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.fine('$objectId.publish()');
|
||||||
|
|
||||||
|
_published = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
@mustCallSuper
|
||||||
|
Future<bool> onUnpublish() async {
|
||||||
|
if (!_published) {
|
||||||
|
// already unpublished
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.fine('$objectId.unpublish()');
|
||||||
|
|
||||||
|
_published = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import '../web/_audio_api.dart' if (dart.library.html) '../web/_audio_html.dart'
|
|||||||
as audio;
|
as audio;
|
||||||
|
|
||||||
class RemoteAudioTrack extends RemoteTrack
|
class RemoteAudioTrack extends RemoteTrack
|
||||||
with AudioTrack, AudioManagementMixin {
|
with AudioTrack, RemoteAudioManagementMixin {
|
||||||
//
|
//
|
||||||
RemoteAudioTrack(
|
RemoteAudioTrack(
|
||||||
String name,
|
String name,
|
||||||
|
|||||||
Reference in New Issue
Block a user