Skip to content

Commit 8f294dd

Browse files
authored
fix: windows crashes & builds (#55)
- Doing jobs in parallels in GHA (Divide build time by 3). - Now use custom webrtc builds for this repo - Windows crashes when receiving video is now fixed (thanks @cloudwebrtc) - Now reading defines from webrtc.ninja - Added a small create_pc test to be sure libwebrtc is working/linked correctly - Fix MacOS builds - Fixed Linux duplicated main build error (due to nasm)
1 parent 4f8229c commit 8f294dd

File tree

10 files changed

+418
-199
lines changed

10 files changed

+418
-199
lines changed

.github/workflows/webrtc-builds.yml

+46-12
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
11
name: WebRTC builds
22
on: workflow_dispatch
3-
env:
4-
CARGO_TERM_COLOR: always
53

64
jobs:
75
build:
86
strategy:
7+
fail-fast: false
98
matrix:
9+
os:
10+
- windows-latest
11+
- ubuntu-latest
12+
- macos-latest
13+
arch:
14+
- x64
15+
- arm64
16+
profile:
17+
- release
18+
- debug
1019
include:
1120
- os: windows-latest
12-
cmd: .\build_windows.cmd
13-
artifacts: windows
21+
cmd: .\build_windows.cmd
22+
name: win
1423
- os: ubuntu-latest
1524
cmd: ./build_linux.sh
16-
artifacts: linux
25+
name: linux
1726
- os: macos-latest
1827
cmd: ./build_macos.sh
19-
artifacts: macos
28+
name: macos
2029

21-
name: Build webrtc (${{ matrix.os }})
30+
name: Build webrtc (${{ matrix.name }}-${{ matrix.arch }}-${{ matrix.profile }})
2231
runs-on: ${{ matrix.os }}
2332
steps:
33+
- name: Setup vars
34+
id: setup
35+
run: |
36+
echo "OUT=${{ matrix.name }}-${{ matrix.arch }}-${{ matrix.profile }}" >> "$GITHUB_OUTPUT"
37+
echo "ZIP=webrtc-${{ matrix.name }}-${{ matrix.arch }}-${{ matrix.profile }}.zip" >> "$GITHUB_OUTPUT"
38+
shell: bash
39+
40+
# Print some debug infos to be sure everything is ok before doing really long tasks..
41+
- name: Info
42+
run: |
43+
echo "OutName: ${{ steps.setup.outputs.OUT }}"
44+
echo "OutZip: ${{ steps.setup.outputs.ZIP }}"
45+
2446
- uses: actions/checkout@v3
2547
with:
2648
submodules: true
@@ -33,22 +55,34 @@ jobs:
3355
if: ${{ matrix.os == 'macos-latest' }}
3456
run: brew install ninja
3557

58+
# It doesn't seem to be used?
3659
- name: Install windows dependencies
3760
if: ${{ matrix.os == 'windows-latest' }}
3861
run: |
3962
Invoke-WebRequest -Uri "https://github.com/ninja-build/ninja/releases/latest/download/ninja-win.zip" -OutFile ninja.zip
4063
Expand-Archive -Path ninja.zip -DestinationPath ninja
41-
echo "${{github.workspace}}\ninja" >> $GITHUB_PATH
64+
echo "${{ github.workspace }}\ninja" >> $GITHUB_PATH
4265
4366
- name: Print ninja version
4467
run: ninja --version
4568

46-
- name: Build
47-
run: ${{ matrix.cmd }}
69+
- name: Build WebRTC
70+
run: ${{ matrix.cmd }} --arch ${{ matrix.arch }} --profile ${{ matrix.profile }}
4871
working-directory: webrtc-sys/libwebrtc
4972

73+
- name: Zip artifact (Unix)
74+
if: ${{ matrix.os != 'windows-latest' }}
75+
run: |
76+
cd webrtc-sys/libwebrtc/${{ steps.setup.outputs.OUT }}
77+
zip ${{ github.workspace }}/${{ steps.setup.outputs.ZIP }} ./* -r
78+
79+
- name: Zip artifact (Windows)
80+
if: ${{ matrix.os == 'windows-latest' }}
81+
run: Compress-Archive -Path .\webrtc-sys\libwebrtc\${{ steps.setup.outputs.OUT }}\* -DestinationPath ${{ steps.setup.outputs.ZIP }}
82+
83+
# doublezip here but I don't think there is an alternative
5084
- name: Upload artifacts
5185
uses: actions/upload-artifact@v3
5286
with:
53-
name: builds-${{ matrix.artifacts }}
54-
path: webrtc-sys/libwebrtc/${{ matrix.artifacts }}
87+
name: ${{ steps.setup.outputs.ZIP }}
88+
path: ${{ steps.setup.outputs.ZIP }}

livekit-webrtc/src/native/peer_connection.rs

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ impl From<sys_pc::ffi::SignalingState> for SignalingState {
110110

111111
#[derive(Clone)]
112112
pub struct PeerConnection {
113+
#[allow(dead_code)]
113114
native_observer: SharedPtr<sys_pc::ffi::NativePeerConnectionObserver>,
114115
observer: Arc<PeerObserver>,
115116

livekit-webrtc/src/native/rtp_parameters.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::rtp_parameters::*;
2-
use crate::MediaType;
32
use webrtc_sys::rtp_parameters as sys_rp;
43
use webrtc_sys::webrtc as sys_webrtc;
54

livekit-webrtc/src/native/rtp_transceiver.rs

-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::rtp_receiver;
55
use crate::rtp_sender;
66
use crate::rtp_transceiver::RtpTransceiverDirection;
77
use crate::rtp_transceiver::RtpTransceiverInit;
8-
use crate::MediaType;
98
use crate::RtcError;
109
use cxx::SharedPtr;
1110
use webrtc_sys::rtc_error as sys_err;
@@ -33,7 +32,6 @@ impl From<RtpTransceiverDirection> for sys_webrtc::ffi::RtpTransceiverDirection
3332
RtpTransceiverDirection::RecvOnly => Self::RecvOnly,
3433
RtpTransceiverDirection::Inactive => Self::Inactive,
3534
RtpTransceiverDirection::Stopped => Self::Stopped,
36-
_ => panic!("unknown RtpTransceiverDirection"),
3735
}
3836
}
3937
}

livekit-webrtc/src/peer_connection.rs

+81
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,84 @@ impl Debug for PeerConnection {
238238
.finish()
239239
}
240240
}
241+
242+
#[cfg(test)]
243+
mod tests {
244+
use crate::peer_connection::*;
245+
use crate::peer_connection_factory::*;
246+
use log::trace;
247+
use tokio::sync::mpsc;
248+
249+
fn init_log() {
250+
let _ = env_logger::builder().is_test(true).try_init();
251+
}
252+
253+
#[tokio::test]
254+
async fn create_pc() {
255+
init_log();
256+
257+
let factory = PeerConnectionFactory::default();
258+
let config = RtcConfiguration {
259+
ice_servers: vec![IceServer {
260+
urls: vec!["stun:stun1.l.google.com:19302".to_string()],
261+
username: "".into(),
262+
password: "".into(),
263+
}],
264+
continual_gathering_policy: ContinualGatheringPolicy::GatherOnce,
265+
ice_transport_type: IceTransportsType::All,
266+
};
267+
268+
let bob = factory.create_peer_connection(config.clone()).unwrap();
269+
let alice = factory.create_peer_connection(config.clone()).unwrap();
270+
271+
let (bob_ice_tx, mut bob_ice_rx) = mpsc::channel::<IceCandidate>(16);
272+
let (alice_ice_tx, mut alice_ice_rx) = mpsc::channel::<IceCandidate>(16);
273+
let (alice_dc_tx, mut alice_dc_rx) = mpsc::channel::<DataChannel>(16);
274+
275+
bob.on_ice_candidate(Some(Box::new(move |candidate| {
276+
bob_ice_tx.blocking_send(candidate).unwrap();
277+
})));
278+
279+
alice.on_ice_candidate(Some(Box::new(move |candidate| {
280+
alice_ice_tx.blocking_send(candidate).unwrap();
281+
})));
282+
283+
alice.on_data_channel(Some(Box::new(move |dc| {
284+
alice_dc_tx.blocking_send(dc).unwrap();
285+
})));
286+
287+
let bob_dc = bob
288+
.create_data_channel("test_dc", DataChannelInit::default())
289+
.unwrap();
290+
291+
let offer = bob.create_offer(OfferOptions::default()).await.unwrap();
292+
trace!("Bob offer: {:?}", offer);
293+
bob.set_local_description(offer.clone()).await.unwrap();
294+
alice.set_remote_description(offer).await.unwrap();
295+
296+
let answer = alice.create_answer(AnswerOptions::default()).await.unwrap();
297+
trace!("Alice answer: {:?}", answer);
298+
alice.set_local_description(answer.clone()).await.unwrap();
299+
bob.set_remote_description(answer).await.unwrap();
300+
301+
let bob_ice = bob_ice_rx.recv().await.unwrap();
302+
let alice_ice = alice_ice_rx.recv().await.unwrap();
303+
304+
bob.add_ice_candidate(alice_ice).await.unwrap();
305+
alice.add_ice_candidate(bob_ice).await.unwrap();
306+
307+
let (data_tx, mut data_rx) = mpsc::channel::<String>(1);
308+
let alice_dc = alice_dc_rx.recv().await.unwrap();
309+
alice_dc.on_message(Some(Box::new(move |buffer| {
310+
data_tx
311+
.blocking_send(String::from_utf8_lossy(buffer.data).to_string())
312+
.unwrap();
313+
})));
314+
315+
bob_dc.send(b"This is a test", true).unwrap();
316+
assert_eq!(data_rx.recv().await.unwrap(), "This is a test");
317+
318+
alice.close();
319+
bob.close();
320+
}
321+
}

webrtc-sys/build.rs

+55-48
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
use regex::Regex;
22
use std::env;
33
use std::fs;
4+
use std::io::BufRead;
45
use std::io::{self, Write};
56
use std::path;
67
use std::process::Command;
78

89
const WEBRTC_TAG: &str = "webrtc-beb0471";
10+
const IGNORE_DEFINES: [&str; 2] = ["CR_CLANG_REVISION", "CR_XCODE_VERSION"];
911

1012
fn download_prebuilt_webrtc(
1113
out_path: path::PathBuf,
1214
) -> Result<path::PathBuf, Box<dyn std::error::Error>> {
1315
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
1416
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
17+
18+
// This is not yet supported on all platforms.
19+
// On Windows, we need Rust to link against libcmtd.
1520
let use_debug = {
1621
let var = env::var("LK_DEBUG_WEBRTC");
1722
var.is_ok() && var.unwrap() == "true"
@@ -95,19 +100,26 @@ fn main() {
95100
let var = env::var("LK_CUSTOM_WEBRTC");
96101
var.is_ok() && var.unwrap() == "true"
97102
};
103+
println!("cargo:rerun-if-env-changed=LK_CUSTOM_WEBRTC");
98104

99-
let (webrtc_include, webrtc_lib) = if use_custom_webrtc {
105+
let (webrtc_dir, webrtc_include, webrtc_lib) = if use_custom_webrtc {
100106
// Use a local WebRTC version (libwebrtc folder)
101107
let webrtc_dir = path::PathBuf::from("./libwebrtc");
102-
(webrtc_dir.join("src"), webrtc_dir.join("src/out/Dev/obj"))
108+
(
109+
webrtc_dir.clone(),
110+
webrtc_dir.join("include"),
111+
webrtc_dir.join("lib"),
112+
)
103113
} else {
104114
// Download a prebuilt version of WebRTC
105115
let download_dir = env::var("OUT_DIR").unwrap() + "/webrtc-sdk";
106116
let webrtc_dir = download_prebuilt_webrtc(path::PathBuf::from(download_dir)).unwrap();
107-
108-
(webrtc_dir.join("include"), webrtc_dir.join("lib"))
117+
(
118+
webrtc_dir.clone(),
119+
webrtc_dir.join("include"),
120+
webrtc_dir.join("lib"),
121+
)
109122
};
110-
println!("cargo:rerun-if-env-changed=LK_CUSTOM_WEBRTC");
111123

112124
// Just required for the bridge build to succeed.
113125
let includes = &[
@@ -140,23 +152,25 @@ fn main() {
140152
"src/helper.rs",
141153
]);
142154

143-
builder.file("src/peer_connection.cpp");
144-
builder.file("src/peer_connection_factory.cpp");
145-
builder.file("src/media_stream.cpp");
146-
builder.file("src/data_channel.cpp");
147-
builder.file("src/jsep.cpp");
148-
builder.file("src/candidate.cpp");
149-
builder.file("src/rtp_receiver.cpp");
150-
builder.file("src/rtp_sender.cpp");
151-
builder.file("src/rtp_transceiver.cpp");
152-
builder.file("src/rtp_parameters.cpp");
153-
builder.file("src/rtc_error.cpp");
154-
builder.file("src/webrtc.cpp");
155-
builder.file("src/video_frame.cpp");
156-
builder.file("src/video_frame_buffer.cpp");
157-
builder.file("src/video_encoder_factory.cpp");
158-
builder.file("src/video_decoder_factory.cpp");
159-
builder.file("src/audio_device.cpp");
155+
builder.files(&[
156+
"src/peer_connection.cpp",
157+
"src/peer_connection_factory.cpp",
158+
"src/media_stream.cpp",
159+
"src/data_channel.cpp",
160+
"src/jsep.cpp",
161+
"src/candidate.cpp",
162+
"src/rtp_receiver.cpp",
163+
"src/rtp_sender.cpp",
164+
"src/rtp_transceiver.cpp",
165+
"src/rtp_parameters.cpp",
166+
"src/rtc_error.cpp",
167+
"src/webrtc.cpp",
168+
"src/video_frame.cpp",
169+
"src/video_frame_buffer.cpp",
170+
"src/video_encoder_factory.cpp",
171+
"src/video_decoder_factory.cpp",
172+
"src/audio_device.cpp",
173+
]);
160174

161175
for include in includes {
162176
builder.include(include);
@@ -167,6 +181,20 @@ fn main() {
167181
webrtc_lib.canonicalize().unwrap().to_str().unwrap()
168182
);
169183

184+
// Read preprocessor definitions from webrtc.ninja
185+
let webrtc_gni = fs::File::open(webrtc_dir.join("webrtc.ninja")).unwrap();
186+
let mut reader = io::BufReader::new(webrtc_gni).lines();
187+
let defines_line = reader.next().unwrap().unwrap(); // The first line contains the defines
188+
let defines_re = Regex::new(r"-D(\w+)(?:=([^\s]+))?").unwrap();
189+
for cap in defines_re.captures_iter(&defines_line) {
190+
let define_name = &cap[1];
191+
let define_value = cap.get(2).map(|m| m.as_str());
192+
if IGNORE_DEFINES.contains(&define_name) {
193+
continue;
194+
}
195+
builder.define(define_name, define_value);
196+
}
197+
170198
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
171199
match target_os.as_str() {
172200
"windows" => {
@@ -186,12 +214,7 @@ fn main() {
186214
println!("cargo:rustc-link-lib=dylib=dwmapi");
187215
println!("cargo:rustc-link-lib=static=webrtc");
188216

189-
builder
190-
.flag("/std:c++17")
191-
.flag("/EHsc")
192-
.define("WEBRTC_WIN", None)
193-
//.define("WEBRTC_ENABLE_SYMBOL_EXPORT", None) Not necessary when using WebRTC as a static library
194-
.define("NOMINMAX", None);
217+
builder.flag("/std:c++17").flag("/EHsc");
195218
}
196219
"linux" => {
197220
println!("cargo:rustc-link-lib=dylib=Xext");
@@ -203,10 +226,7 @@ fn main() {
203226
println!("cargo:rustc-link-lib=dylib=m");
204227
println!("cargo:rustc-link-lib=static=webrtc");
205228

206-
builder
207-
.flag("-std=c++17")
208-
.define("WEBRTC_POSIX", None)
209-
.define("WEBRTC_LINUX", None);
229+
builder.flag("-std=c++17");
210230
}
211231
"macos" => {
212232
println!("cargo:rustc-link-lib=framework=Foundation");
@@ -254,19 +274,10 @@ fn main() {
254274
builder
255275
.flag("-stdlib=libc++")
256276
.flag("-std=c++17")
257-
.flag(format!("-isysroot{}", sysroot).as_str())
258-
.define("WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT", None)
259-
.define("WEBRTC_POSIX", None)
260-
.define("WEBRTC_MAC", None);
277+
.flag(format!("-isysroot{}", sysroot).as_str());
261278
}
262279
"ios" => {
263-
builder
264-
.flag("-std=c++17")
265-
.file("src/objc_test.mm")
266-
.define("WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT", None)
267-
.define("WEBRTC_MAC", None)
268-
.define("WEBRTC_POSIX", None)
269-
.define("WEBRTC_IOS", None);
280+
builder.flag("-std=c++17");
270281
}
271282
"android" => {
272283
let ndk_env = env::var("ANDROID_NDK_HOME").expect(
@@ -330,11 +341,7 @@ fn main() {
330341
vs_path.to_str().unwrap()
331342
);
332343

333-
builder
334-
.flag("-std=c++17")
335-
.define("WEBRTC_LINUX", None)
336-
.define("WEBRTC_POSIX", None)
337-
.define("WEBRTC_ANDROID", None);
344+
builder.flag("-std=c++17");
338345
}
339346
_ => {
340347
panic!("Unsupported target, {}", target_os);

0 commit comments

Comments
 (0)