当前位置: 首页 > article >正文

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;
      });
    }
  }
}


http://www.kler.cn/a/541398.html

相关文章:

  • DeepSeek 实践总结
  • Expo运行模拟器失败错误解决(xcrun simctl )
  • Day82:创建图形界面(GUI)
  • AMD 8845HS 780M核显部署本地deepseek大模型的性能
  • MyBatis面试题解析
  • 使用 Three.js 实现热力渐变效果
  • 简易图书管理系统——MYsql+Javase+JDBC
  • 后端开发校招面试常见问答总结(一)|Java高频考点解析
  • ESP32S3(主模式) 与 STM32(从模式) 进行SPI全双工通信
  • Ansible与shell脚本执行的区别
  • 数据源和 sqlSessionFactory 配置
  • 13.10 统一配置管理中心:TranslationChain 架构的简洁配置管理方案
  • DeepSeek的崛起:开源AI的革命
  • WebSocket学习记录
  • 数据治理双证通关经验分享 | CDGA/CDGP备考全指南
  • Leetcode Hot100 81-85
  • 企业AI招聘会在2025年出现爆炸式增长吗?
  • Node.js笔记入门篇
  • 【从零开始入门unity游戏开发之——C#篇54】C#补充知识点——元组(Tuple)
  • 【SpringBoot实现全局API限频】 最佳实践
  • AWS Savings Plans 监控与分析工具使用指南
  • 开源AI终端工具Wave Terminal从安装到远程连接内网服务器全流程攻略
  • 深度整理总结MySQL——MySQL加锁工作原理
  • 【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案
  • 安卓基础(第一集)
  • 嵌入式音视频开发(一)ffmpeg框架及内核解析