react-native-webrtc-nitro 1.1.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/configuration.hpp +1 -1
  2. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/rtc.h +9 -1
  3. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/rtcpreceivingsession.hpp +26 -1
  4. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/rtp.hpp +1 -1
  5. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/rtppacketizationconfig.hpp +9 -0
  6. package/3rdparty/output/android/libdatachannel/arm64-v8a/include/rtc/version.h +3 -3
  7. package/3rdparty/output/android/libdatachannel/arm64-v8a/lib/libdatachannel.so +0 -0
  8. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/configuration.hpp +1 -1
  9. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/rtc.h +9 -1
  10. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/rtcpreceivingsession.hpp +26 -1
  11. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/rtp.hpp +1 -1
  12. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/rtppacketizationconfig.hpp +9 -0
  13. package/3rdparty/output/android/libdatachannel/armeabi-v7a/include/rtc/version.h +3 -3
  14. package/3rdparty/output/android/libdatachannel/armeabi-v7a/lib/libdatachannel.so +0 -0
  15. package/3rdparty/output/android/libdatachannel/x86/include/rtc/configuration.hpp +1 -1
  16. package/3rdparty/output/android/libdatachannel/x86/include/rtc/rtc.h +9 -1
  17. package/3rdparty/output/android/libdatachannel/x86/include/rtc/rtcpreceivingsession.hpp +26 -1
  18. package/3rdparty/output/android/libdatachannel/x86/include/rtc/rtp.hpp +1 -1
  19. package/3rdparty/output/android/libdatachannel/x86/include/rtc/rtppacketizationconfig.hpp +9 -0
  20. package/3rdparty/output/android/libdatachannel/x86/include/rtc/version.h +3 -3
  21. package/3rdparty/output/android/libdatachannel/x86/lib/libdatachannel.so +0 -0
  22. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/configuration.hpp +1 -1
  23. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/rtc.h +9 -1
  24. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/rtcpreceivingsession.hpp +26 -1
  25. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/rtp.hpp +1 -1
  26. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/rtppacketizationconfig.hpp +9 -0
  27. package/3rdparty/output/android/libdatachannel/x86_64/include/rtc/version.h +3 -3
  28. package/3rdparty/output/android/libdatachannel/x86_64/lib/libdatachannel.so +0 -0
  29. package/3rdparty/output/ios/ffmpeg.xcframework/ios-arm64/libffmpeg.a +0 -0
  30. package/3rdparty/output/ios/ffmpeg.xcframework/ios-arm64_x86_64-simulator/libffmpeg.a +0 -0
  31. package/3rdparty/output/ios/libdatachannel.xcframework/Info.plist +5 -5
  32. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/configuration.hpp +1 -1
  33. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/rtc.h +9 -1
  34. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/rtcpreceivingsession.hpp +26 -1
  35. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/rtp.hpp +1 -1
  36. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/rtppacketizationconfig.hpp +9 -0
  37. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/Headers/rtc/version.h +3 -3
  38. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64/libdatachannel.a +0 -0
  39. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/configuration.hpp +1 -1
  40. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/rtc.h +9 -1
  41. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/rtcpreceivingsession.hpp +26 -1
  42. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/rtp.hpp +1 -1
  43. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/rtppacketizationconfig.hpp +9 -0
  44. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/Headers/rtc/version.h +3 -3
  45. package/3rdparty/output/ios/libdatachannel.xcframework/ios-arm64_x86_64-simulator/libdatachannel.a +0 -0
  46. package/README.md +563 -13
  47. package/android/src/main/java/com/webrtc/HybridCamera.kt +0 -1
  48. package/android/src/main/java/com/webrtc/HybridMicrophone.kt +0 -1
  49. package/android/src/main/java/com/webrtc/HybridPermissions.kt +95 -0
  50. package/cpp/Hybrid/HybridMediaDevices.cpp +23 -19
  51. package/ios/HybridCamera.swift +1 -3
  52. package/ios/HybridMicrophone.swift +1 -3
  53. package/ios/HybridPermissions.swift +63 -0
  54. package/lib/commonjs/index.js +11 -0
  55. package/lib/commonjs/index.js.map +1 -1
  56. package/lib/commonjs/specs/Permissions.nitro.js +9 -0
  57. package/lib/commonjs/specs/Permissions.nitro.js.map +1 -0
  58. package/lib/module/index.js +1 -0
  59. package/lib/module/index.js.map +1 -1
  60. package/lib/module/specs/Permissions.nitro.js +6 -0
  61. package/lib/module/specs/Permissions.nitro.js.map +1 -0
  62. package/lib/typescript/src/index.d.ts +1 -0
  63. package/lib/typescript/src/index.d.ts.map +1 -1
  64. package/lib/typescript/src/specs/Permissions.nitro.d.ts +17 -0
  65. package/lib/typescript/src/specs/Permissions.nitro.d.ts.map +1 -0
  66. package/nitro.json +4 -0
  67. package/nitrogen/generated/android/Webrtc+autolinking.cmake +2 -0
  68. package/nitrogen/generated/android/WebrtcOnLoad.cpp +10 -0
  69. package/nitrogen/generated/android/c++/JHybridPermissionsSpec.cpp +91 -0
  70. package/nitrogen/generated/android/c++/JHybridPermissionsSpec.hpp +66 -0
  71. package/nitrogen/generated/android/c++/JPermissionDescriptor.hpp +58 -0
  72. package/nitrogen/generated/android/c++/JPermissionName.hpp +59 -0
  73. package/nitrogen/generated/android/c++/JPermissionState.hpp +62 -0
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/webrtc/HybridPermissionsSpec.kt +62 -0
  75. package/nitrogen/generated/android/kotlin/com/margelo/nitro/webrtc/PermissionDescriptor.kt +38 -0
  76. package/nitrogen/generated/android/kotlin/com/margelo/nitro/webrtc/PermissionName.kt +21 -0
  77. package/nitrogen/generated/android/kotlin/com/margelo/nitro/webrtc/PermissionState.kt +22 -0
  78. package/nitrogen/generated/ios/Webrtc-Swift-Cxx-Bridge.cpp +25 -0
  79. package/nitrogen/generated/ios/Webrtc-Swift-Cxx-Bridge.hpp +63 -0
  80. package/nitrogen/generated/ios/Webrtc-Swift-Cxx-Umbrella.hpp +14 -0
  81. package/nitrogen/generated/ios/WebrtcAutolinking.mm +8 -0
  82. package/nitrogen/generated/ios/WebrtcAutolinking.swift +15 -0
  83. package/nitrogen/generated/ios/c++/HybridPermissionsSpecSwift.cpp +11 -0
  84. package/nitrogen/generated/ios/c++/HybridPermissionsSpecSwift.hpp +92 -0
  85. package/nitrogen/generated/ios/swift/Func_void_PermissionState.swift +47 -0
  86. package/nitrogen/generated/ios/swift/HybridPermissionsSpec.swift +57 -0
  87. package/nitrogen/generated/ios/swift/HybridPermissionsSpec_cxx.swift +157 -0
  88. package/nitrogen/generated/ios/swift/PermissionDescriptor.swift +36 -0
  89. package/nitrogen/generated/ios/swift/PermissionName.swift +40 -0
  90. package/nitrogen/generated/ios/swift/PermissionState.swift +44 -0
  91. package/nitrogen/generated/shared/c++/HybridPermissionsSpec.cpp +22 -0
  92. package/nitrogen/generated/shared/c++/HybridPermissionsSpec.hpp +68 -0
  93. package/nitrogen/generated/shared/c++/PermissionDescriptor.hpp +76 -0
  94. package/nitrogen/generated/shared/c++/PermissionName.hpp +76 -0
  95. package/nitrogen/generated/shared/c++/PermissionState.hpp +80 -0
  96. package/package.json +4 -5
  97. package/src/index.ts +1 -0
  98. package/src/specs/Permissions.nitro.ts +22 -0
  99. package/android/src/main/java/com/webrtc/Permission.kt +0 -58
  100. package/ios/Permission.swift +0 -26
