flutter-webrtc Documentation

WebRTC plugin for Flutter Mobile/Desktop/Web

4,498 stars C++ C++ 5 files ยท ~4,067 tokens #android#desktop#flutter#ios#sip#voip#web#webrtc
RAW Doc

Technical Documentation: flutter-webrtc/flutter-webrtc

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

Flutter-WebRTC

WebRTC plugin for Flutter Mobile/Desktop/Web

LiveKit - Open source WebRTC and realtime AI infrastructure

Functionality

Feature Android iOS Web macOS Windows Linux Embedded Fuchsia
Audio/Video :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
Data Channel :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
Screen Capture :heavy_check_mark: :heavy_check_mark:(*) :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
Unified-Plan :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
Simulcast :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
MediaRecorder :warning: :warning: :heavy_check_mark:
End to End Encryption :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
Insertable Streams

Additional platform/OS support from the other community

Add flutter_webrtc as a dependency in your pubspec.yaml file.

iOS/macOS: Swift Package Manager

The plugin supports both Swift Package Manager and CocoaPods on iOS and macOS. With Flutter 3.44 or later, Swift Package Manager is enabled by default and the plugin is consumed as a Swift package automatically. No project changes are needed.

CocoaPods remains fully supported and is used when Swift Package Manager is disabled or on older Flutter versions. The Podfile notes below apply to CocoaPods projects only.

iOS

Add the following entry to your Info.plist file, located in /ios/Runner/Info.plist:

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

This entry allows your app to access camera and microphone.

Note for iOS

The WebRTC.xframework compiled after the m104 release no longer supports iOS arm devices, so need to add the config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' to your ios/Podfile in your project

ios/Podfile

ruby
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' # <= this line
     end
  end
end

Android

Ensure the following permission is present in your Android Manifest file, located in /android/app/src/main/AndroidManifest.xml:

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" />

If you need to use a Bluetooth device, please add:

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

The Flutter project template adds it, so it may already be there.

Also you will need to set your build settings to Java 8, because official WebRTC jar now uses static methods in EglBase interface. Just add this to your app level build.gradle:

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

If necessary, in the same build.gradle you will need to increase minSdkVersion of defaultConfig up to 23 (currently default Flutter generator set it to 16).

Important reminder

When you compile the release apk, you need to add the following operations,
Setup Proguard Rules

Contributing

The project is inseparable from the contributors of the community.

Example

For more examples, please refer to flutter-webrtc-demo.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]











Overview (`example/README.md`)

webrtc_example

Demonstrates how to use the webrtc plugin.

Getting Started

Make sure your flutter is using the dev channel.

bash
flutter channel dev
./scripts/project_tools.sh create

Android/iOS

bash
flutter run

macOS

bash
flutter run -d macos

Web

bash
dart compile js ../web/e2ee.worker.dart -o web/e2ee.worker.dart.js
flutter run -d web

Windows

bash
flutter channel master
flutter create --platforms windows .
flutter run -d windows

Overview (`example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md`)

Launch Screen Assets

You can customize the launch screen with your own desired assets by replacing the image files in this directory.

You can also do it by opening your Flutter project's Xcode project with open ios/Runner.xcworkspace, selecting Runner/Assets.xcassets in the Project Navigator and dropping in the desired images.


Documentation E2EE (`Documentation/E2EE.md`)

End to End Encryption

E2EE is an AES-GCM encryption interface injected before sending the packaged RTP packet and after receiving the RTP packet, ensuring that the data is not eavesdropped when passing through SFU or any public transmission network. It coexists with DTLS-SRTP as two layers of encryption. You can control the key, ratchet and other operations of FrameCryptor yourself to ensure that no third party will monitor your tracks.

Process of enabling E2EE

1, Prepare the key provider

ratchetSalt is used to add to the mixture when ratcheting or deriving AES passwords
aesKey aesKey is the plaintext password you entered, which will be used to derive the actual password

