Flutter WebRTC视频通信插件videosdk_webrtc的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter WebRTC视频通信插件videosdk_webrtc的使用

功能性

功能特性 Android iOS Web macOS Windows Linux 嵌入式 Fuchsia
音频/视频 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ [WIP]
数据通道 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ [WIP]
屏幕捕获 ✔️ ✔️(*) ✔️ ✔️ ✔️ ✔️ [WIP]
统一计划 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ [WIP]
多流传输 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ [WIP]
媒体录制 ⚠️ ⚠️ ✔️
SFrame/帧加密器 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
插入流

iOS

在您的Info.plist文件中添加以下条目,该文件位于<项目根目录>/ios/Runner/Info.plist

<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) Camera Usage!</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) Microphone Usage!</string>

这将允许您的应用程序访问摄像头和麦克风。

iOS注意事项

WebRTC.xframework编译版本在m104发布后不再支持iOS arm设备。因此,需要在您的项目的ios/Podfile中添加以下行:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      # Workaround for https://github.com/flutter/flutter/issues/64502
      config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' # <= 这一行
    end
  end
end

Android

确保在您的Android Manifest文件中包含以下权限,该文件位于<项目根目录>/android/app/src/main/AndroidManifest.xml

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

如果需要使用蓝牙设备,请添加以下权限:

<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

默认情况下,Flutter项目模板会添加这些权限,所以可能已经存在。

还需要将构建设置设为Java 8,因为官方WebRTC jar现在使用了EglBase接口中的静态方法。只需在您的应用级build.gradle中添加以下内容:

android {
    //...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

如果有必要,在同一build.gradle中您可能需要将defaultConfigminSdkVersion增加到23(当前默认的Flutter生成器将其设置为16)。

完整示例代码

import 'dart:core';

import 'package:flutter/foundation.dart'
    show debugDefaultTargetPlatformOverride;
import 'package:flutter/material.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:videosdk_webrtc/flutter_webrtc.dart';
import 'package:flutter_webrtc_example/src/capture_frame_sample.dart';

import 'src/device_enumeration_sample.dart';
import 'src/get_display_media_sample.dart';
import 'src/get_user_media_sample.dart'
    if (dart.library.html) 'src/get_user_media_sample_web.dart';
import 'src/loopback_data_channel_sample.dart';
import 'src/loopback_sample_unified_tracks.dart';
import 'src/route_item.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  if (WebRTC.platformIsDesktop) {
    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  } else if (WebRTC.platformIsAndroid) {
    //startForegroundService();
  }
  runApp(MyApp());
}

Future<bool> startForegroundService() async {
  final androidConfig = FlutterBackgroundAndroidConfig(
    notificationTitle: 'Title of the notification',
    notificationText: 'Text of the notification',
    notificationImportance: AndroidNotificationImportance.Default,
    notificationIcon: AndroidResource(
        name: 'background_icon',
        defType: 'drawable'), // 默认为mipmap文件夹下的ic_launcher
  );
  await FlutterBackground.initialize(androidConfig: androidConfig);
  return FlutterBackground.enableBackgroundExecution();
}

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late List<RouteItem> items;

  [@override](/user/override)
  void initState() {
    super.initState();
    _initItems();
  }

  ListBody _buildRow(context, item) {
    return ListBody(children: <Widget>[
      ListTile(
        title: Text(item.title),
        onTap: () => item.push(context),
        trailing: Icon(Icons.arrow_right),
      ),
      Divider()
    ]);
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
          appBar: AppBar(
            title: Text('Flutter-WebRTC example'),
          ),
          body: ListView.builder(
              shrinkWrap: true,
              padding: const EdgeInsets.all(0.0),
              itemCount: items.length,
              itemBuilder: (context, i) {
                return _buildRow(context, items[i]);
              })),
    );
  }

  void _initItems() {
    items = <RouteItem>[
      RouteItem(
          title: 'GetUserMedia',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) => GetUserMediaSample()));
          }),
      RouteItem(
          title: 'Device Enumeration',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) =>
                        DeviceEnumerationSample()));
          }),
      RouteItem(
          title: 'GetDisplayMedia',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) =>
                        GetDisplayMediaSample()));
          }),
      RouteItem(
          title: 'LoopBack Sample (Unified Tracks)',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) =>
                        LoopBackSampleUnifiedTracks()));
          }),
      RouteItem(
          title: 'DataChannelLoopBackSample',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) =>
                        DataChannelLoopBackSample()));
          }),
      RouteItem(
          title: 'Capture Frame',
          push: (BuildContext context) {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (BuildContext context) =>
                        CaptureFrameSample()));
          }),
    ];
  }
}

