use std::sync::Arc;
use std::time::Duration;
use arcly_stream::prelude::*;
use arcly_stream::{
ClusterRelay, FrameFlags, FrameType, NodeAddr, PlaybackRegistry, PublishRegistry, StreamError,
StreamHandle,
};
use bytes::Bytes;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
const HDR: usize = 37;
fn encode_frame(f: &MediaFrame) -> Vec<u8> {
let mut b = Vec::with_capacity(HDR + f.data.len());
b.extend_from_slice(&f.pts.to_le_bytes());
b.extend_from_slice(&f.dts.to_le_bytes());
match f.duration {
Some(d) => {
b.push(1);
b.extend_from_slice(&d.to_le_bytes());
}
None => {
b.push(0);
b.extend_from_slice(&0u64.to_le_bytes());
}
}
b.push(f.codec as u8);
b.push(match f.frame_type {
FrameType::Key => 0,
FrameType::Delta => 1,
FrameType::Audio => 2,
});
b.extend_from_slice(&f.flags.bits().to_le_bytes());
b.extend_from_slice(&f.track_id.to_le_bytes());
b.extend_from_slice(&(f.data.len() as u32).to_le_bytes());
b.extend_from_slice(&f.data);
b
}
async fn read_frame(r: &mut TcpStream) -> std::io::Result<Option<MediaFrame>> {
let mut h = [0u8; HDR];
if let Err(e) = r.read_exact(&mut h).await {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
return Ok(None);
}
return Err(e);
}
let le8 = |i: usize| i64::from_le_bytes(h[i..i + 8].try_into().unwrap());
let pts = le8(0);
let dts = le8(8);
let duration = (h[16] == 1).then(|| u64::from_le_bytes(h[17..25].try_into().unwrap()));
let codec = CodecId::try_from(h[25]).unwrap_or(CodecId::Unknown);
let frame_type = match h[26] {
0 => FrameType::Key,
1 => FrameType::Delta,
_ => FrameType::Audio,
};
let flags = FrameFlags::from_bits_truncate(u16::from_le_bytes(h[27..29].try_into().unwrap()));
let track_id = u32::from_le_bytes(h[29..33].try_into().unwrap());
let len = u32::from_le_bytes(h[33..37].try_into().unwrap()) as usize;
let mut data = vec![0u8; len];
r.read_exact(&mut data).await?;
Ok(Some(MediaFrame {
pts,
dts,
duration,
data: Bytes::from(data),
codec,
frame_type,
flags,
track_id,
}))
}
async fn write_handshake(s: &mut TcpStream, key: &StreamKey) -> std::io::Result<()> {
let (app, stream) = (
key.app.as_str().as_bytes(),
key.stream_id.as_str().as_bytes(),
);
s.write_all(&[app.len() as u8]).await?;
s.write_all(app).await?;
s.write_all(&[stream.len() as u8]).await?;
s.write_all(stream).await
}
async fn read_handshake(s: &mut TcpStream) -> std::io::Result<StreamKey> {
let mut one = [0u8; 1];
s.read_exact(&mut one).await?;
let mut app = vec![0u8; one[0] as usize];
s.read_exact(&mut app).await?;
s.read_exact(&mut one).await?;
let mut stream = vec![0u8; one[0] as usize];
s.read_exact(&mut stream).await?;
Ok(StreamKey::new(
String::from_utf8_lossy(&app).into_owned(),
String::from_utf8_lossy(&stream).into_owned(),
))
}
async fn serve_relay(listener: TcpListener, origin: Arc<Engine>) {
loop {
let Ok((mut sock, _)) = listener.accept().await else {
break;
};
let origin = origin.clone();
tokio::spawn(async move {
let Ok(key) = read_handshake(&mut sock).await else {
return;
};
let Ok(handle) = origin.get_stream(&key) else {
return; };
for fr in handle.replay_buffer() {
if sock.write_all(&encode_frame(&fr)).await.is_err() {
return;
}
}
let mut sub = handle.subscribe_resilient();
while let Some(fr) = sub.recv().await {
if sock.write_all(&encode_frame(&fr)).await.is_err() {
break; }
}
});
}
}
struct TcpRelay {
local: Arc<Engine>,
origin_addr: String,
}
#[async_trait::async_trait]
impl ClusterRelay for TcpRelay {
async fn locate(&self, _key: &StreamKey) -> arcly_stream::Result<Option<NodeAddr>> {
Ok(Some(NodeAddr(self.origin_addr.clone())))
}
async fn pull(&self, key: &StreamKey, origin: &NodeAddr) -> arcly_stream::Result<()> {
let mut sock = TcpStream::connect(&origin.0)
.await
.map_err(|e| StreamError::cluster(format!("connect {}: {e}", origin.0)))?;
write_handshake(&mut sock, key)
.await
.map_err(|e| StreamError::cluster(format!("handshake: {e}")))?;
let dst = self.local.start_publish(key).await?;
let local = self.local.clone();
let key = key.clone();
tokio::spawn(async move {
while let Ok(Some(frame)) = read_frame(&mut sock).await {
if dst.publish_frame(frame).is_err() {
break;
}
}
let _ = local.end_publish(&key).await;
});
Ok(())
}
async fn announce(&self, _key: &StreamKey) -> arcly_stream::Result<()> {
Ok(()) }
async fn withdraw(&self, _key: &StreamKey) -> arcly_stream::Result<()> {
Ok(())
}
}
fn publish_synthetic(handle: StreamHandle) {
tokio::spawn(async move {
let mut cfg =
MediaFrame::new_video(0, 0, Bytes::from_static(b"\x00cfg"), CodecId::H264, true);
cfg.flags |= FrameFlags::CONFIG;
let _ = handle.publish_frame(cfg);
let mut tick = tokio::time::interval(Duration::from_millis(40));
let mut pts = 0i64;
for n in 0..u64::MAX {
tick.tick().await;
let key = n % 25 == 0;
let data = Bytes::from(vec![(n & 0xFF) as u8; 256]);
let mut f = MediaFrame::new_video(pts, pts, data, CodecId::H264, key);
f.duration = Some(40);
if handle.publish_frame(f).is_err() {
break;
}
pts += 40;
}
});
}
#[tokio::main]
async fn main() -> arcly_stream::Result<()> {
let key = StreamKey::new("live", "cam");
let origin = Engine::builder()
.application(AppSpec::new("live").gop_cache(30))
.build();
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let origin_addr = listener.local_addr().expect("addr").to_string();
tokio::spawn(serve_relay(listener, origin.clone()));
let pub_handle = origin.start_publish(&key).await?;
publish_synthetic(pub_handle);
println!("[origin] serving relay at {origin_addr}, publishing {key}");
tokio::time::sleep(Duration::from_millis(300)).await;
let edge = Engine::builder()
.application(AppSpec::new("live").gop_cache(30))
.build();
let relay = TcpRelay {
local: edge.clone(),
origin_addr,
};
let node = relay.locate(&key).await?.expect("origin located");
relay.pull(&key, &node).await?;
println!("[edge] pulling {key} from {} over TCP", node.0);
let edge_handle = loop {
if let Ok(h) = edge.get_stream(&key) {
break h;
}
tokio::time::sleep(Duration::from_millis(20)).await;
};
let mut viewer = edge_handle.subscribe_resilient();
let (mut frames, mut keyframes) = (0u32, 0u32);
for _ in 0..60 {
match tokio::time::timeout(Duration::from_secs(2), viewer.recv()).await {
Ok(Some(fr)) => {
frames += 1;
if fr.is_keyframe() {
keyframes += 1;
}
}
_ => break,
}
}
println!("[edge] viewer received {frames} mirrored frames ({keyframes} keyframes) over TCP");
assert!(
frames > 0 && keyframes > 0,
"edge should mirror live frames"
);
println!("\nOK: network ClusterRelay mirrored {key} origin → edge across a TCP socket.");
println!(" Point `origin_addr` at another host to federate across machines.");
Ok(())
}