diff --git a/Cargo.lock b/Cargo.lock index 88aba38..a501cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,131 +92,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", -] - -[[package]] -name = "async-signal" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.5.1" @@ -282,19 +157,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -657,25 +519,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -750,12 +593,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hmac" version = "0.10.1" @@ -846,12 +683,6 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "lock_api" version = "0.4.14" @@ -867,22 +698,6 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" -[[package]] -name = "macro_rules_attribute" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" - [[package]] name = "memchr" version = "2.8.1" @@ -895,6 +710,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "nom" version = "7.1.3" @@ -975,6 +801,16 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + [[package]] name = "parking_lot_core" version = "0.9.12" @@ -988,12 +824,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1009,17 +839,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -1030,20 +849,6 @@ dependencies = [ "spki", ] -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1229,14 +1034,11 @@ dependencies = [ "async-broadcast", "bytes", "dashmap", - "futures-lite", - "macro_rules_attribute", "rand 0.10.1", "rml_rtmp", - "smol", - "smol-macros", "str0m", "tiny_http", + "tokio", ] [[package]] @@ -1254,19 +1056,6 @@ dependencies = [ "nom 7.1.3", ] -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "rustls-pki-types" version = "1.14.1" @@ -1406,33 +1195,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "smol" -version = "2.0.2" +name = "socket2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", -] - -[[package]] -name = "smol-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcaedb62e0475a6898988138995ec7b1e5d116167a72bb12c7b59d0649fbbc2" -dependencies = [ - "async-executor", - "async-io", - "async-lock", - "event-listener", - "futures-lite", + "libc", + "windows-sys", ] [[package]] @@ -1603,6 +1372,34 @@ dependencies = [ "log", ] +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing" version = "0.1.44" diff --git a/Cargo.toml b/Cargo.toml index 2797068..b7e8940 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,8 @@ edition = "2024" async-broadcast = "0.7.2" bytes = "1.11.1" dashmap = "6.2.1" -macro_rules_attribute = "0.2.2" rand = "0.10.1" rml_rtmp = "0.8.0" -futures-lite = "2" -smol = "2.0.2" -smol-macros = "0.1.1" str0m = "0.20.0" tiny_http = "0.12.0" +tokio = { version = "1", features = ["full"] } diff --git a/src/http.rs b/src/http.rs index e05e770..8989deb 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,6 +1,6 @@ use std::error::Error; -use smol::channel::{Receiver, Sender}; +use tokio::sync::mpsc::{Receiver, Sender}; use tiny_http::{Header, Response, Server, StatusCode}; pub struct HttpServer { @@ -9,7 +9,8 @@ pub struct HttpServer { } impl HttpServer { - pub fn start(self) -> Result<(), Box> { + pub fn start(mut self) -> Result<(), Box> { + let handle = tokio::runtime::Handle::current(); std::thread::spawn(move || { let server = Server::http("0.0.0.0:5000").unwrap(); for mut request in server.incoming_requests() { @@ -36,18 +37,18 @@ impl HttpServer { let mut body = String::new(); request.as_reader().read_to_string(&mut body).unwrap(); println!("HTTP: received offer, body length={}", body.len()); - smol::block_on(self.offer_tx.send(("".to_string(), body))).unwrap(); + handle.block_on(self.offer_tx.send(("".to_string(), body))).unwrap(); let content_type = Header::from_bytes(&b"Content-Type"[..], &b"application/sdp"[..]).unwrap(); - let reply = match smol::block_on(self.accept_rx.recv()) { - Ok(accept) => Response::from_data(accept.1) + let reply = match handle.block_on(self.accept_rx.recv()) { + Some(accept) => Response::from_data(accept.1) .with_status_code(StatusCode(201)) .with_header(content_type) .with_header(cors) .with_header(cors_methods) .with_header(cors_headers), - Err(_) => Response::from_data("") + None => Response::from_data("") .with_status_code(StatusCode(500)) .with_header(content_type), }; diff --git a/src/main.rs b/src/main.rs index c4c12e6..fac1bce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,16 @@ use std::{error::Error, sync::Arc}; -use async_broadcast::{Receiver, Sender, broadcast}; +use async_broadcast::broadcast; use dashmap::DashMap; -use macro_rules_attribute::apply; use rml_rtmp::{ handshake::{Handshake, HandshakeProcessResult, PeerType}, sessions::{ServerSession, ServerSessionConfig, ServerSessionEvent, ServerSessionResult}, }; -use smol::{ +use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, - lock::Mutex, net::TcpListener, - stream::StreamExt, + sync::Mutex, }; -use smol_macros::main; use crate::{ http::HttpServer, @@ -34,15 +31,14 @@ struct StreamSession { frame_channel: async_broadcast::Sender>, } -#[apply(main!)] +#[tokio::main] async fn main() -> Result<(), Box> { let listener = TcpListener::bind("0.0.0.0:8123").await?; - let mut incoming = listener.incoming(); - let mut appstate = Arc::new(Mutex::new(AppState { + let appstate = Arc::new(Mutex::new(AppState { stream_sessions: Arc::new(DashMap::new()), })); - let (offer_tx, offer_rx) = smol::channel::bounded::<(String, String)>(32); - let (answer_tx, answer_rx) = smol::channel::bounded::<(String, String)>(32); + let (offer_tx, offer_rx) = tokio::sync::mpsc::channel::<(String, String)>(4); + let (answer_tx, answer_rx) = tokio::sync::mpsc::channel::<(String, String)>(4); let http = HttpServer { offer_tx, @@ -63,11 +59,11 @@ async fn main() -> Result<(), Box> { drop(app); - while let Some(connection) = incoming.next().await { - let stream = connection?; + loop { + let (stream, _) = listener.accept().await?; let appstate = appstate.clone(); - smol::spawn(async move { + tokio::spawn(async move { let mut stream = stream; let mut server = Handshake::new(PeerType::Server); let appstate = appstate; @@ -120,7 +116,6 @@ async fn main() -> Result<(), Box> { } ServerSessionEvent::PublishStreamRequested { request_id, - app_name, stream_key, .. } => { @@ -172,10 +167,6 @@ async fn main() -> Result<(), Box> { } } } - }) - .detach(); + }); } - - Ok(()) } - diff --git a/src/webrtc.rs b/src/webrtc.rs index fe99261..0b921cf 100644 --- a/src/webrtc.rs +++ b/src/webrtc.rs @@ -1,7 +1,7 @@ use dashmap::DashMap; -use smol::{ - channel::{Receiver, Sender}, +use tokio::{ net::UdpSocket, + sync::mpsc::{Receiver, Sender}, }; use std::{ error::Error, @@ -10,7 +10,7 @@ use std::{ }; use str0m::{ - Candidate, Event, IceConnectionState, Input, Output, Rtc, + Candidate, Event, Input, Output, Rtc, change::SdpOffer, media::{MediaKind, MediaTime, Mid}, net::{Protocol, Receive}, @@ -25,9 +25,9 @@ pub struct Webrtc { } impl Webrtc { - pub fn start(self) -> Result<(), Box> { - smol::spawn(async move { - while let Ok(offer) = self.offer_rx.recv().await { + pub fn start(mut self) -> Result<(), Box> { + tokio::spawn(async move { + while let Some(offer) = self.offer_rx.recv().await { let (stream_key, sdp_body) = offer; let socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); let local_addr = socket.local_addr().unwrap(); @@ -69,13 +69,11 @@ impl Webrtc { .unwrap(); let sessions_ref = self.sessions_ref.clone(); - smol::spawn(async move { + tokio::spawn(async move { Webrtc::detach_connection(socket, rtc, sessions_ref, mid).await; - }) - .detach(); + }); } - }) - .detach(); + }); Ok(()) } @@ -139,13 +137,10 @@ impl Webrtc { } } if let Some(ref mut stream) = video_stream { - // Drain at most 8 frames per loop tick so the UDP socket - // (ICE keepalives, RTCP) is not starved by a backlog. for _ in 0..8 { match stream.try_recv() { Ok(frame) => { let now = Instant::now(); - // Explicit 90 kHz clock for H.264 RTP timestamps. let rtp_time = MediaTime::from_90khz(frame.timestamp_ms as u64 * 90); if let (Some(pt), Some(writer)) = (video_pt, video_mid.and_then(|m| rtc.writer(m))) @@ -159,48 +154,34 @@ impl Webrtc { } Err(async_broadcast::TryRecvError::Empty) => break, Err(async_broadcast::TryRecvError::Closed) => return, - // Overflow means some frames were dropped; the next - // try_recv will give the oldest surviving frame, so - // continue draining rather than breaking. Err(async_broadcast::TryRecvError::Overflowed(_)) => continue, } } } } - // Cap wait to 20 ms so frame delivery stays timely even when - // str0m's deadline is far out. let wait_until = deadline.min(Instant::now() + Duration::from_millis(20)).max(Instant::now()); + let sleep = tokio::time::sleep_until(wait_until.into()); - let input = smol::future::or( - async { - smol::Timer::at(wait_until).await; - None - }, - async { - let (n, from) = socket.recv_from(&mut recv_buf).await.ok()?; - Some((n, from)) - }, - ) - .await; - - match input { - None => { + tokio::select! { + _ = sleep => { rtc.handle_input(Input::Timeout(Instant::now())).ok(); } - Some((n, from)) => { - let data = recv_buf[..n].to_vec(); - if let Ok(contents) = data.as_slice().try_into() { - rtc.handle_input(Input::Receive( - Instant::now(), - Receive { - proto: Protocol::Udp, - source: from, - destination: local_addr, - contents, - }, - )) - .ok(); + result = socket.recv_from(&mut recv_buf) => { + if let Ok((n, from)) = result { + let data = recv_buf[..n].to_vec(); + if let Ok(contents) = data.as_slice().try_into() { + rtc.handle_input(Input::Receive( + Instant::now(), + Receive { + proto: Protocol::Udp, + source: from, + destination: local_addr, + contents, + }, + )) + .ok(); + } } } }