Better audio management (#10)

* add audio_session package

* `AudioManager` initial design

* try to integrate audio manager

* configure audio session

* `DisposeAware`

disposing if already published causes exception.

* organize

* guard flutter_webrtc calls

* websocket async fix

* use `ConnectionState` instead of `_isClosed` and `isReconnecting`

* change create audio track defaults

* update protos

* protocol 3 speaker updates

* fix exception

* emit `RoomDisconnectedEvent` only once

* fix exception

* keep track of local / remote audio tracks

* explicit types

* manage track state

* re-structure audio management

* change defaults

* unpublish all

* use experimental build

* change defaults

* configuring is optional

* call native `RTCAudioSession.setConfiguration`

* defaults adjustment

* iOS only for now

* use lib 92.4515.07

* clean up

* revert audio options for now

* remove pod source

* rename apple related audio

* `createListener` method

* organize native audio

* `SpeakingChangedEvent` only on `Participant`

* refactoring

* fix ios compile

* fix configure audio only for iOS logic

* minor fix & clean up

* change dispose logic

* format protos

* fix unpublishTrack

* `createListener` into a mixin

* simplify

* use flutter_webrtc master

* unpublish for example

* update ios icon

* android icon

* web icon

* favicon
This commit is contained in:
Hiroshi Horie
2021-10-01 02:47:18 +09:00
committed by GitHub
parent ec17907cdc
commit 001f688729
101 changed files with 1658 additions and 681 deletions
+60 -33
View File
@@ -5,13 +5,14 @@ import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import 'constants.dart';
import 'extensions.dart';
import 'logger.dart';
import 'support/disposable.dart';
import 'types.dart';
import 'utils.dart';
typedef PCTransportOnOffer = void Function(rtc.RTCSessionDescription offer);
/// a wrapper around PeerConnection
class PCTransport {
class PCTransport extends Disposable {
final rtc.RTCPeerConnection pc;
final List<rtc.RTCIceCandidate> _pendingCandidates = [];
bool restartingIce = false;
@@ -20,11 +21,42 @@ class PCTransport {
Function? _cancelDebounce;
// private constructor
PCTransport._(this.pc);
PCTransport._(this.pc) {
//
onDispose(() async {
_cancelDebounce?.call();
_cancelDebounce = null;
// Ensure callbacks won't fire any more
pc.onRenegotiationNeeded = null;
pc.onIceCandidate = null;
pc.onIceConnectionState = null;
pc.onTrack = null;
// Remove all senders
List<rtc.RTCRtpSender> senders = [];
try {
senders = await pc.getSenders();
} catch (_) {
logger.warning('getSenders() failed with error: $_');
}
for (final e in senders) {
try {
await pc.removeTrack(e);
} catch (_) {
logger.warning('removeTrack() failed with error: $_');
}
}
await pc.close();
await pc.dispose();
});
}
static Future<PCTransport> create([RTCConfiguration? rtcConfig]) async {
rtcConfig ??= const RTCConfiguration();
logger.fine('PCTransport creating ${rtcConfig.toMap()}');
logger.fine('[PCTransport] creating ${rtcConfig.toMap()}');
final _ = await rtc.createPeerConnection(rtcConfig.toMap());
return PCTransport._(_);
}
@@ -35,39 +67,19 @@ class PCTransport {
wait: Timeouts.debounce,
);
Future<void> dispose() async {
logger.fine('${objectId} dispose()');
// Ensure debounce won't fire
_cancelDebounce?.call();
_cancelDebounce = null;
// @override
// Future<void> dispose() async {
// super.dispose();
// // Ensure debounce won't fire
// Ensure callbacks won't fire any more
pc.onRenegotiationNeeded = null;
pc.onIceCandidate = null;
pc.onIceConnectionState = null;
pc.onTrack = null;
// Remove all senders
List<rtc.RTCRtpSender> senders = [];
try {
senders = await pc.getSenders();
} catch (_) {
logger.warning('getSenders() failed with error: $_');
}
for (final e in senders) {
try {
await pc.removeTrack(e);
} catch (_) {
logger.warning('removeTrack() failed with error: $_');
}
}
await pc.close();
await pc.dispose();
}
// }
Future<void> setRemoteDescription(rtc.RTCSessionDescription sd) async {
if (isDisposed) {
logger.warning('[$objectId] setRemoteDescription() already disposed');
return;
}
await pc.setRemoteDescription(sd);
for (final candidate in _pendingCandidates) {
@@ -84,6 +96,11 @@ class PCTransport {
}
Future<void> createAndSendOffer([RTCOfferOptions? options]) async {
if (isDisposed) {
logger.warning('[$objectId] createAndSendOffer() already disposed');
return;
}
if (onOffer == null) {
logger.warning('onOffer is null');
return;
@@ -116,6 +133,11 @@ class PCTransport {
}
Future<void> addIceCandidate(rtc.RTCIceCandidate candidate) async {
if (isDisposed) {
logger.warning('[$objectId] addIceCandidate() already disposed');
return;
}
final desc = await getRemoteDescription();
if (desc != null && !restartingIce) {
@@ -127,6 +149,11 @@ class PCTransport {
}
Future<rtc.RTCSessionDescription?> getRemoteDescription() async {
if (isDisposed) {
logger.warning('[$objectId] getRemoteDescription() already disposed');
return null;
}
// Checking agains null doesn't work as intended
// if (pc.iceConnectionState == null) return null;