Di Flutter, saya ingin melakukan panggilan suara antara dua rekan. Saya menggunakan Flutter-WebRTC. Saya sedang melakukan beberapa pengujian dan video sepertinya berfungsi dengan webrtc, tetapi tidak ada audio. Saya melihat video dari rekan jarak jauh, tetapi tidak mendengar audio apa pun di sisi mana pun.
Satu rekan adalah ponsel Android saya, dan lainnya adalah emulator
Kode main.dart saya adalah:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sdp_transform/sdp_transform.dart';
import 'dart:developer' as developer;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'WebRTC lets learn together'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _offer = false;
RTCPeerConnection _peerConnection;
MediaStream _localStream;
RTCVideoRenderer _localRenderer = new RTCVideoRenderer();
RTCVideoRenderer _remoteRenderer = new RTCVideoRenderer();
final sdpController = TextEditingController();
@override
dispose() {
_localRenderer.dispose();
_remoteRenderer.dispose();
sdpController.dispose();
super.dispose();
}
@override
void initState() {
initRenderers();
_createPeerConnection().then((pc) {
_peerConnection = pc;
});
super.initState();
}
initRenderers() async {
await _localRenderer.initialize();
await _remoteRenderer.initialize();
}
void _createOffer() async {
RTCSessionDescription description =
await _peerConnection.createOffer({'offerToReceiveAudio': 1, 'offerToReceiveVideo': 1});
var session = parse(description.sdp);
print(json.encode(session));
_offer = true;
_peerConnection.setLocalDescription(description);
}
void _createAnswer() async {
RTCSessionDescription description =
await _peerConnection.createAnswer({'offerToReceiveAudio': 1, 'offerToReceiveVideo': 1});
var session = parse(description.sdp);
print(json.encode(session));
_peerConnection.setLocalDescription(description);
}
void _setRemoteDescription() async {
String jsonString = sdpController.text;
dynamic session = await jsonDecode('$jsonString');
String sdp = write(session, null);
// RTCSessionDescription description =
// new RTCSessionDescription(session['sdp'], session['type']);
RTCSessionDescription description =
new RTCSessionDescription(sdp, _offer ? 'answer' : 'offer');
print(description.toMap());
await _peerConnection.setRemoteDescription(description);
}
void _addCandidate() async {
String jsonString = sdpController.text;
dynamic session = await jsonDecode('$jsonString');
print(session['candidate']);
dynamic candidate =
new RTCIceCandidate(session['candidate'], session['sdpMid'], session['sdpMlineIndex']);
await _peerConnection.addCandidate(candidate);
}
_createPeerConnection() async {
Map<String, dynamic> configuration = {
"iceServers": [
{"url": "stun:stun.l.google.com:19302"},
]
};
final Map<String, dynamic> offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": true,
},
"optional": [],
};
_localStream = await _getUserMedia();
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
pc.addStream(_localStream);
pc.onIceCandidate = (e) {
if (e.candidate != null) {
print(json.encode({
'candidate': e.candidate.toString(),
'sdpMid': e.sdpMid.toString(),
'sdpMlineIndex': e.sdpMlineIndex,
}));
}
};
pc.onIceConnectionState = (e) {
print(e);
};
pc.onAddStream = (stream) {
print('addStream: ' + stream.id);
_remoteRenderer.srcObject = stream;
};
return pc;
}
_getUserMedia() async {
final Map<String, dynamic> mediaConstraints = {
'audio': false,
'video': {
'facingMode': 'user',
},
};
MediaStream stream = await MediaDevices.getUserMedia(mediaConstraints);
_localRenderer.srcObject = stream;
return stream;
}
SizedBox videoRenderers() => SizedBox(
height: 210,
child: Row(children: [
Flexible(
child: new Container(
key: new Key("local"),
margin: new EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0),
decoration: new BoxDecoration(color: Colors.black),
child: new RTCVideoView(_localRenderer)
),
),
Flexible(
child: new Container(
key: new Key("remote"),
margin: new EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0),
decoration: new BoxDecoration(color: Colors.black),
child: new RTCVideoView(_remoteRenderer)),
)
]));
Row offerAndAnswerButtons() =>
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
new RaisedButton(
onPressed: _createOffer,
child: Text('Offer'),
color: Colors.amber,
),
RaisedButton(
onPressed: _createAnswer,
child: Text('Answer'),
color: Colors.amber,
),
]);
Row sdpCandidateButtons() =>
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
RaisedButton(
onPressed: _setRemoteDescription,
child: Text('Set Remote Desc'),
color: Colors.amber,
),
RaisedButton(
onPressed: _addCandidate,
child: Text('Add Candidate'),
color: Colors.amber,
)
]);
Padding sdpCandidatesTF() => Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: sdpController,
keyboardType: TextInputType.multiline,
maxLines: 4,
maxLength: TextField.noMaxLength,
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: Column(children: [
videoRenderers(),
offerAndAnswerButtons(),
sdpCandidatesTF(),
sdpCandidateButtons(),
])));
}
}
Di build.gradle, ubah minSdkVersion menjadi 21.
Di AndroidManifest.xml, ditambahkan:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
Saya melihat video dari rekan jarak jauh, tetapi tidak mendengar audio apa pun di sisi mana pun. Apakah saya melewatkan sesuatu?