Как мне реализовать webrtc на флаттере?IOS

Программируем под IOS
Ответить
Anonymous
 Как мне реализовать webrtc на флаттере?

Сообщение Anonymous »


Я работаю над VoIP-приложением для Android и IOS, используя Flutter. Я использую дарт sip_ua. Я могу зарегистрироваться на сервере сигнализации, а также принять звонок. Клиент также может подключиться к ледяному серверу. Однако вызов завершается через 30 секунд с ошибкой тайм-аута rtp. Ниже приведен журнал ошибок:

I/flutter (6747): Record-Route: I/flutter (6747): Маршрут записи: D/FlutterWebRTCPlugin (6747): onAddTrack D/FlutterWebRTCPlugin (6747): onIceGatheringChangeGATHERING D/FlutterWebRTCPlugin (6747): onConnectionChangeCONNECTING D/FlutterWebRTCPlugin (6747): onIceCandidate D/FlutterWebRTCPlugin (6747): onIceGatheringChangeCOMPLETE I/flutter (6747): Маршрут записи: I/flutter (6747): Маршрут записи: I/futter (6747): m=аудио 56441 RTP/SAVPF 0 8 101 D/FlutterWebRTCPlugin (6747): onIceCandidate I/futter (6747): m=аудио 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 ::: вызов завершился с причиной: Код: [408], Причина: тайм-аут RTP, Причина: тайм-аут RTP D/FlutterWebRTCPlugin (6747): onConnectionChangeCLOSED I/flutter (6747): Маршрут: I/flutter (6747): Маршрут: I/futter (6747): Причина: SIP;case=408; text="Тайм-аут RTP" Ниже приведен код sip ua

