8000 [WIP] - Selecting an audio output (#422) · vkpdeveloper/flutter-webrtc@c2ce476 · GitHub
[go: up one dir, main page]

Skip to content

Commit c2ce476

Browse files
authored
[WIP] - Selecting an audio output (flutter-webrtc#422)
* Initial code * FIX - enumerateDevices cast Example - Add list audiooutputs for native.
1 parent 456acf3 commit c2ce476

9 files changed

+81
-10
lines changed

example/lib/src/get_user_media_sample.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class _GetUserMediaSampleState extends State<GetUserMediaSample> {
2323
MediaRecorder _mediaRecorder;
2424
bool get _isRec => _mediaRecorder != null;
2525

26+
List<MediaDeviceInfo> _mediaDevicesList;
27+
2628
@override
2729
void initState() {
2830
super.initState();
@@ -60,6 +62,7 @@ class _GetUserMediaSampleState extends State<GetUserMediaSample> {
6062

6163
try {
6264
var stream = await navigator.getUserMedia(mediaConstraints);
65+
_mediaDevicesList = await navigator.mediaDevices.enumerateDevices();
6366
_localStream = stream;
6467
_localRenderer.srcObject = _localStream;
6568
} catch (e) {
@@ -172,6 +175,21 @@ class _GetUserMediaSampleState extends State<GetUserMediaSample> {
172175
icon: Icon(_isRec ? Icons.stop : Icons.fiber_manual_record),
173176
onPressed: _isRec ? _stopRecording : _startRecording,
174177
),
178+
PopupMenuButton<String>(
179+
onSelected: _selectAudioOutput,
180+
itemBuilder: (BuildContext context) {
181+
return _mediaDevicesList
182+
.where((device) => device.kind == 'audiooutput')
183+
.map((device) {
184+
if (device.kind == 'audiooutput') {
185+
return PopupMenuItem<String>(
186+
value: device.deviceId,
187+
child: Text(device.label),
188+
);
189+
}
190+
}).toList();
191+
},
192+
),
175193
]
176194
: null,
177195
),
@@ -195,4 +213,8 @@ class _GetUserMediaSampleState extends State<GetUserMediaSample> {
195213
),
196214
);
197215
}
216+
217+
void _selectAudioOutput(String deviceId) {
218+
_localRenderer.audioOutput = deviceId;
219+
}
198220
}

example/lib/src/get_user_media_sample_web.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'dart:html' as html;
44

55
import 'package:flutter/material.dart';
66
import 'package:flutter_webrtc/flutter_webrtc.dart';
7-
import 'package:flutter_webrtc/src/interface/mediadevices.dart';
87

98
/*
109
* getUserMedia sample

lib/flutter_webrtc.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export 'src/helper.dart';
44
export 'src/interface/enums.dart';
55
export 'src/interface/media_stream.dart';
66
export 'src/interface/media_stream_track.dart';
7+
export 'src/interface/mediadevices.dart' hide MediaDevices;
78
export 'src/interface/rtc_data_channel.dart';
89
export 'src/interface/rtc_dtmf_sender.dart';
910
export 'src/interface/rtc_ice_candidate.dart';

lib/src/helper.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ class Helper {
1919
static Future<List<MediaDeviceInfo>> get cameras =>
2020
enumerateDevices('videoinput');
2121

22+
/// Return the available audiooutputs
23+
///
24+
/// Note: Make sure to call this gettet after
25+
/// navigator.mediaDevices.getUserMedia(), otherwise the devices will not be
26+
/// listed.
27+
static Future<List<MediaDeviceInfo>> get audiooutputs =>
28+
enumerateDevices('audiooutput');
29+
2230
/// To select a a specific camera, you need to set constraints
2331
/// eg.
2432
/// constraints = {

lib/src/interface/rtc_video_renderer.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ abstract class VideoRenderer extends ValueNotifier<RTCVideoValue> {
5555
bool get muted;
5656
set muted(bool mute);
5757

58+
///Return true if the audioOutput have been succesfully changed
59+
Future<bool> audioOutput(String deviceId);
60+
5861
bool get renderVideo;
5962
int get textureId;
6063

lib/src/native/mediadevices_impl.dart

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,14 @@ class MediaDeviceNative extends MediaDevices {
6363
@override
6464
Future<List<MediaDeviceInfo>> enumerateDevices() async {
6565
var _source = await getSources();
66-
return _source.map(
67-
(e) => MediaDeviceInfo(
68-
deviceId: e['deviceId'],
69-
groupId: e['groupId'],
70-
kind: e['kind'],
71-
label: e['label']),
72-
);
66+
return _source
67+
.map(
68+
(e) => MediaDeviceInfo(
69+
deviceId: e['deviceId'],
70+
groupId: e['groupId'],
71+
kind: e['kind'],
72+
label: e['label']),
73+
)
74+
.toList();
7375
}
7476
}

lib/src/native/rtc_video_renderer_impl.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22

33
import 'package:flutter/services.dart';
4+
import 'package:flutter_webrtc/flutter_webrtc.dart';
45

56
import '../interface/media_stream.dart';
67
import '../interface/rtc_video_renderer.dart';
@@ -91,10 +92,27 @@ class RTCVideoRendererNative extends VideoRenderer {
9192
bool get renderVideo => srcObject != null;
9293

9394
@override
94-
bool get muted => throw UnimplementedError();
95+
bool get muted => _srcObject?.getAudioTracks()[0]?.muted ?? true;
9596

9697
@override
9798
set muted(bool mute) {
98-
throw UnimplementedError();
99+
if (_srcObject == null) {
100+
throw Exception('Can\'t be muted: The MediaStream is null');
101+
}
102+
if (_srcObject.ownerTag != 'local') {
103+
throw Exception(
104+
'You\'re trying to mute a remote track, this is not supported');
105+
}
106+
if (_srcObject.getAudioTracks()[0] == null) {
107+
throw Exception('Can\'t be muted: The MediaStreamTrack is null');
108+
}
109+
110+
Helper.setMicrophoneMute(mute, _srcObject.getAudioTracks()[0]);
111+
}
112+
113+
@override
114+
Future<bool> audioOutput(String deviceId) {
115+
// TODO(cloudwebrtc): related to https://github.com/flutter-webrtc/flutter-webrtc/issues/395
116+
throw UnimplementedError('This is not implement yet');
99117
}
100118
}

lib/src/rtc_video_renderer.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class RTCVideoRenderer {
2424

2525
set muted(bool mute) => _delegate.muted = mute;
2626

27+
set audioOutput(String deviceId) => _delegate.audioOutput(deviceId);
28+
2729
set srcObject(MediaStream stream) => _delegate.srcObject = stream;
2830

2931
int get textureId => _delegate.textureId;

lib/src/web/rtc_video_renderer_impl.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:async';
22
import 'dart:html' as html;
3+
import 'dart:js_util' as jsutil;
34

45
import 'package:flutter/services.dart';
56

@@ -163,4 +164,19 @@ class RTCVideoRendererWeb extends VideoRenderer {
163164
_videoElement.load();
164165
return super.dispose();
165166
}
167+
168+
@override
169+
Future<bool> audioOutput(String deviceId) async {
170+
try {
171+
if (jsutil.hasProperty(_videoElement, 'setSinkId')) {
172+
await jsutil.promiseToFuture<void>(
173+
jsutil.callMethod(_videoElement, 'setSinkId', [deviceId]));
174+
175+
return true;
176+
}
177+
} catch (e) {
178+
print('Unable to setSinkId: ${e.toString()}');
179+
}
180+
return false;
181+
}
166182
}

0 commit comments

Comments
 (0)
0