Skip to content

Commit bbc798e

Browse files
authored
Merge pull request #43 from GetStream/chore/sync_1.2.0
chore: bump webrtc to m137 + sync with flutter_webrtc 1.2.0
2 parents 8002e09 + 1a595d2 commit bbc798e

File tree

136 files changed

+4341
-5409
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+4341
-5409
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ jobs:
6161
channel: 'stable'
6262
- name: Install project dependencies
6363
run: flutter pub get
64+
- name: Install xcode platform support for iOS
65+
run: xcodebuild -downloadPlatform iOS
6466
- name: Build for iOS
6567
working-directory: ./example
6668
run: flutter build ios --release --no-codesign

.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,22 @@ example/ios/Flutter/flutter_export_environment.sh
2626
.buildlog/
2727
.history
2828
.svn/
29+
migrate_working_dir/
2930

3031
# IntelliJ related
3132
*.iml
3233
*.ipr
3334
*.iws
3435
.idea/
3536

37+
# The .vscode folder contains launch configuration and tasks you configure in
38+
# VS Code which you may wish to be included in version control, so this line
39+
# is commented out by default.
40+
#.vscode/
41+
3642
# Flutter/Dart/Pub related
43+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
44+
/pubspec.lock
3745
**/doc/api/
3846
.dart_tool/
3947
.flutter-plugins
@@ -56,4 +64,5 @@ android/.settings/org.eclipse.buildship.core.prefs
5664
!webrtc.iml
5765

5866
# vs
59-
*.pdb
67+
*.pdb
68+
ios/Frameworks

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11

22
# Changelog
33