`import 'package:flutter/material.dart'; импортировать «пакет: flutter_callkit_incoming/entities/entities.dart»; импортировать «пакет: uuid/uuid.dart»; импортировать «пакет: flutter_callkit_incoming/flutter_callkit_incoming.dart»; импортировать «пакет: sip_ua/sip_ua.dart»; импортировать «пакет: flutter_webrtc/flutter_webrtc.dart»; пустая функция() { runApp(const MyApp()); вар uuid = const Uuid(); вар sipHelper = SIPUAHelper(); SipEvents sipevent = SipEvents(); sipevent._initRenderers(); вар ua = UaSettings(); ua.authorizationUser = '2167'; ua.пароль = 'ХХХХ'; ua.webSocketUrl = 'wss://turn.konza.go.ke/ws'; ua.uri = 'sip:2167@XX.XX.XX.XX'; ua.iceGatheringTimeout = 3000; ua.iceServers = [ { 'url': 'turn:turn.konza.go.ke:3478', 'имя пользователя': 'goipvoice', «учетные данные»: «ХХХХХ», }, ]; ua.register = правда; sipHelper.start(ua); print("слушатели здесь"); печать (sipHelper.listeners); sipHelper.addSipUaHelperListener(sipevent); } класс SipEvents расширяет SipUaHelperListener { окончательный RTCVideoRenderer? _localRenderer = RTCVideoRenderer(); окончательный RTCVideoRenderer? _remoteRenderer = RTCVideoRenderer(); двойной? _localVideoHeight; двойной? _localVideoWidth; окончательный mediaConstraints = { «аудио»: правда, «видео»: ложь, }; МедиаСтрим? _localStream; МедиаСтрим? _remoteStream; поздний MediaStream mediaStream; void _handleAccept() асинхронный { mediaStream = ждут navigator.mediaDevices.getUserMedia(mediaConstraints); } void _handelStreams (событие CallState) асинхронный { МедиаСтрим? поток = событие.поток; if (event.originator == 'local') { если (_localRenderer!= ноль) { _localRenderer!.srcObject = поток; } если (!WebRTC.platformIsDesktop) { event.stream?.getAudioTracks().first.enableSpeakerphone(false); } _localStream = поток; } if (event.originator == 'удаленный') { если (_remoteRenderer!= ноль) { _remoteRenderer!.srcObject = поток; } _remoteStream = поток; } } void _initRenderers() асинхронный { если (_localRenderer!= ноль) { дождитесь _localRenderer!.initialize(); } если (_remoteRenderer!= ноль) { дождитесь _remoteRenderer!.initialize(); } } @переопределить Будущее callStateChanged (вызов вызова, состояние CallState) async { // TODO: реализовать callStateChanged Строка имя_состояния = состояние.имя_состояния; print("новый вызов $stateName"); печать(состояние.состояние.имя); if (stateName == "ПРОГРЕСС") { print("Звонок начался"); пытаться { медиапоток = ждут navigator.mediaDevices.getUserMedia(mediaConstraints); call.answer(SIPUAHelper().buildCallOptions(true), медиапоток: медиапоток); print("полученные медиа"); вар PeerConnection = call.peerConnection; print("одноранговое соединение $peerConnection"); } поймать (е) {} } if (stateName == "ПОТОК") { _handelStreams (состояние); } } @переопределить void onNewMessage (SIPMessageRequest сообщение) { // TODO: реализовать onNewMessage print("Новое sip-сообщение"); } @переопределить void onNewNotify (Уведомить ntf) { // TODO: реализовать onNewNotify print("новое уведомление"); } @переопределить void RegistrationStateChanged (состояние RegistrationState) { // TODO: реализовать регистрациюStateChanged print("новая регистрация"); } @переопределить void TransportStateChanged (состояние TransportState) { // TODO: реализовать TransportStateChanged print("Новый транспорт"); } } класс MyApp расширяет StatelessWidget { const MyApp({super.key}); // Этот виджет является корнем вашего приложения. @переопределить Сборка виджета (контекст BuildContext) { вернуть MaterialApp( название: «Демо-версия Flutter», тема: ThemeData( // Это тема вашего приложения. // // ПОПРОБУЙТЕ ЭТО: попробуйте запустить приложение с помощью «futter run». Вот увидишь // приложение имеет фиолетовую панель инструментов. Затем, не выходя из приложения, // попробуйте изменить семенный цвет в цветовой схеме ниже на Colors.green // а затем вызвать «горячую перезагрузку» (сохраните изменения или нажмите кнопку «горячая перезагрузка» // перезагрузить» в IDE, поддерживаемой Flutter, или нажмите «r», если вы использовали // командная строка для запуска приложения). // // Обратите внимание, что счетчик не обнулился; приложение // состояние не теряется во время перезагрузки. Для сброса состояния используйте горячую // вместо этого перезапускаем. // // Это работает и для кода, а не только для значений: большинство изменений кода можно // протестировано с помощью горячей перезагрузки. colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: правда, ), дома: const MyHomePage( title: 'Счетчик горячей перезагрузки домашней страницы демонстрационной страницы Flutter'), ); } } класс MyHomePage расширяет StatefulWidget реализует SipUaHelperListener { const MyHomePage({super.key, требуется this.title}); // Этот виджет является домашней страницей вашего приложения. Это состояние, то есть // что у него есть объект State (определенный ниже), содержащий поля, которые влияют // как это выглядит. // Этот класс является конфигурацией состояния. Он содержит значения (в этом // регистрируем заголовок), предоставленный родителем (в данном случае виджетом приложения) и // используется методом сборки State. Поля в подклассе Widget: // всегда помечен как «финальный». окончательный заголовок строки; недействительный initState() { SIPUAHelper().addSipUaHelperListener(это); } @переопределить State createState() => _MyHomePageState(); @переопределить void callStateChanged (вызов вызова, состояние CallState) { // TODO: реализовать callStateChanged print("Новое сообщение"); } @переопределить void onNewMessage (SIPMessageRequest сообщение) { // TODO: реализовать onNewMessage print("Новое сообщение"); } @переопределить void onNewNotify (Уведомить ntf) { // TODO: реализовать onNewNotify print("Новое сообщение"); } @переопределить void RegistrationStateChanged (состояние RegistrationState) { // TODO: реализовать регистрациюStateChanged print("Новое сообщение"); } @переопределить void TransportStateChanged (состояние TransportState) { // TODO: реализовать TransportStateChanged print("Новое сообщение"); } } класс _MyHomePageState расширяет State { интервал _counter = 0; void _incrementCounter() { setState(() { // Этот вызов setState сообщает платформе Flutter, что что-то произошло // изменилось в этом состоянии, что приводит к повторному запуску метода сборки ниже // чтобы на дисплее отображались обновленные значения. Если бы мы изменились // _counter без вызова setState(), тогда метод сборки не будет // вызвали еще раз, и, похоже, ничего не произошло. _счетчик++; }); } @переопределить Сборка виджета (контекст BuildContext) { вар uuid = const Uuid(); вар callid = uuid.v4(); // Этот метод запускается повторно каждый раз, когда вызывается setState, например, как сделано // методом _incrementCounter выше. // // Платформа Flutter оптимизирована для повторного запуска методов сборки // быстро, так что вы можете просто перестроить все, что требует обновления, а не // чем менять экземпляры виджетов по отдельности. вернуть эшафот( AppBar: AppBar( // ПОПРОБУЙТЕ ЭТО: попробуйте изменить цвет здесь на определенный цвет (на // Colors.amber, возможно?) и запускаем горячую перезагрузку, чтобы увидеть AppBar // изменяем цвет, а остальные цвета остаются прежними. BackgroundColor: Theme.of(context).colorScheme.inversePrimary, // Здесь мы берем значение из объекта MyHomePage, созданного // метод App.build и используем его для установки заголовка панели приложения. заголовок: Текст(виджет.заголовок), ), тело: Центр( // Центр — это виджет макета. Он берет одного ребенка и позиционирует его // в середине родителя. ребенок: Столбец( // Столбец также является виджетом макета. Берется список детей и // Располагает их вертикально. По умолчанию он подстраивается под свой размер. // дочерние элементы расположены горизонтально и стараются быть такими же высокими, как и родительский элемент. // // Столбец имеет различные свойства для управления его размером и // как он позиционирует своих детей. Здесь мы используем mainAxisAlignment для // центрируем дочерние элементы по вертикали; основная ось здесь вертикальная // ось, потому что столбцы вертикальные (поперечная ось будет // горизонтально). // // ПОПРОБУЙТЕ ЭТО: вызвать «отладку рисования» (выберите «Переключить отладку рисования» // действие в IDE или нажмите «p» в консоли), чтобы увидеть // каркас для каждого виджета. mainAxisAlignment: MainAxisAlignment.center, дети: [ константный текст( 'Вы нажали кнопку здесь столько раз:', ), Текст( '$_counter', стиль: Theme.of(context).textTheme.headlineMedium, ), ElevatedButton( onPressed: () асинхронный { print("Нажата кнопка действия"); Параметры CallKitParams = CallKitParams( идентификатор: Каллид, nameCaller: 'Эммануэль', appName: 'перейти на мобильное устройство', тип: 0, продолжительность: 30000, textAccept: 'Ответ', textDecline: 'Отклонить', дескриптор: '0716597086', дополнительно: {'userId': '1a2b3c4d'}, пропущенный вызов: const NotificationParams( идентификатор: 234, showNotification: правда, callbackText: «Обратный звонок», ), Android: const AndroidParams( RingtonePath: 'system_ringtone_default', isCustomNotification: правда, isShowLogo: ложь, )); ждут FlutterCalkitIncoming.showCalkitIncoming (параметры); FlutterCalkitIncoming.onEvent.listen((событие) { печать (событие?.событие); переключатель (событие?.event) { случай Event.actionCallAccept: FlutterCalkitIncoming.setCallConnected(callid); print("Приемник вызова"); перерыв; по умолчанию: } }); }, дочерний элемент: const Text('Входящий вызов'), ), ], ), ), FloatingActionButton: FloatingActionButton( onPressed: _incrementCounter, подсказка: «Приращение», дочерний элемент: const Icon(Icons.add), ), // Эта завершающая запятая делает автоматическое форматирование более удобным для методов сборки. ); } }` Пожалуйста, помогите с этим. Я новичок в Flutter, но у меня есть опыт работы с webct на JavaScript.

Я попробовал сменить ледяные серверы, а также протестировал соединение с ледяными серверами, чтобы убедиться, что ледяные серверы работают так же хорошо, как и серверы сигнализации.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»