Skip to content

Commit f7445e9

Browse files
committed
add web support
1 parent 4a008e4 commit f7445e9

File tree

28 files changed

+1648
-46
lines changed

28 files changed

+1648
-46
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ jobs:
3131
dep: "brew update && brew install cocoapods xcodegen"
3232
- cmd: ".\\scripts\\setup_windows.bat \"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.43.34808\\bin\\Hostx64\\x64\\lib.exe\" && cd example\\winApp && msbuild winApp.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64"
3333
os: windows-latest
34+
- cmd: "./gradlew :example:webApp:jsBrowserDistribution"
35+
os: macos-latest
3436
runs-on: ${{ matrix.os }}
3537
permissions:
3638
pull-requests: write

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ KMP wrapper for WebRTC.
1212
| `iOS` | 🚀 |
1313
| `macOS` | 🚀 |
1414
| `Windows X64` | 🚀 |
15-
| `JS` (Chrome) | 🔮 |
16-
| `WasmJS` (Chrome) | 🔮 |
15+
| `JS` (Chrome) | 🚀 |
1716
| `Linux X64` | 🔮 |
1817

1918
## Dependency
@@ -90,6 +89,12 @@ Open the project (the repo root dir) in Android studio, and run the example.andr
9089
# open example\winApp\winApp.sln in Visual Studio 2022, and run it.
9190
```
9291

92+
### JS
93+
94+
```bash
95+
./gradlew :example:webApp:jsBrowserRun
96+
```
97+
9398
## Build WebRTC
9499

95100
[hack_webrtc_43659 branch of HackWebRTC/webrtc](https://github.com/HackWebRTC/webrtc/tree/hack_webrtc_43659), which is based on m133.

example/androidApp/src/main/java/com/piasy/kmp/webrtc/android/CallActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ public void onIceConnected(@NotNull String peerUid) {
328328
runOnUiThread(new Runnable() {
329329
@Override
330330
public void run() {
331+
// 10. on ice connected, add renderer for remote stream
331332
pcClient.addRemoteTrackRenderer(remoteRenderer);
332333
}
333334
});

example/iosApp/iosApp/CallViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ - (void)startLoopback {
371371
_pcClient = [_pcClientFactory createPeerConnectionClientPeerUid:@"test"
372372
dir:0
373373
hasVideo:YES
374-
videoMaxBitrate:[[_settingsModel currentMaxBitrateSettingFromStore] intValue]
374+
videoMaxBitrateBps:[[_settingsModel currentMaxBitrateSettingFromStore] intValue]
375375
videoMaxFrameRate:30
376376
callback:self];
377377

example/macApp/macApp/APPRTCViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ - (void)startLoopback {
302302
_pcClient = [_pcClientFactory createPeerConnectionClientPeerUid:@"test"
303303
dir:0
304304
hasVideo:YES
305-
videoMaxBitrate:2000
305+
videoMaxBitrateBps:2000
306306
videoMaxFrameRate:30
307307
callback:self];
308308

example/webApp/build.gradle.kts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
plugins {
2+
alias(libs.plugins.kmp)
3+
}
4+
5+
kotlin {
6+
js(IR) {
7+
browser {
8+
}
9+
binaries.executable()
10+
}
11+
12+
applyDefaultHierarchyTemplate()
13+
sourceSets {
14+
commonMain {
15+
dependencies {
16+
implementation(project(":kmp-webrtc"))
17+
}
18+
}
19+
}
20+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.piasy.kmp.webrtc.web
2+
3+
import com.piasy.kmp.webrtc.*
4+
import com.piasy.kmp.webrtc.data.IceCandidate
5+
import com.piasy.kmp.webrtc.data.RtcStatsReport
6+
import com.piasy.kmp.webrtc.data.SessionDescription
7+
import kotlinx.browser.document
8+
import kotlinx.browser.window
9+
import org.w3c.dom.HTMLButtonElement
10+
import org.w3c.dom.events.Event
11+
12+
var isLoopback = false
13+
var loopback: HTMLButtonElement? = null
14+
15+
var pcClientFactory: PeerConnectionClientFactory? = null
16+
// on web, we can't disable encryption, so we need two pc clients.
17+
var localPcClient: PeerConnectionClient? = null
18+
var remotePcClient: PeerConnectionClient? = null
19+
20+
fun main() {
21+
window.onload = {
22+
loopback = document.getElementById("loopbackButton") as HTMLButtonElement
23+
loopback?.addEventListener("click", ::handleLoopbackClick)
24+
}
25+
}
26+
27+
fun handleLoopbackClick(event: Event) {
28+
isLoopback = !isLoopback
29+
loopback?.innerHTML = if (isLoopback) "Stop" else "Loopback"
30+
if (isLoopback) {
31+
startLoopback()
32+
} else {
33+
stopLoopback()
34+
}
35+
}
36+
37+
fun startLoopback() {
38+
// 1. initialize
39+
initializeWebRTC(null, "", true)
40+
41+
// 2. create PcClientFactory
42+
val privateConfig = JsPrivateConfig(js("createBlackStream")())
43+
val config = PeerConnectionClientFactory.Config(
44+
PeerConnectionClientFactory.VIDEO_CAPTURE_IMPL_SYSTEM_CAMERA, 1280, 720, 30,
45+
PeerConnectionClientFactory.CAMERA_FACE_FRONT, privateConfig,
46+
)
47+
pcClientFactory = createPeerConnectionClientFactory(config) { code, msg ->
48+
println("PeerConnectionClientFactory error: $code, $msg")
49+
}
50+
51+
// 3. create local tracks
52+
pcClientFactory?.createLocalTracks()
53+
54+
// 4. add local preview & start camera capture
55+
pcClientFactory?.addLocalTrackRenderer(document.getElementById("localVideo")!!)
56+
pcClientFactory?.startVideoCapture()
57+
58+
// 5. create PcClient
59+
localPcClient = pcClientFactory?.createPeerConnectionClient(
60+
"test_local", PeerConnectionClientFactory.DIR_SEND_ONLY, true,
61+
800_000, 30, localPcClientCallback
62+
)
63+
remotePcClient = pcClientFactory?.createPeerConnectionClient(
64+
"test_remote", PeerConnectionClientFactory.DIR_RECV_ONLY, true,
65+
800_000, 30, remotePcClientCallback
66+
)
67+
68+
// 6. create pc
69+
localPcClient?.createPeerConnection(emptyList())
70+
remotePcClient?.createPeerConnection(emptyList())
71+
72+
// 7. create offer
73+
localPcClient?.createOffer()
74+
}
75+
76+
val localPcClientCallback = object : PeerConnectionClientCallback {
77+
override fun onLocalDescription(peerUid: String, sdp: SessionDescription) {
78+
// 8.1. send offer to remote
79+
remotePcClient?.setRemoteDescription(sdp)
80+
remotePcClient?.createAnswer()
81+
}
82+
83+
override fun onIceCandidate(peerUid: String, candidate: IceCandidate) {
84+
// 9. send ice candidate to remote, get ice candidate from remote, add ice candidate
85+
remotePcClient?.addIceCandidate(candidate)
86+
}
87+
88+
override fun onIceConnected(peerUid: String) {
89+
}
90+
91+
override fun onPreferCodecs(peerUid: String, sdp: String): String {
92+
return sdp
93+
}
94+
95+
override fun onIceCandidatesRemoved(peerUid: String, candidates: List<IceCandidate>) {
96+
}
97+
98+
override fun onPeerConnectionStatsReady(peerUid: String, report: RtcStatsReport) {
99+
}
100+
101+
override fun onIceDisconnected(peerUid: String) {
102+
}
103+
104+
override fun onError(peerUid: String, code: Int) {
105+
}
106+
}
107+
108+
val remotePcClientCallback = object : PeerConnectionClientCallback {
109+
override fun onLocalDescription(peerUid: String, sdp: SessionDescription) {
110+
// 8.2. get answer from remote, and set answer
111+
localPcClient?.setRemoteDescription(sdp);
112+
}
113+
114+
override fun onIceCandidate(peerUid: String, candidate: IceCandidate) {
115+
// 9. send ice candidate to remote, get ice candidate from remote, add ice candidate
116+
localPcClient?.addIceCandidate(candidate);
117+
}
118+
119+
override fun onIceConnected(peerUid: String) {
120+
// 10. on ice connected, add renderer for remote stream
121+
remotePcClient?.addRemoteTrackRenderer(document.getElementById("remoteVideo")!!)
122+
}
123+
124+
override fun onPreferCodecs(peerUid: String, sdp: String): String {
125+
return sdp
126+
}
127+
128+
override fun onIceCandidatesRemoved(peerUid: String, candidates: List<IceCandidate>) {
129+
}
130+
131+
override fun onPeerConnectionStatsReady(peerUid: String, report: RtcStatsReport) {
132+
}
133+
134+
override fun onIceDisconnected(peerUid: String) {
135+
}
136+
137+
override fun onError(peerUid: String, code: Int) {
138+
}
139+
}
140+
141+
fun stopLoopback() {
142+
pcClientFactory?.stopVideoCapture()
143+
localPcClient?.close()
144+
remotePcClient?.close()
145+
pcClientFactory?.destroyPeerConnectionFactory()
146+
pcClientFactory = null
147+
localPcClient = null
148+
remotePcClient = null
149+
}

0 commit comments

Comments
 (0)