dart
final aesKey = 'you-private-key-here'.codeUnits;
    final ratchetSalt = 'flutter-webrtc-ratchet-salt';

    var keyProviderOptions = KeyProviderOptions(
      sharedKey: true,
      ratchetSalt: Uint8List.fromList(ratchetSalt.codeUnits),
      ratchetWindowSize: 16,
      failureTolerance: -1,
    );

    var keyProvider = await frameCyrptorFactory.createDefaultKeyProvider(keyProviderOptions);
    /// set shared key for all track, default index is 0
    /// also you can set multiple keys by different indexes
    await keyProvider.setSharedKey(key: aesKey);

2, create PeerConnectioin

when you use E2EE on the web, please add encodedInsertableStreams,

dart
var pc = await createPeerConnection( {
        'encodedInsertableStreams': true,
        });

3, Enable FrameCryptor for RTPSender.

dart
var stream = await navigator.mediaDevices
        .getUserMedia({'audio': true, 'video': false });
var audioTrack = stream.getAudioTracks();
var sender = await pc.addTrack(audioTrack, stream);

var trackId = audioTrack?.id;
var id = 'audio_' + trackId! + '_sender';

var frameCyrptor =
            await frameCyrptorFactory.createFrameCryptorForRtpSender(
                participantId: id,
                sender: sender,
                algorithm: Algorithm.kAesGcm,
                keyProvider: keyProvider!);
/// print framecyrptor state
frameCyrptor.onFrameCryptorStateChanged = (participantId, state) =>
            print('EN onFrameCryptorStateChanged $participantId $state');

/// set currently shared key index
await frameCyrptor.setKeyIndex(0);

/// enable encryption now.
await frameCyrptor.setEnabled(true);

4, Enable FrameCryptor for RTPReceiver

dart
pc.onTrack((RTCTrackEvent event) async {
    var receiver = event.receiver;
    var trackId = event.track?.id;
    var id = event.track.kind + '_' + trackId! + '_receiver';

    var frameCyrptor =
                await frameCyrptorFactory.createFrameCryptorForRtpReceiver(
                    participantId: id,
                    receiver: receiver,
                    algorithm: Algorithm.kAesGcm,
                    keyProvider: keyProvider);
        
    frameCyrptor.onFrameCryptorStateChanged = (participantId, state) =>
            print('DE onFrameCryptorStateChanged $participantId $state');

    /// set currently shared key index
    await frameCyrptor.setKeyIndex(0);

    /// enable encryption now.
    await frameCyrptor.setEnabled(true);
});

Documentation Desktop Peerconnection Disposal (`Documentation/desktop-peerconnection-disposal.md`)

Desktop PeerConnection disposal regression check

example/lib/peer_connection_dispose_smoke.dart exercises native observer
teardown on Windows and Linux. It creates a remote audio stream from a synthetic
SDP offer, waits for onAddStream, then calls dispose() followed by close().
It repeats this sequence ten times. No account, microphone capture, ICE server,
or media connection is needed.

Run this with a desktop runner. flutter test uses mocked platform channels and
cannot exercise the native observer lifetime. From the repository root:

sh
cd example
flutter pub get
flutter run -d linux -t lib/peer_connection_dispose_smoke.dart

On Windows, replace -d linux with -d windows. Success prints:

text
PASS: 10 native PeerConnections disposed with remote audio

To check the native process exit code on Windows (PowerShell, from example):

powershell
flutter build windows --debug -t lib/peer_connection_dispose_smoke.dart
$process = Start-Process -FilePath .\build\windows\x64\runner\Debug\flutter_webrtc_example.exe -WindowStyle Hidden -Wait -PassThru
$process.ExitCode

The expected exit code is zero. A Dart failure exits with code 1. A native
access violation can terminate the process before Dart can report a failure.

Failure being exercised

In common/cpp/src/flutter_peerconnection.cc, RTCPeerConnectionDispose
previously erased peerconnection_observers_[uuid] while the native connection
still had that observer registered. A later RTCPeerConnectionClose called
libwebrtc Close(), which can deliver OnRemoveStream for remaining remote
streams. That callback could access the deleted observer.

Disposal now closes the native connection while its observer is still alive,
unregisters the observer, then erases it. The connection map entry remains for
the subsequent peerConnectionClose call, preserving that cleanup sequence.

Restore the regular example entry point after the check:

sh
flutter run -d windows -t lib/main.dart

Use -d linux for the Linux example.


--- METRICS ---