Skip to content

Commit e4fd32a

Browse files
authored
Merge pull request #12 from flutter-webrtc/dl/selectaudio
select audio output and ondevicechange methods
2 parents c629ad2 + 1b6caea commit e4fd32a

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed

lib/src/mediadevices_impl.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'dart:async';
22
import 'dart:html' as html;
3+
import 'dart:html_common';
4+
import 'dart:js' as js;
35
import 'dart:js_util' as jsutil;
46
import 'package:webrtc_interface/webrtc_interface.dart';
57

@@ -126,4 +128,66 @@ class MediaDevicesWeb extends MediaDevices {
126128
width: _mapConstraints['width'],
127129
zoom: _mapConstraints['zoom']);
128130
}
131+
132+
@override
133+
Future<MediaDeviceInfo> selectAudioOutput(
134+
[AudioOutputOptions? options]) async {
135+
try {
136+
final mediaDevices = html.window.navigator.mediaDevices;
137+
if (mediaDevices == null) throw Exception('MediaDevices is null');
138+
139+
if (jsutil.hasProperty(mediaDevices, 'selectAudioOutput')) {
140+
if (options != null) {
141+
final arg = jsutil.jsify(options);
142+
final deviceInfo = await jsutil.promiseToFuture<html.MediaDeviceInfo>(
143+
jsutil.callMethod(mediaDevices, 'selectAudioOutput', [arg]));
144+
return MediaDeviceInfo(
145+
kind: deviceInfo.kind,
146+
label: deviceInfo.label ?? '',
147+
deviceId: deviceInfo.deviceId ?? '',
148+
groupId: deviceInfo.groupId,
149+
);
150+
} else {
151+
final deviceInfo = await jsutil.promiseToFuture<html.MediaDeviceInfo>(
152+
jsutil.callMethod(mediaDevices, 'selectAudioOutput', []));
153+
return MediaDeviceInfo(
154+
kind: deviceInfo.kind,
155+
label: deviceInfo.label ?? '',
156+
deviceId: deviceInfo.deviceId ?? '',
157+
groupId: deviceInfo.groupId,
158+
);
159+
}
160+
} else {
161+
throw UnimplementedError('selectAudioOutput is missing');
162+
}
163+
} catch (e) {
164+
throw 'Unable to selectAudioOutput: ${e.toString()}, Please try to use MediaElement.setSinkId instead.';
165+
}
166+
}
167+
168+
@override
169+
set ondevicechange(Function(dynamic event)? listener) {
170+
try {
171+
final mediaDevices = html.window.navigator.mediaDevices;
172+
if (mediaDevices == null) throw Exception('MediaDevices is null');
173+
174+
jsutil.setProperty(mediaDevices, 'ondevicechange',
175+
js.allowInterop((evt) => listener?.call(evt)));
176+
} catch (e) {
177+
throw 'Unable to set ondevicechange: ${e.toString()}';
178+
}
179+
}
180+
181+
@override
182+
Function(dynamic event)? get ondevicechange {
183+
try {
184+
final mediaDevices = html.window.navigator.mediaDevices;
185+
if (mediaDevices == null) throw Exception('MediaDevices is null');
186+
187+
jsutil.getProperty(mediaDevices, 'ondevicechange');
188+
} catch (e) {
189+
throw 'Unable to get ondevicechange: ${e.toString()}';
190+
}
191+
return null;
192+
}
129193
}

lib/src/rtc_video_element.dart

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

33
import '../dart_webrtc.dart';
4-
import 'media_stream_impl.dart';
54

65
class RTCVideoElement {
76
RTCVideoElement() {
@@ -61,4 +60,6 @@ class RTCVideoElement {
6160
void load() => _html.load();
6261

6362
void removeAttribute(String name) => _html.removeAttribute(name);
63+
64+
Future<void> setSinkId(String sinkId) => _html.setSinkId(sinkId);
6465
}

web/main.dart

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,43 @@ void loopBackTest() async {
2525
var localVideo = RTCVideoElement();
2626
local!.append(localVideo.htmlElement);
2727

28+
//var pc = await createPeerConnection({});
29+
//pc.onAddStream = (MediaStream stream) {};
30+
var stream =
31+
await navigator.mediaDevices.getUserMedia({'audio': true, 'video': true});
32+
/*.getUserMedia(MediaStreamConstraints(audio: true, video: true))*/
33+
print('getDisplayMedia: stream.id => ${stream.id}');
34+
35+
navigator.mediaDevices.ondevicechange = (event) async {
36+
var list = await navigator.mediaDevices.enumerateDevices();
37+
print('ondevicechange: ');
38+
list.where((element) => element.kind == 'audiooutput').forEach((e) {
39+
print('${e.runtimeType}: ${e.label}, type => ${e.kind}');
40+
});
41+
};
42+
2843
var list = await navigator.mediaDevices.enumerateDevices();
2944
list.forEach((e) {
3045
print('${e.runtimeType}: ${e.label}, type => ${e.kind}');
3146
});
47+
var outputList = list.where((element) => element.kind == 'audiooutput');
48+
if (outputList.isNotEmpty) {
49+
var sinkId = outputList.last.deviceId;
50+
try {
51+
await navigator.mediaDevices
52+
.selectAudioOutput(AudioOutputOptions(deviceId: sinkId));
53+
} catch (e) {
54+
print('selectAudioOutput error: ${e.toString()}');
55+
await localVideo.setSinkId(sinkId);
56+
}
57+
}
3258

33-
var pc = await createPeerConnection({});
34-
pc.onAddStream = (MediaStream stream) {};
35-
var stream =
36-
await navigator.mediaDevices.getUserMedia({'audio': true, 'video': true});
37-
/*.getUserMedia(MediaStreamConstraints(audio: true, video: true))*/
38-
print('getDisplayMedia: stream.id => ${stream.id}');
3959
/*
4060
stream.oninactive = (Event event) {
4161
print('oninactive: stream.id => ${event.target.id}');
4262
localVideo.srcObject = null;
4363
};
4464
*/
45-
await pc.addStream(stream);
65+
//await pc.addStream(stream);
4666
localVideo.srcObject = stream;
4767
}

0 commit comments

Comments
 (0)