更多关于Flutter WebRTC视频通信插件videosdk_webrtc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter WebRTC视频通信插件videosdk_webrtc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用videosdk_webrtc插件进行视频通信的基本示例代码。请注意,这只是一个基础示例,实际项目中可能需要根据具体需求进行调整和扩展。

首先,确保你的Flutter项目已经创建好,并且在pubspec.yaml文件中添加了videosdk_webrtc依赖:

dependencies:
  flutter:
    sdk: flutter
  videosdk_webrtc: ^最新版本号  # 请替换为实际可用的最新版本号

然后运行flutter pub get来安装依赖。

接下来是示例代码,这里展示如何初始化WebRTC并进行基本的视频通信设置。

main.dart

import 'package:flutter/material.dart';
import 'package:videosdk_webrtc/videosdk_webrtc.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter WebRTC Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: VideoCallScreen(),
    );
  }
}

class VideoCallScreen extends StatefulWidget {
  @override
  _VideoCallScreenState createState() => _VideoCallScreenState();
}

class _VideoCallScreenState extends State<VideoCallScreen> {
  late VideoRTC videoRTC;

  @override
  void initState() {
    super.initState();
    initWebRTC();
  }

  void initWebRTC() {
    videoRTC = VideoRTC(
      config: RTCConfiguration(
        // 配置你的STUN/TURN服务器信息
        iceServers: [
          RTCIceServer(urls: ['stun:stun.l.google.com:19302']),
          // 如果需要TURN服务器,请添加相应的配置
          // RTCIceServer(
          //   urls: ['turn:your-turn-server-url'],
          //   username: 'your-username',
          //   credential: 'your-credential',
          // ),
        ],
      ),
      onLocalStream: (MediaStream stream) {
        // 处理本地视频流
        print('Local stream available');
      },
      onRemoteStream: (MediaStream stream, String peerId) {
        // 处理远程视频流
        print('Remote stream available from $peerId');
      },
      onError: (String errorMessage) {
        // 处理错误
        print('Error: $errorMessage');
      },
    );

    // 开始获取本地媒体流
    videoRTC.startLocalMediaStream();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter WebRTC Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 显示本地视频
            Container(
              width: 300,
              height: 400,
              child: videoRTC.localVideoRenderer != null
                  ? RTCVideoRendererWidget(videoRenderer: videoRTC.localVideoRenderer!)
                  : Center(child: CircularProgressIndicator()),
            ),
            SizedBox(height: 20),
            // 显示远程视频(假设只有一个远程对等体)
            Container(
              width: 300,
              height: 400,
              child: videoRTC.remoteVideoRenderers.isNotEmpty
                  ? RTCVideoRendererWidget(videoRenderer: videoRTC.remoteVideoRenderers.first)
                  : Container(),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // 连接到远程对等体(这里需要实现具体的信令机制来交换SDP和ICE候选)
                // 例如,通过WebSocket或其他方式发送offer/answer
                // connectToRemotePeer();
              },
              child: Text('Connect to Remote Peer'),
            ),
          ],
        ),
      ),
    );
  }

  // 这是一个假设的方法,用于连接到远程对等体
  // 你需要实现自己的信令机制来交换SDP和ICE候选
  void connectToRemotePeer() async {
    // 创建offer
    RTCSessionDescription offer = await videoRTC.createOffer();
    // 设置本地描述
    await videoRTC.setLocalDescription(offer);
    // 发送offer到远程对等体(通过信令服务器)
    // ...
    
    // 接收remote description(answer)并设置
    // RTCSessionDescription answer = ...; // 从信令服务器接收
    // await videoRTC.setRemoteDescription(answer);
    
    // 添加ICE候选(从信令服务器接收)
    // videoRTC.addIceCandidate(...);
  }

  @override
  void dispose() {
    videoRTC.dispose();
    super.dispose();
  }
}

注意事项:

  1. 信令机制:上述代码中的connectToRemotePeer方法是一个占位符,你需要实现自己的信令机制来交换SDP和ICE候选。这通常通过WebSocket、Firebase或其他实时通信服务来完成。

  2. 错误处理:在实际应用中,需要更全面的错误处理逻辑,以确保在各种网络条件下都能稳定工作。

  3. UI优化:示例中的UI非常简单,实际项目中可能需要更复杂的布局和交互设计。

  4. 权限处理:在Android和iOS上,你需要确保在AndroidManifest.xmlInfo.plist中添加了必要的权限,以允许应用访问摄像头和麦克风。

  5. TURN服务器:如果你的应用需要在NAT后面进行通信,可能需要配置TURN服务器。TURN服务器通常需要付费,并且会增加延迟。

  6. 依赖版本:确保使用最新版本的videosdk_webrtc插件,并查阅其官方文档以获取最新的API和配置信息。

这个示例提供了一个基本的框架,你可以在此基础上根据具体需求进行扩展和优化。

回到顶部