4+
[1.1.0]
5+
* Synced flutter-webrtc v0.14.2
6+
* [Doc] fix: typo in package description (#1895)
7+
* [Android] fix: Video recording crashing and freezing on Android 14 Devices (#1886)
8+
* [Android] fix: Add audio recording for Android Platform (#1884)
9+
* [Dart] fix: Removed outdated code to avoid UI not being displayed in Windows release mode (#1890)
10+
* [Apple] fix: Fix compile warnings (#1887)
11+
* [Android] fix: Ensure both video and audio tracks are added before starting the muxer (#1879)
12+
* [Apple/Android] feat: Add H265/HEVC support.
13+
* [Mobile/Desktop] feat: Support write logs with Logger (logger package) (#1891)
14+
* [Android] fix: Reduce Recording Stop Delay and Prevent Encoder OOM Crashes (Android) (#1912)
15+
* [Native/Web] feat: small setVolume addition (#1904)
16+
* [Web] feat: Add texture-based video rendering for web (#1911)
17+
* [Android] fix: RECORDINGS - Add fallback resolutions for unsupported stream frame sizes on low-end Android devices (#1900)
18+
* [Android] fix: Update proguard-rules.pro (#1902)
19+
* [Android] upgrade compileSdk to 36 as standard for 16kb pages support (#1925)
20+
* [Apple/Android] Local recording API for Darwin and Android (#1880)
21+
* [Apple/Android] Data Packet Cryptor Support.
22+
423
[1.0.13] - 2025-10-28
524
* Exposed `eventStream` in `MediaDevices`.
625
* [Android] Sends event when screen capture ends.

android/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ android {
2929
if (project.android.hasProperty("namespace")) {
3030
namespace 'io.getstream.webrtc.flutter'
3131
}
32-
compileSdkVersion 31
32+
compileSdkVersion 36
3333

3434
defaultConfig {
3535
minSdkVersion 21
@@ -47,12 +47,12 @@ android {
4747
}
4848

4949
kotlinOptions {
50-
jvmTarget = '1.8'
50+
jvmTarget = JavaVersion.VERSION_1_8
5151
}
5252
}
5353

5454
dependencies {
55-
implementation 'io.getstream:stream-webrtc-android:1.3.8'
55+
implementation 'io.getstream:stream-video-webrtc-android:137.0.1'
5656
implementation 'com.github.davidliu:audioswitch:89582c47c9a04c62f90aa5e57251af4800a62c9a'
5757
implementation 'androidx.annotation:annotation:1.1.0'
5858
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

android/gradle.properties

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
org.gradle.jvmargs=-Xmx1536M
1+
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
22
android.useAndroidX=true
33
android.enableJetifier=true
4-

android/proguard-rules.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Flutter WebRTC
22
-keep class io.getstream.webrtc.flutter.** { *; }
33
-keep class org.webrtc.** { *; }
4+
-keep class org.jni_zero.** { *; }
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package io.getstream.webrtc.flutter;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import io.getstream.webrtc.flutter.utils.ConstraintsMap;
6+
7+
import org.webrtc.DataPacketCryptor;
8+
import org.webrtc.DataPacketCryptorFactory;
9+
import org.webrtc.FrameCryptor;
10+
import org.webrtc.FrameCryptorAlgorithm;
11+
import org.webrtc.FrameCryptorKeyProvider;
12+
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.UUID;
16+
17+
import io.flutter.plugin.common.MethodCall;
18+
import io.flutter.plugin.common.MethodChannel;
19+
20+
class FlutterDataPacketCryptor {
21+
private static final String TAG = "FlutterDataPacketCryptor";
22+
private final Map<String, DataPacketCryptor> dataCryptos = new HashMap<>();
23+
24+
private final FlutterRTCFrameCryptor frameCryptor;
25+
26+
public FlutterDataPacketCryptor(FlutterRTCFrameCryptor frameCryptor) {
27+
this.frameCryptor = frameCryptor;
28+
}
29+
30+
public boolean handleMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
31+
String method_name = call.method;
32+
Map<String, Object> params = (Map<String, Object>) call.arguments;
33+
if (method_name.equals("createDataPacketCryptor")) {
34+
createDataPacketCryptor(params, result);
35+
} else if (method_name.equals("dataPacketCryptorEncrypt")) {
36+
dataPacketCryptorEncrypt(params, result);
37+
} else if (method_name.equals("dataPacketCryptorDecrypt")) {
38+
dataPacketCryptorDecrypt(params, result);
39+
} else if (method_name.equals("dataPacketCryptorDispose")) {
40+
dataPacketCryptorDispose(params, result);
41+
} else {
42+
return false;
43+
}
44+
return true;
45+
}
46+
47+
private void createDataPacketCryptor(@NonNull Map<String, Object> params, @NonNull MethodChannel.Result result) {
48+
String keyProviderId = (String) params.get("keyProviderId");
49+
FrameCryptorKeyProvider keyProvider = frameCryptor.getKeyProvider(keyProviderId);
50+
if (keyProvider == null) {
51+
result.error("createDataPacketCryptorFailed", "keyProvider not found", null);
52+
return;
53+
}
54+
55+
if (params.get("algorithm") == null) {
56+
result.error("createDataPacketCryptorFailed", "algorithm is null", null);
57+
return;
58+
}
59+
60+
int algorithm = (int) params.get("algorithm");
61+
62+
DataPacketCryptor dataPacketCryptor = DataPacketCryptorFactory.createDataPacketCryptor(
63+
frameCryptor.frameCryptorAlgorithmFromInt(algorithm),
64+
keyProvider);
65+
if (dataPacketCryptor == null) {
66+
result.error("createDataPacketCryptorFailed", "createDataPacketCryptor failed", null);
67+
return;
68+
}
69+
70+
String dataCryptorId = UUID.randomUUID().toString();
71+
dataCryptos.put(dataCryptorId, dataPacketCryptor);
72+
73+
ConstraintsMap paramsResult = new ConstraintsMap();
74+
paramsResult.putString("dataCryptorId", dataCryptorId);
75+
result.success(paramsResult.toMap());
76+
}
77+
78+
private void dataPacketCryptorEncrypt(@NonNull Map<String, Object> params, @NonNull MethodChannel.Result result) {
79+
String dataCryptorId = (String) params.get("dataCryptorId");
80+
if (dataCryptorId == null) {
81+
result.error("dataPacketCryptorEncryptFailed", "dataCryptorId is null", null);
82+
return;
83+
}
84+
85+
DataPacketCryptor dataPacketCryptor = dataCryptos.get(dataCryptorId);
86+
if (dataPacketCryptor == null) {
87+
result.error("dataPacketCryptorEncryptFailed", "dataPacketCryptor not found", null);
88+
return;
89+
}
90+
91+
String participantId = (String) params.get("participantId");
92+
if (participantId == null) {
93+
result.error("dataPacketCryptorEncryptFailed", "participantId is null", null);
94+
return;
95+
}
96+
97+
byte[] data = (byte[]) params.get("data");
98+
if (data == null) {
99+
result.error("dataPacketCryptorEncryptFailed", "data is null", null);
100+
return;
101+
}
102+
103+
int keyIndex = (int) params.get("keyIndex");
104+
if (keyIndex < 0) {
105+
result.error("dataPacketCryptorEncryptFailed", "keyIndex is invalid", null);
106+
return;
107+
}
108+
109+
DataPacketCryptor.EncryptedPacket packet = dataPacketCryptor.encrypt(participantId, keyIndex, data);
110+
ConstraintsMap paramsResult = new ConstraintsMap();
111+
paramsResult.putInt("keyIndex", packet.keyIndex);
112+
paramsResult.putByte("data", packet.payload);
113+
paramsResult.putByte("iv", packet.iv);
114+
result.success(paramsResult.toMap());
115+
}
116+
117+
private void dataPacketCryptorDecrypt(@NonNull Map<String, Object> params, @NonNull MethodChannel.Result result) {
118+
String dataCryptorId = (String) params.get("dataCryptorId");
119+
if (dataCryptorId == null) {
120+
result.error("dataPacketCryptorEncryptFailed", "dataCryptorId is null", null);
121+
return;
122+
}
123+
124+
DataPacketCryptor dataPacketCryptor = dataCryptos.get(dataCryptorId);
125+
if (dataPacketCryptor == null) {
126+
result.error("dataPacketCryptorEncryptFailed", "dataPacketCryptor not found", null);
127+
return;
128+
}
129+
130+
String participantId = (String) params.get("participantId");
131+
if (participantId == null) {
132+
result.error("dataPacketCryptorEncryptFailed", "participantId is null", null);
133+
return;
134+
}
135+
136+
byte[] data = (byte[]) params.get("data");
137+
if (data == null) {
138+
result.error("dataPacketCryptorEncryptFailed", "data is null", null);
139+
return;
140+
}
141+
142+
byte[] iv = (byte[]) params.get("iv");
143+
if (iv == null) {
144+
result.error("dataPacketCryptorEncryptFailed", "iv is null", null);
145+
return;
146+
}
147+
148+
int keyIndex = (int) params.get("keyIndex");
149+
if (keyIndex < 0) {
150+
result.error("dataPacketCryptorEncryptFailed", "keyIndex is invalid", null);
151+
return;
152+
}
153+
DataPacketCryptor.EncryptedPacket encryptedPacket = new DataPacketCryptor.EncryptedPacket(data, iv, keyIndex);
154+
byte[] decrypted = dataPacketCryptor.decrypt(participantId, encryptedPacket);
155+
if (decrypted == null) {
156+
result.error("dataPacketCryptorDecryptFailed", "decrypt failed", null);
157+
return;
158+
}
159+
160+
ConstraintsMap paramsResult = new ConstraintsMap();
161+
paramsResult.putByte("data", decrypted);
162+
result.success(paramsResult.toMap());
163+
}
164+
165+
private void dataPacketCryptorDispose(@NonNull Map<String, Object> params, @NonNull MethodChannel.Result result) {
166+
String dataCryptorId = (String) params.get("dataCryptorId");
167+
if (dataCryptorId == null) {
168+
result.error("dataPacketCryptorDisposeFailed", "dataCryptorId is null", null);
169+
return;
170+
}
171+
172+
DataPacketCryptor dataPacketCryptor = dataCryptos.remove(dataCryptorId);
173+
if (dataPacketCryptor == null) {
174+
result.error("dataPacketCryptorDisposeFailed", "dataPacketCryptor not found", null);
175+
return;
176+
}
177+
178+
if (dataPacketCryptor != null) {
179+
dataPacketCryptor.dispose();
180+
}
181+
result.success(null);
182+
}
183+
}

android/src/main/java/io/getstream/webrtc/flutter/FlutterRTCFrameCryptor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public boolean handleMethodCall(MethodCall call, @NonNull Result result) {
148148
return true;
149149
}
150150

151-
private FrameCryptorAlgorithm frameCryptorAlgorithmFromInt(int algorithm) {
151+
public FrameCryptorAlgorithm frameCryptorAlgorithmFromInt(int algorithm) {
152152
switch (algorithm) {
153153
case 0:
154154
return FrameCryptorAlgorithm.AES_GCM;
@@ -210,7 +210,6 @@ private void frameCryptorFactoryCreateFrameCryptor(Map<String, Object> params, @
210210
result.success(paramsResult.toMap());
211211
} else {
212212
result.error("frameCryptorFactoryCreateFrameCryptorFailed", "type must be sender or receiver", null);
213-
return;
214213
}
215214
}
216215

@@ -431,4 +430,8 @@ private void keyProviderDispose(Map<String, Object> params, @NonNull Result resu
431430
paramsResult.putString("result", "success");
432431
result.success(paramsResult.toMap());
433432
}
433+
434+
public FrameCryptorKeyProvider getKeyProvider(String id) {
435+
return keyProviders.get(id);
436+
}
434437
}

android/src/main/java/io/getstream/webrtc/flutter/FlutterWebRTCPlugin.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ public class FlutterWebRTCPlugin implements FlutterPlugin, ActivityAware, EventC
4242
private LifeCycleObserver observer;
4343
private Lifecycle lifecycle;
4444
private EventChannel eventChannel;
45-
public EventChannel.EventSink eventSink;
45+
46+
// eventSink is static because FlutterWebRTCPlugin can be instantiated multiple times
47+
// but the onListen(Object, EventChannel.EventSink) event only fires once for the first
48+
// FlutterWebRTCPlugin instance, so for the next instances eventSink will be == null
49+
public static EventChannel.EventSink eventSink;
4650

4751
public FlutterWebRTCPlugin() {
4852
if (sharedSingleton == null) {

0 commit comments

Comments
 (0)