Flutter下SIP-UA+WebRTC实现VoIP遇RTP超时问题求助
Flutter VoIP应用RTP超时问题排查
我正在使用Flutter开发适用于Android和iOS的VoIP应用,采用dart sip_ua库。目前已成功注册到信令服务器、接收呼叫,且客户端可连接ICE服务器,但呼叫会在30秒后因RTP超时错误终止。
错误日志
I/flutter ( 6747): Record-Route: <sip:192.168.0.7:5080;transport=ws;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> I/flutter ( 6747): Record-Route: <sip:192.168.0.7;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> D/FlutterWebRTCPlugin( 6747): onAddTrack D/FlutterWebRTCPlugin( 6747): onIceGatheringChangeGATHERING D/FlutterWebRTCPlugin( 6747): onConnectionChangeCONNECTING D/FlutterWebRTCPlugin( 6747): onIceCandidate D/FlutterWebRTCPlugin( 6747): onIceGatheringChangeCOMPLETE I/flutter ( 6747): Record-Route: <sip:192.168.0.7:5080;transport=ws;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> I/flutter ( 6747): Record-Route: <sip:192.168.0.7;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> I/flutter ( 6747): m=audio 56441 RTP/SAVPF 0 8 101 D/FlutterWebRTCPlugin( 6747): onIceCandidate I/flutter ( 6747): m=audio 56441 RTP/SAVPF 0 8 101 D/FlutterWebRTCPlugin( 6747): onConnectionChangeFAILED I/flutter ( 6747): [2024-01-02 00:28:28.596] Level.debug sip_ua_helper.dart:249 ::: call ended with cause: Code: [408], Cause: RTP Timeout, Reason: RTP Timeout D/FlutterWebRTCPlugin( 6747): onConnectionChangeCLOSED I/flutter ( 6747): Route: <sip:192.168.0.7:5080;transport=ws;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> I/flutter ( 6747): Route: <sip:192.168.0.7;r2=on;lr=on;nat=yes;rtp=bridge;rtp=ws> I/flutter ( 6747): Reason: SIP ;case=408; text="RTP Timeout"
SIP UA代码
import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/entities/entities.dart'; import 'package:uuid/uuid.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:sip_ua/sip_ua.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; void main() { runApp(const MyApp()); var uuid = const Uuid(); var sipHelper = SIPUAHelper(); SipEvents sipevent = SipEvents(); sipevent._initRenderers(); var ua = UaSettings(); ua.authorizationUser = '2167'; ua.password = 'XXXX'; ua.webSocketUrl = 'wss://turn.konza.go.ke/ws'; ua.uri = 'sip:2167@XX.XX.XX.XX'; ua.iceGatheringTimeout = 3000; ua.iceServers = <Map<String, String>>[ <String, String>{ 'url': 'turn:turn.konza.go.ke:3478', 'username': 'goipvoice', 'credential': 'XXXXX', }, ]; ua.register = true; sipHelper.start(ua); print("listeners here"); print(sipHelper.listeners); sipHelper.addSipUaHelperListener(sipevent); } class SipEvents extends SipUaHelperListener { final RTCVideoRenderer? _localRenderer = RTCVideoRenderer(); final RTCVideoRenderer? _remoteRenderer = RTCVideoRenderer(); double? _localVideoHeight; double? _localVideoWidth; final mediaConstraints = <String, dynamic>{ 'audio': true, 'video': false, }; MediaStream? _localStream; MediaStream? _remoteStream; late MediaStream mediaStream; void _handleAccept() async { mediaStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); } void _handelStreams(CallState event) async { MediaStream? stream = event.stream; if (event.originator == 'local') { if (_localRenderer != null) { _localRenderer!.srcObject = stream; } if (!WebRTC.platformIsDesktop) { event.stream?.getAudioTracks().first.enableSpeakerphone(false); } _localStream = stream; } if (event.originator == 'remote') { if (_remoteRenderer != null) { _remoteRenderer!.srcObject = stream; } _remoteStream = stream; } } void _initRenderers() async { if (_localRenderer != null) { await _localRenderer!.initialize(); } if (_remoteRenderer != null) { await _remoteRenderer!.initialize(); } } @override Future<void> callStateChanged(Call call, CallState state) async { String stateName = state.state.name; print("new Call $stateName"); print(state.state.name); if (stateName == "PROGRESS") { print("call has started"); try { mediaStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); call.answer(SIPUAHelper().buildCallOptions(true), mediaStream: mediaStream); print("gotten media"); var peerConnection = call.peerConnection; print("peer connection $peerConnection"); } catch (e) {} } if (stateName == "STREAM") { _handelStreams(state); } } @override void onNewMessage(SIPMessageRequest msg) { print("new sip mesasge"); } @override void onNewNotify(Notify ntf) { print("new notify"); } @override void registrationStateChanged(RegistrationState state) { print("new registration"); } @override void transportStateChanged(TransportState state) { print("new transport"); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage( title: 'Flutter Demo Home Page hot reload count here'), ); } } class MyHomePage extends StatefulWidget implements SipUaHelperListener { const MyHomePage({super.key, required this.title}); final String title; void initState() { SIPUAHelper().addSipUaHelperListener(this); } @override State<MyHomePage> createState() => _MyHomePageState(); @override void callStateChanged(Call call, CallState state) { print("new message"); } @override void onNewMessage(SIPMessageRequest msg) { print("new message"); } @override void onNewNotify(Notify ntf) { print("new message"); } @override void registrationStateChanged(RegistrationState state) { print("new message"); } @override void transportStateChanged(TransportState state) { print("new message"); } } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { var uuid = const Uuid(); var callid = uuid.v4(); return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times here:', ), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, ), ElevatedButton( onPressed: () async { print("action button pressed"); CallKitParams params = CallKitParams( id: callid, nameCaller: 'Emmanuel', appName: 'go mobile', type: 0, duration: 30000, textAccept: 'Answer', textDecline: 'Decline', handle: '0716597086', extra: {'userId': '1a2b3c4d'}, missedCallNotification: const NotificationParams( id: 234, showNotification: true, callbackText: "Call back", ), android: const AndroidParams( ringtonePath: 'system_ringtone_default', isCustomNotification: true, isShowLogo: false, )); await FlutterCallkitIncoming.showCallkitIncoming(params); FlutterCallkitIncoming.onEvent.listen((event) { print(event?.event); switch (event?.event) { case Event.actionCallAccept: FlutterCallkitIncoming.setCallConnected(callid); print("call accepter"); break; default: } }); }, child: const Text('Incoming Call'), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
我刚接触Flutter,但具备WebRTC JavaScript开发经验。已尝试更换ICE服务器并验证其与信令服务器的可用性,仍未解决问题,恳请协助。
内容的提问来源于stack exchange,提问作者emmanuel simiyu
相关产品推荐
相关产品推荐

