flutter-webrtc安装示例

1、pubspec.yaml 添加以下flutter_webrtc库

flutter pub get 安装库

2、使用示例

复制代码
class GetUserMediaSample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _GetUserMediaSampleState();
  }
}

class _GetUserMediaSampleState extends State<GetUserMediaSample> {
  // 定义本地和远程视频渲染器
  final RTCVideoRenderer _localRenderer = RTCVideoRenderer();
  final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
  bool _isFrontCamera = false;
  // 本地媒体流(摄像头+麦克风)
  MediaStream? _localStream;

  // PeerConnection(简化示例中未实际连接)
  RTCPeerConnection? _peerConnection;

  @override
  void initState() {
    super.initState();
    // 初始化渲染器并获取本地媒体流
    _initializeRenderers();
  }

  @override
  void dispose() {
    // 释放资源
    _localRenderer.dispose();
    _remoteRenderer.dispose();
    _localStream?.dispose();
    _peerConnection?.close();
    super.dispose();
  }

  // 初始化视频渲染器
  Future<void> _initializeRenderers() async {
    await _localRenderer.initialize();
    await _remoteRenderer.initialize();
    await _getUserMedia(false);
  }

  // 获取摄像头和麦克风权限并绑定到渲染器
  Future<void> _getUserMedia(bool useFrontCamera) async {
    try {
      final devices = await navigator.mediaDevices.enumerateDevices();
      final videoDevices =
          devices.where((d) => d.kind == 'videoinput').toList();
      String? targetDeviceId;
      for (var device in videoDevices) {
        if (useFrontCamera && device.label.contains("front") ||
            !useFrontCamera && device.label.contains("back")) {
          targetDeviceId = device.deviceId;
          break;
        }
      }

      targetDeviceId ??= videoDevices.first.deviceId;

      // 定义媒体约束(视频+音频)
      final constraints = {
        'audio': true,
        'video': {
          'deviceId': targetDeviceId,
          'width': 1920,
          'height': 1080,
          'mandatory': {
            'minWidth': '640',
            'minHeight': '480',
            'minFrameRate': '30',
          },
        }
      };

      // 获取媒体流
      _localStream = await navigator.mediaDevices.getUserMedia(constraints);

      // 将本地流绑定到渲染器
      setState(() {
        _localRenderer.srcObject = _localStream;
      });
    } catch (e) {
      print("获取媒体设备失败: $e");
    }
  }

  void _switchCamera() async {
    setState(() {
      _isFrontCamera = !_isFrontCamera;
    });
    await _getUserMedia(_isFrontCamera);
  }

  // 构建 UI
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text('WebRTC 示例'),
      ),
      body: Column(
        children: [
          // 本地视频预览
          Expanded(
            child: Container(
              margin: EdgeInsets.all(8),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blue),
              ),
              child: RTCVideoView(_localRenderer),
            ),
          ),

          // 远程视频预览(简化示例中未实际连接)
          // Expanded(
          //   child: Container(
          //     margin: EdgeInsets.all(8),
          //     decoration: BoxDecoration(
          //       border: Border.all(color: Colors.red),
          //     ),
          //     child: RTCVideoView(_remoteRenderer),
          //   ),
          // ),

          // 控制按钮
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.videocam_off),
                onPressed: _toggleVideo,
              ),
              IconButton(
                icon: Icon(Icons.mic_off),
                onPressed: _toggleAudio,
              ),
              IconButton(onPressed: _switchCamera, icon: Icon(Icons.cached))
            ],
          ),
        ],
      ),
    );
  }

  // 切换摄像头开关
  void _toggleVideo() {
    if (_localStream != null) {
      final videoTrack = _localStream!.getVideoTracks().first;
      setState(() {
        videoTrack.enabled = !videoTrack.enabled;
      });
    }
  }

  // 切换麦克风开关
  void _toggleAudio() {
    if (_localStream != null) {
      final audioTrack = _localStream!.getAudioTracks().first;
      setState(() {
        audioTrack.enabled = !audioTrack.enabled;
      });
    }
  }
}
相关推荐
里欧跑得慢7 小时前
15. Web可访问性最佳实践:让每个用户都能平等访问
前端·css·flutter·web
Lanren的编程日记10 小时前
Flutter 鸿蒙应用数据版本管理实战:版本记录+版本回退+版本对比,实现全链路数据版本控制
flutter·华为·harmonyos
三十_14 小时前
WebRTC 远端画面无法显示:ICE 与 SDP 时序问题深度解析与解决方案
webrtc
MonkeyKing16 小时前
Flutter列表性能极致优化:从卡顿到丝滑
flutter·dart
IntMainJhy16 小时前
「Flutter三方库sqflite的鸿蒙化适配与实战指南:从入门到踩坑的本地数据库开发全记录」
数据库·flutter·华为·信息可视化·数据库开发·harmonyos
梦想不只是梦与想17 小时前
flutter中 safeArea组件
flutter·safearea
Hello__777720 小时前
开源鸿蒙 Flutter 实战|自定义头像组件全流程实现
flutter·华为·harmonyos
LIO20 小时前
Flutter——直击核心的极简指南
flutter
愚者Pro21 小时前
Flutter项目 lib/ 目录结构(大厂规范)
flutter