phase 1
This commit is contained in:
11
media/client/Cargo.toml
Normal file
11
media/client/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "cht-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cht-common = { path = "../common" }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
102
media/client/src/main.rs
Normal file
102
media/client/src/main.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use anyhow::Result;
|
||||
use cht_common::protocol::{
|
||||
self, AudioParams, ControlMessage, PacketHeader, PacketType, VideoParams, WirePacket,
|
||||
FLAG_KEYFRAME,
|
||||
};
|
||||
use tokio::io::{AsyncWriteExt, BufWriter};
|
||||
use tokio::net::TcpStream;
|
||||
use tracing::info;
|
||||
|
||||
const DEFAULT_SERVER: &str = "mcrndeb:4444";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
cht_common::logging::init("client");
|
||||
|
||||
let server_addr = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| DEFAULT_SERVER.to_string());
|
||||
|
||||
info!("Connecting to {server_addr}...");
|
||||
let stream = TcpStream::connect(&server_addr).await?;
|
||||
info!("Connected");
|
||||
|
||||
let mut writer = BufWriter::new(stream);
|
||||
|
||||
// Send session_start
|
||||
let session_start = ControlMessage::SessionStart {
|
||||
id: chrono_session_id(),
|
||||
video: VideoParams {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
codec: "h264".into(),
|
||||
fps: 30,
|
||||
},
|
||||
audio: AudioParams {
|
||||
sample_rate: 48000,
|
||||
channels: 2,
|
||||
codec: "aac".into(),
|
||||
},
|
||||
};
|
||||
protocol::write_packet(&mut writer, &session_start.to_wire_packet()?).await?;
|
||||
info!("Sent session_start");
|
||||
|
||||
// Send test packets (placeholder — will be replaced by real capture)
|
||||
let frame_interval_ns = 33_333_333u64; // ~30fps
|
||||
for i in 0u64..300 {
|
||||
let ts = i * frame_interval_ns;
|
||||
let keyframe = i % 30 == 0;
|
||||
|
||||
// Fake video packet
|
||||
let video = WirePacket {
|
||||
header: PacketHeader {
|
||||
packet_type: PacketType::Video,
|
||||
flags: if keyframe { FLAG_KEYFRAME } else { 0 },
|
||||
length: 1024,
|
||||
timestamp_ns: ts,
|
||||
},
|
||||
payload: vec![0u8; 1024],
|
||||
};
|
||||
protocol::write_packet(&mut writer, &video).await?;
|
||||
|
||||
// Fake audio packet every 3 video frames
|
||||
if i % 3 == 0 {
|
||||
let audio = WirePacket {
|
||||
header: PacketHeader {
|
||||
packet_type: PacketType::Audio,
|
||||
flags: 0,
|
||||
length: 512,
|
||||
timestamp_ns: ts,
|
||||
},
|
||||
payload: vec![0u8; 512],
|
||||
};
|
||||
protocol::write_packet(&mut writer, &audio).await?;
|
||||
}
|
||||
|
||||
// Keepalive every 150 frames (~5s)
|
||||
if i % 150 == 0 && i > 0 {
|
||||
let keepalive = ControlMessage::Keepalive;
|
||||
protocol::write_packet(&mut writer, &keepalive.to_wire_packet()?).await?;
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_nanos(frame_interval_ns)).await;
|
||||
}
|
||||
|
||||
// Send session_stop and flush
|
||||
let stop = ControlMessage::SessionStop;
|
||||
protocol::write_packet(&mut writer, &stop.to_wire_packet()?).await?;
|
||||
writer.flush().await?;
|
||||
writer.shutdown().await?;
|
||||
info!("Sent session_stop, done");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chrono_session_id() -> String {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let secs = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
format!("{secs}")
|
||||
}
|
||||
Reference in New Issue
Block a user