You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Flutter的sip_ua与webrtc包连接Free Switch PBX服务器

Flutter WebRTC 分步实现指南(适配Web端开发经验)

前提说明

Flutter中实现WebRTC依赖flutter_webrtc包,核心API和Web端的RTCPeerConnection、MediaStream等概念完全对齐,只是调用方式适配了Flutter的异步框架。


步骤1:添加依赖

在项目的pubspec.yaml中添加依赖:

dependencies:
  flutter_webrtc: ^0.9.46 # 请使用最新稳定版本

执行命令安装依赖:

flutter pub get

步骤2:初始化PeerConnection与本地媒体流

2.1 创建PeerConnection实例

和Web端逻辑一致,先配置ICE服务器,再初始化连接:

import 'package:flutter_webrtc/flutter_webrtc.dart';

final Map<String, dynamic> configuration = {
  'iceServers': [
    {'urls': 'stun:stun.l.google.com:19302'},
  ]
};

RTCPeerConnection? peerConnection;

Future<void> initPeerConnection() async {
  peerConnection = await createPeerConnection(configuration);
  // 监听ICE候选生成事件
  peerConnection!.onIceCandidate = (RTCIceCandidate candidate) {
    // 将候选通过信令服务器发送给对方
    sendCandidateToSignalingServer(candidate.toMap());
  };
  // 监听远程媒体流添加事件
  peerConnection!.onTrack = (RTCTrackEvent event) {
    if (event.streams.isNotEmpty) {
      // 获取远程流用于渲染
      setState(() {
        remoteStream = event.streams[0];
      });
    }
  };
}

2.2 获取本地摄像头/麦克风流

对应Web端的getUserMedia方法,调用Flutter的同名异步方法:

MediaStream? localStream;

Future<void> getLocalStream() async {
  final Map<String, dynamic> constraints = {
    'audio': true,
    'video': {
      'facingMode': 'user', // 前置摄像头
      'width': 1280,
      'height': 720,
    }
  };
  localStream = await navigator.mediaDevices.getUserMedia(constraints);
  // 将本地流添加到PeerConnection
  if (peerConnection != null) {
    localStream!.getTracks().forEach((track) {
      peerConnection!.addTrack(track, localStream!);
    });
  }
}

步骤3:信令交互(Offer/Answer流程)

逻辑和Web端完全一致,用Flutter异步语法实现:

3.1 发起方创建Offer

Future<void> createOffer() async {
  if (peerConnection == null) return;
  final RTCSessionDescription offer = await peerConnection!.createOffer();
  await peerConnection!.setLocalDescription(offer);
  // 将Offer通过信令服务器发送给接收方
  sendOfferToSignalingServer(offer.toMap());
}

3.2 接收方处理Offer并创建Answer

Future<void> handleRemoteOffer(Map<String, dynamic> offerMap) async {
  if (peerConnection == null) return;
  final RTCSessionDescription remoteOffer = RTCSessionDescription(
    offerMap['sdp'],
    offerMap['type'],
  );
  await peerConnection!.setRemoteDescription(remoteOffer);
  // 创建Answer并发送给发起方
  final RTCSessionDescription answer = await peerConnection!.createAnswer();
  await peerConnection!.setLocalDescription(answer);
  sendAnswerToSignalingServer(answer.toMap());
}

3.3 发起方处理远程Answer

Future<void> handleRemoteAnswer(Map<String, dynamic> answerMap) async {
  if (peerConnection == null) return;
  final RTCSessionDescription remoteAnswer = RTCSessionDescription(
    answerMap['sdp'],
    answerMap['type'],
  );
  await peerConnection!.setRemoteDescription(remoteAnswer);
}

步骤4:处理ICE候选

接收方收到对方发送的ICE候选后,添加到本地PeerConnection:

Future<void> handleRemoteCandidate(Map<String, dynamic> candidateMap) async {
  if (peerConnection == null) return;
  final RTCIceCandidate candidate = RTCIceCandidate(
    candidateMap['candidate'],
    candidateMap['sdpMid'],
    candidateMap['sdpMLineIndex'],
  );
  await peerConnection!.addCandidate(candidate);
}

步骤5:渲染视频流

使用Flutter提供的RTCVideoView组件渲染本地和远程流:

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      // 本地视频渲染
      if (localStream != null)
        RTCVideoView(
          localStream!,
          objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
        ),
      // 远程视频渲染
      if (remoteStream != null)
        RTCVideoView(
          remoteStream!,
          objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
        ),
    ],
  );
}

关键差异说明

  • Flutter中所有异步操作依赖Future,需用async/await处理,和Web端Promise逻辑一致但语法不同
  • 视频渲染必须使用RTCVideoView组件,而非Web端的video标签
  • 事件监听通过设置onIceCandidate、onTrack等属性实现,等价于Web端的addEventListener

内容的提问来源于stack exchange,提问作者M.zafar shafaq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:25:14