package/README.md CHANGED
@@ -1,29 +1,579 @@
1
1
  # react-native-webrtc-nitro
2
2
 
3
- Nitro WebRTC module for React Native
4
-
5
- [![Version](https://img.shields.io/npm/v/react-native-webrtc-nitro.svg)](https://www.npmjs.com/package/react-native-webrtc)
3
+ [![Version](https://img.shields.io/npm/v/react-native-webrtc-nitro.svg)](https://www.npmjs.com/package/react-native-webrtc-nitro)
6
4
  [![Downloads](https://img.shields.io/npm/dm/react-native-webrtc-nitro.svg)](https://www.npmjs.com/package/react-native-webrtc-nitro)
7
- [![License](https://img.shields.io/npm/l/react-native-webrtc-nitro.svg)](https://github.com/patrickkabwe/react-native-webrtc-nitro/LICENSE)
5
+ [![License](https://img.shields.io/npm/l/react-native-webrtc-nitro.svg)](https://github.com/SingTown/react-native-webrtc-nitro/blob/main/LICENSE)
6
+
7
+ A high-performance WebRTC library built on [Nitro Modules](https://github.com/mrousavy/nitro), providing powerful real-time audio and video communication capabilities for React Native applications.
8
+
9
+ ## ✨ Features
8
10
 
9
- ## Requirements
11
+ - 🚀 **High Performance**: Built on Nitro Modules for native-level performance
12
+ - 🎥 **Complete WebRTC API**: Support for audio/video calls, data channels, and more
13
+ - 📹 **Media Recording**: Built-in MediaRecorder support for recording audio/video streams
14
+ - 🎬 **FFmpeg Integration**: Powerful audio/video encoding, decoding, transcoding, and processing capabilities
15
+ - 🎞️ **H.264/H.265 Support**: Hardware-accelerated H.264 and H.265 (HEVC) encoding and decoding
16
+ - 🎵 **Opus Audio Codec**: High-quality Opus audio encoding and decoding for efficient audio streaming
17
+ - 📱 **Cross-Platform**: Perfect support for iOS and Android
18
+ - 🔧 **TypeScript**: Full type definitions
19
+ - 🎨 **Nitro Views**: High-performance video rendering views
10
20
 
11
- - React Native v0.76.0 or higher
12
- - Node 18.0.0 or higher
21
+ ## 📋 Requirements
22
+
23
+ - React Native >= 0.76.0
24
+ - Node.js >= 18.0.0
25
+ - iOS >= 13.0
26
+ - Android API >= 24
13
27
 
14
28
  > [!IMPORTANT]
15
- > To Support `Nitro Views` you need to install React Native version v0.78.0 or higher.
29
+ > To use `Nitro Views` features, React Native >= 0.78.0 is required
30
+
31
+ ## 📦 Installation
16
32
 
17
- ## Installation
33
+ Using pnpm:
18
34
 
19
35
  ```bash
20
36
  pnpm add react-native-webrtc-nitro react-native-nitro-modules
21
37
  ```
22
38
 
23
- ## Credits
39
+ Using npm:
40
+
41
+ ```bash
42
+ npm install react-native-webrtc-nitro react-native-nitro-modules
43
+ ```
44
+
45
+ Using yarn:
46
+
47
+ ```bash
48
+ yarn add react-native-webrtc-nitro react-native-nitro-modules
49
+ ```
50
+
51
+ ### iOS Configuration
52
+
53
+ ```bash
54
+ cd ios && pod install
55
+ ```
56
+
57
+ Add permission descriptions to `Info.plist`:
58
+
59
+ ```xml
60
+ <key>NSCameraUsageDescription</key>
61
+ <string>Camera access is required for video calls</string>
62
+ <key>NSMicrophoneUsageDescription</key>
63
+ <string>Microphone access is required for audio calls</string>
64
+ ```
65
+
66
+ ### Android Configuration
67
+
68
+ Add permissions to `AndroidManifest.xml`:
69
+
70
+ ```xml
71
+ <uses-permission android:name="android.permission.CAMERA" />
72
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
73
+ <uses-permission android:name="android.permission.INTERNET" />
74
+ ```
75
+
76
+ ## 🚀 Quick Start
77
+
78
+ ### Create Offer (Caller Side)
79
+
80
+ ```typescript
81
+ import {
82
+ MediaDevices,
83
+ RTCPeerConnection,
84
+ Permissions
85
+ } from 'react-native-webrtc-nitro';
86
+
87
+ // 1. Check and request permissions
88
+ let cameraPermission = await Permissions.query({ name: 'camera' });
89
+ if (cameraPermission !== 'granted') {
90
+ cameraPermission = await Permissions.request({ name: 'camera' });
91
+ }
92
+
93
+ let microphonePermission = await Permissions.query({ name: 'microphone' });
94
+ if (microphonePermission !== 'granted') {
95
+ microphonePermission = await Permissions.request({ name: 'microphone' });
96
+ }
97
+
98
+ if (cameraPermission !== 'granted' || microphonePermission !== 'granted') {
99
+ console.error('Camera and microphone permissions are required');
100
+ return;
101
+ }
102
+
103
+ // 2. Get local media stream
104
+ const localStream = await MediaDevices.getUserMedia({
105
+ audio: true,
106
+ video: true
107
+ });
108
+
109
+ // 3. Create RTCPeerConnection
110
+ const peerConnection = new RTCPeerConnection({
111
+ iceServers: [
112
+ { urls: 'stun:stun.l.google.com:19302' }
113
+ ]
114
+ });
115
+
116
+ // 4. Add local stream to connection
117
+ localStream.getTracks().forEach(track => {
118
+ peerConnection.addTransceiver(track, {
119
+ direction: 'sendrecv',
120
+ streams: [localStream]
121
+ });
122
+ });
123
+
124
+ // 5. Handle ICE candidates
125
+ peerConnection.onicecandidate = (event) => {
126
+ if (event.candidate) {
127
+ // Send ICE candidate to remote peer via signaling server
128
+ signalingServer.send({
129
+ type: 'candidate',
130
+ candidate: event.candidate
131
+ });
132
+ }
133
+ };
134
+
135
+ // 6. Handle remote stream
136
+ peerConnection.ontrack = (event) => {
137
+ const remoteStream = event.streams[0];
138
+ setRemoteStream(remoteStream);
139
+ };
140
+
141
+ // 7. Create and send offer
142
+ const offer = await peerConnection.createOffer();
143
+ await peerConnection.setLocalDescription(offer);
144
+
145
+ // Send offer to remote peer via signaling server
146
+ signalingServer.send({
147
+ type: 'offer',
148
+ sdp: offer.sdp
149
+ });
150
+
151
+ // 8. Receive answer from remote peer
152
+ signalingServer.on('answer', async (answer) => {
153
+ await peerConnection.setRemoteDescription({
154
+ type: 'answer',
155
+ sdp: answer.sdp
156
+ });
157
+ });
158
+
159
+ // 9. Receive ICE candidates from remote peer
160
+ signalingServer.on('candidate', async (candidate) => {
161
+ await peerConnection.addIceCandidate({
162
+ candidate: candidate.candidate,
163
+ sdpMid: candidate.sdpMid
164
+ });
165
+ });
166
+ ```
167
+
168
+ ### Create Answer (Callee Side)
169
+
170
+ ```typescript
171
+ import {
172
+ MediaDevices,
173
+ RTCPeerConnection,
174
+ Permissions
175
+ } from 'react-native-webrtc-nitro';
176
+
177
+ // 1. Check and request permissions
178
+ let cameraPermission = await Permissions.query({ name: 'camera' });
179
+ if (cameraPermission !== 'granted') {
180
+ cameraPermission = await Permissions.request({ name: 'camera' });
181
+ }
182
+
183
+ let microphonePermission = await Permissions.query({ name: 'microphone' });
184
+ if (microphonePermission !== 'granted') {
185
+ microphonePermission = await Permissions.request({ name: 'microphone' });
186
+ }
187
+
188
+ if (cameraPermission !== 'granted' || microphonePermission !== 'granted') {
189
+ console.error('Camera and microphone permissions are required');
190
+ return;
191
+ }
192
+
193
+ // 2. Get local media stream
194
+ const localStream = await MediaDevices.getUserMedia({
195
+ audio: true,
196
+ video: true
197
+ });
198
+
199
+ // 3. Create RTCPeerConnection
200
+ const peerConnection = new RTCPeerConnection({
201
+ iceServers: [
202
+ { urls: 'stun:stun.l.google.com:19302' }
203
+ ]
204
+ });
205
+
206
+ // 4. Add local stream to connection
207
+ localStream.getTracks().forEach(track => {
208
+ peerConnection.addTransceiver(track, {
209
+ direction: 'sendrecv',
210
+ streams: [localStream]
211
+ });
212
+ });
213
+
214
+ // 5. Handle ICE candidates
215
+ peerConnection.onicecandidate = (event) => {
216
+ if (event.candidate) {
217
+ // Send ICE candidate to remote peer via signaling server
218
+ signalingServer.send({
219
+ type: 'candidate',
220
+ candidate: event.candidate
221
+ });
222
+ }
223
+ };
224
+
225
+ // 6. Handle remote stream
226
+ peerConnection.ontrack = (event) => {
227
+ const remoteStream = event.streams[0];
228
+ setRemoteStream(remoteStream);
229
+ };
230
+
231
+ // 7. Receive offer from remote peer
232
+ signalingServer.on('offer', async (offer) => {
233
+ // Set remote description
234
+ await peerConnection.setRemoteDescription({
235
+ type: 'offer',
236
+ sdp: offer.sdp
237
+ });
238
+
239
+ // Create and send answer
240
+ const answer = await peerConnection.createAnswer();
241
+ await peerConnection.setLocalDescription(answer);
242
+
243
+ // Send answer back to caller via signaling server
244
+ signalingServer.send({
245
+ type: 'answer',
246
+ sdp: answer.sdp
247
+ });
248
+ });
249
+
250
+ // 8. Receive ICE candidates from remote peer
251
+ signalingServer.on('candidate', async (candidate) => {
252
+ await peerConnection.addIceCandidate({
253
+ candidate: candidate.candidate,
254
+ sdpMid: candidate.sdpMid
255
+ });
256
+ });
257
+ ```
258
+
259
+ ### Video Rendering
260
+
261
+ ```typescript
262
+ import { WebrtcView } from 'react-native-webrtc-nitro';
263
+
264
+ function VideoCall() {
265
+ const [localStream, setLocalStream] = useState<MediaStream | null>(null);
266
+ const [remoteStream, setRemoteStream] = useState<MediaStream | null>(null);
267
+
268
+ return (
269
+ <View style={styles.container}>
270
+ {/* Local video */}
271
+ <WebrtcView
272
+ style={styles.localVideo}
273
+ stream={localStream}
274
+ />
275
+
276
+ {/* Remote video */}
277
+ <WebrtcView
278
+ style={styles.remoteVideo}
279
+ stream={remoteStream}
280
+ />
281
+ </View>
282
+ );
283
+ }
284
+ ```
285
+
286
+ ### Media Recording
287
+
288
+ ```typescript
289
+ import { MediaRecorder } from 'react-native-webrtc-nitro';
290
+ import RNFS from 'react-native-fs';
291
+
292
+ // Create recorder with media stream
293
+ const recorder = new MediaRecorder(mediaStream);
294
+
295
+ // Take a photo from the stream
296
+ const photoPath = `${RNFS.DocumentDirectoryPath}/photo.jpg`;
297
+ await recorder.takePhoto(photoPath);
298
+ console.log('Photo saved to:', photoPath);
299
+
300
+ // Start video recording
301
+ const videoPath = `${RNFS.DocumentDirectoryPath}/video.mp4`;
302
+ recorder.startRecording(videoPath);
303
+
304
+ // Stop recording after some time
305
+ setTimeout(() => {
306
+ recorder.stopRecording();
307
+ console.log('Video saved to:', videoPath);
308
+ }, 10000);
309
+ ```
310
+
311
+ ## 📚 API Documentation
312
+
313
+ ### Permissions
314
+
315
+ Permission management module
316
+
317
+ ```typescript
318
+ // Query permission status
319
+ const cameraStatus = await Permissions.query({ name: 'camera' });
320
+ const microphoneStatus = await Permissions.query({ name: 'microphone' });
321
+ // Returns: 'granted' | 'denied' | 'prompt'
322
+
323
+ // Request permissions
324
+ const cameraPermission = await Permissions.request({ name: 'camera' });
325
+ const microphonePermission = await Permissions.request({ name: 'microphone' });
326
+ // Returns: 'granted' | 'denied' | 'prompt'
327
+ ```
328
+
329
+ ### MediaDevices
330
+
331
+ Media device access module
332
+
333
+ ```typescript
334
+ // Get user media
335
+ const stream = await MediaDevices.getUserMedia({
336
+ audio: true,
337
+ video: true
338
+ });
339
+
340
+ // Get audio only
341
+ const audioStream = await MediaDevices.getUserMedia({
342
+ audio: true,
343
+ video: false
344
+ });
345
+
346
+ // Get video only
347
+ const videoStream = await MediaDevices.getUserMedia({
348
+ audio: false,
349
+ video: true
350
+ });
351
+
352
+ // Get mock media for testing
353
+ const mockStream = await MediaDevices.getMockMedia({
354
+ audio: true,
355
+ video: true
356
+ });
357
+ ```
358
+
359
+ ### MediaStream
360
+
361
+ Media stream management
362
+
363
+ ```typescript
364
+ // Get stream ID
365
+ const streamId = stream.id;
366
+
367
+ // Get all tracks
368
+ const allTracks = stream.getTracks();
369
+
370
+ // Get audio tracks
371
+ const audioTracks = stream.getAudioTracks();
372
+
373
+ // Get video tracks
374
+ const videoTracks = stream.getVideoTracks();
375
+
376
+ // Add track to stream
377
+ stream.addTrack(track);
378
+
379
+ // Remove track from stream
380
+ stream.removeTrack(track);
381
+ ```
382
+
383
+ ### MediaStreamTrack
384
+
385
+ Media track operations
386
+
387
+ ```typescript
388
+ // Get track ID
389
+ const trackId = track.id;
390
+
391
+ // Get track kind ('audio' or 'video')
392
+ const trackKind = track.kind;
393
+
394
+ // Get track state ('live' or 'ended')
395
+ const trackState = track.readyState;
396
+
397
+ // Enable/disable track
398
+ track.enabled = true;
399
+ track.enabled = false;
400
+
401
+ // Stop track
402
+ track.stop();
403
+ ```
404
+
405
+ ### RTCPeerConnection
406
+
407
+ Peer-to-peer connection
408
+
409
+ ```typescript
410
+ // Create connection
411
+ const pc = new RTCPeerConnection(config);
412
+
413
+ // Add transceiver with track
414
+ const transceiver = pc.addTransceiver(track, {
415
+ direction: 'sendrecv', // 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive'
416
+ streams: [stream]
417
+ });
418
+
419
+ // Add transceiver with media kind
420
+ const videoTransceiver = pc.addTransceiver('video', {
421
+ direction: 'recvonly'
422
+ });
423
+
424
+ // Get all transceivers
425
+ const transceivers = pc.getTransceivers();
426
+
427
+ // Create offer
428
+ const offer = await pc.createOffer();
429
+ await pc.setLocalDescription(offer);
430
+
431
+ // Create answer
432
+ const answer = await pc.createAnswer();
433
+ await pc.setLocalDescription(answer);
434
+
435
+ // Set remote description
436
+ await pc.setRemoteDescription(answer);
437
+
438
+ // Add ICE candidate
439
+ await pc.addIceCandidate(candidate);
440
+
441
+ // Get connection state
442
+ const connectionState = pc.connectionState; // 'new' | 'connecting' | 'connected' | 'disconnected' | 'failed' | 'closed'
443
+ const iceGatheringState = pc.iceGatheringState; // 'new' | 'gathering' | 'complete'
444
+
445
+ // Get local/remote description
446
+ const localDesc = pc.localDescription;
447
+ const remoteDesc = pc.remoteDescription;
448
+
449
+ // Event listeners
450
+ pc.onicecandidate = (event) => { };
451
+ pc.ontrack = (event) => { };
452
+ pc.onconnectionstatechange = (event) => { };
453
+ pc.onicegatheringstatechange = (event) => { };
454
+
455
+ // Close connection
456
+ pc.close();
457
+ ```
458
+
459
+ ### MediaRecorder
460
+
461
+ Media recording
462
+
463
+ ```typescript
464
+ import { MediaRecorder } from 'react-native-webrtc-nitro';
465
+ import RNFS from 'react-native-fs';
466
+
467
+ // Create recorder
468
+ const recorder = new MediaRecorder(stream);
469
+
470
+ // Take photo
471
+ const photoPath = `${RNFS.DocumentDirectoryPath}/photo.jpg`;
472
+ await recorder.takePhoto(photoPath);
473
+
474
+ // Start recording video
475
+ const videoPath = `${RNFS.DocumentDirectoryPath}/video.mp4`;
476
+ recorder.startRecording(videoPath);
477
+
478
+ // Stop recording
479
+ recorder.stopRecording();
480
+ ```
481
+
482
+ ## 🏗️ Architecture
483
+
484
+ ```
485
+ react-native-webrtc-nitro
486
+ ├── src/ # TypeScript API
487
+ │ ├── specs/ # Nitro spec definitions
488
+ │ └── views/ # View components
489
+ ├── cpp/ # C++ core implementation
490
+ │ ├── FFmpeg/ # FFmpeg wrapper
491
+ │ ├── Hybrid/ # Hybrid object implementations
492
+ │ └── FramePipe/ # Frame processing pipeline
493
+ ├── ios/ # iOS platform code
494
+ ├── android/ # Android platform code
495
+ └── 3rdparty/ # Third-party libraries
496
+ ├── ffmpeg/
497
+ ├── libdatachannel/
498
+ └── opus/
499
+ ```
500
+
501
+ ## 🔧 Development
502
+
503
+ ### Setup Development Environment
504
+
505
+ ```bash
506
+ # Clone repository
507
+ git clone https://github.com/SingTown/react-native-webrtc-nitro.git
508
+ cd react-native-webrtc-nitro
509
+
510
+ # Build native libraries first
511
+ cd 3rdparty
512
+
513
+ # Download dependencies
514
+ ./download.sh
515
+
516
+ # Build for iOS
517
+ ./build_ios.sh
518
+
519
+ # Build for Android
520
+ ./build_android.sh
521
+
522
+ cd ..
523
+
524
+ # Install dependencies
525
+ pnpm install
526
+ ```
527
+
528
+ ### Run Example
529
+
530
+ ```bash
531
+ cd example
532
+
533
+ # iOS
534
+ pnpm ios
535
+
536
+ # Android
537
+ pnpm android
538
+ ```
539
+
540
+ ## 📝 Example Project
541
+
542
+ Check the `example` directory for a complete sample application, including:
543
+
544
+ - Audio/video calls
545
+ - Media recording
546
+ - Multi-party conferencing
547
+
548
+ ## 🤝 Contributing
549
+
550
+ Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
551
+
552
+ 1. Fork the repository
553
+ 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
554
+ 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
555
+ 4. Push to the branch (`git push origin feature/AmazingFeature`)
556
+ 5. Open a Pull Request
557
+
558
+ ## 📄 License
559
+
560
+ MIT License - see the [LICENSE](LICENSE) file for details
561
+
562
+ ## 🙏 Acknowledgments
563
+
564
+ - [Nitro Modules](https://github.com/mrousavy/nitro) - Powerful React Native native module framework
565
+ - [libdatachannel](https://github.com/paullouisageneau/libdatachannel) - WebRTC data channel implementation
566
+ - [FFmpeg](https://ffmpeg.org/) - Audio/video processing library
567
+
568
+ ## 📮 Contact
569
+
570
+ - GitHub: [@SingTown](https://github.com/SingTown)
571
+ - Issues: [Submit an issue](https://github.com/SingTown/react-native-webrtc-nitro/issues)
572
+
573
+ ## 🌟 Star History
24
574
 
25
- Bootstrapped with [create-nitro-module](https://github.com/patrickkabwe/create-nitro-module).
575
+ If this project helps you, please give us a ⭐️!
26
576
 
27
- ## Contributing
577
+ ---
28
578
 
29
- Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
579
+ Built with ❤️ using [Nitro Modules](https://github.com/mrousavy/nitro)
@@ -154,7 +154,6 @@ class HybridCamera : HybridCameraSpec() {
154
154
  override fun open(pipeId: String): Promise<Unit> {
155
155
  this.pipeId = pipeId
156
156
  return Promise.async {
157
- requestPermission(android.Manifest.permission.CAMERA)
158
157
  Camera.addPipeId(pipeId)
159
158
  }
160
159
  }
@@ -35,7 +35,6 @@ class HybridMicrophone : HybridMicrophoneSpec() {
35
35
  override fun open(pipeId: String): Promise<Unit> {
36
36
  this.pipeId = pipeId
37
37
  return Promise.async {
38
- requestPermission(android.Manifest.permission.RECORD_AUDIO)
39
38
 
40
39
  audioRecord = AudioRecord(
41
40
  MediaRecorder.AudioSource.VOICE_COMMUNICATION,
@@ -0,0 +1,95 @@
1
+ package com.webrtc
2
+
3
+ import android.content.pm.PackageManager
4
+ import androidx.core.content.ContextCompat
5
+ import androidx.annotation.Keep
6
+ import com.facebook.react.modules.core.PermissionAwareActivity
7
+ import com.facebook.react.modules.core.PermissionListener
8
+ import com.facebook.proguard.annotations.DoNotStrip
9
+ import kotlin.random.Random
10
+ import kotlinx.coroutines.CompletableDeferred
11
+ import kotlinx.coroutines.Dispatchers
12
+ import kotlinx.coroutines.withContext
13
+ import com.margelo.nitro.NitroModules
14
+ import com.margelo.nitro.core.Promise
15
+ import com.margelo.nitro.webrtc.HybridPermissionsSpec
16
+ import com.margelo.nitro.webrtc.PermissionDescriptor
17
+ import com.margelo.nitro.webrtc.PermissionState
18
+ import com.margelo.nitro.webrtc.PermissionName
19
+
20
+ @Keep
21
+ @DoNotStrip
22
+ class HybridPermissions : HybridPermissionsSpec() {
23
+
24
+ override fun query(permissionDesc: PermissionDescriptor): Promise<PermissionState> {
25
+ return Promise.async {
26
+ val permission = when (permissionDesc.name) {
27
+ PermissionName.MICROPHONE -> android.Manifest.permission.RECORD_AUDIO
28
+ PermissionName.CAMERA -> android.Manifest.permission.CAMERA
29
+ }
30
+
31
+ val context = NitroModules.applicationContext
32
+ ?: throw RuntimeException("ReactApplicationContext is not available")
33
+
34
+ val currentActivity = context.currentActivity
35
+ ?: throw RuntimeException("No current Activity")
36
+ val status = ContextCompat.checkSelfPermission(context, permission)
37
+
38
+ if (status == PackageManager.PERMISSION_GRANTED) {
39
+ PermissionState.GRANTED
40
+ } else if (status == PackageManager.PERMISSION_DENIED && !currentActivity.shouldShowRequestPermissionRationale(
41
+ permission
42
+ )
43
+ ) {
44
+ PermissionState.PROMPT
45
+ } else {
46
+ PermissionState.DENIED
47
+ }
48
+ }
49
+ }
50
+
51
+ override fun request(permissionDesc: PermissionDescriptor): Promise<PermissionState> {
52
+ return Promise.async {
53
+ val permission = when (permissionDesc.name) {
54
+ PermissionName.MICROPHONE -> android.Manifest.permission.RECORD_AUDIO
55
+ PermissionName.CAMERA -> android.Manifest.permission.CAMERA
56
+ }
57
+
58
+ val context = NitroModules.applicationContext
59
+ ?: throw RuntimeException("ReactApplicationContext is not available")
60
+
61
+ val currentActivity = context.currentActivity
62
+ ?: throw RuntimeException("No current Activity")
63
+
64
+ if (currentActivity !is PermissionAwareActivity) {
65
+ throw RuntimeException("Current activity doesn't support permissions")
66
+ }
67
+
68
+ val code = Random.nextInt(0, 1000)
69
+ val deferred = CompletableDeferred<PermissionState>()
70
+
71
+ val listener = object : PermissionListener {
72
+ override fun onRequestPermissionsResult(
73
+ requestCode: Int,
74
+ permissions: Array<String>,
75
+ grantResults: IntArray
76
+ ): Boolean {
77
+ if (requestCode != code) return false
78
+
79
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
80
+ deferred.complete(PermissionState.GRANTED)
81
+ } else {
82
+ deferred.complete(PermissionState.DENIED)
83
+ }
84
+ return true
85
+ }
86
+ }
87
+
88
+ withContext(Dispatchers.Main) {
89
+ currentActivity.requestPermissions(arrayOf(permission), code, listener)
90
+ }
91
+
92
+ deferred.await()
93
+ }
94
+ }
95
+ }