151 lines
4.0 KiB
Rust
151 lines
4.0 KiB
Rust
mod message;
|
|
|
|
use std::collections::hash_map as hmap;
|
|
use std::time;
|
|
|
|
use quiche;
|
|
use quiche::h3::webtransport;
|
|
|
|
use crate::{media, transport};
|
|
|
|
#[derive(Default)]
|
|
pub struct Session {
|
|
media: Option<media::Source>,
|
|
streams: transport::Streams, // An easy way of buffering stream data.
|
|
tracks: hmap::HashMap<u32, u64>, // map from track_id to current stream_id
|
|
}
|
|
|
|
impl transport::App for Session {
|
|
// Process any updates to a session.
|
|
fn poll(&mut self, conn: &mut quiche::Connection, session: &mut webtransport::ServerSession) -> anyhow::Result<()> {
|
|
loop {
|
|
let event = match session.poll(conn) {
|
|
Err(webtransport::Error::Done) => break,
|
|
Err(e) => return Err(e.into()),
|
|
Ok(e) => e,
|
|
};
|
|
|
|
log::debug!("webtransport event {:?}", event);
|
|
|
|
match event {
|
|
webtransport::ServerEvent::ConnectRequest(_req) => {
|
|
// you can handle request with
|
|
// req.authority()
|
|
// req.path()
|
|
// and you can validate this request with req.origin()
|
|
session.accept_connect_request(conn, None)?;
|
|
|
|
// TODO
|
|
let media = media::Source::new("../media/fragmented.mp4")?;
|
|
let init = &media.init;
|
|
|
|
// Create a JSON header.
|
|
let mut message = message::Message::new();
|
|
message.init = Some(message::Init {});
|
|
let data = message.serialize()?;
|
|
|
|
// Create a new stream and write the header.
|
|
let stream_id = session.open_stream(conn, false)?;
|
|
self.streams.send(conn, stream_id, data.as_slice(), false)?;
|
|
self.streams.send(conn, stream_id, init.as_slice(), true)?;
|
|
|
|
self.media = Some(media);
|
|
}
|
|
webtransport::ServerEvent::StreamData(stream_id) => {
|
|
let mut buf = vec![0; 10000];
|
|
while let Ok(len) = session.recv_stream_data(conn, stream_id, &mut buf) {
|
|
let _stream_data = &buf[0..len];
|
|
}
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
// Send any pending stream data.
|
|
// NOTE: This doesn't return an error because it's async, and would be confusing.
|
|
self.streams.poll(conn);
|
|
|
|
// Fetch the next media fragment, possibly queuing up stream data.
|
|
self.poll_source(conn, session)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn timeout(&self) -> Option<time::Duration> {
|
|
self.media.as_ref().and_then(|m| m.timeout())
|
|
}
|
|
}
|
|
|
|
impl Session {
|
|
fn poll_source(
|
|
&mut self,
|
|
conn: &mut quiche::Connection,
|
|
session: &mut webtransport::ServerSession,
|
|
) -> anyhow::Result<()> {
|
|
// Get the media source once the connection is established.
|
|
let media = match &mut self.media {
|
|
Some(m) => m,
|
|
None => return Ok(()),
|
|
};
|
|
|
|
// Get the next media fragment.
|
|
let fragment = match media.fragment()? {
|
|
Some(f) => f,
|
|
None => return Ok(()),
|
|
};
|
|
|
|
let stream_id = match self.tracks.get(&fragment.track_id) {
|
|
// Close the old stream.
|
|
Some(stream_id) if fragment.keyframe => {
|
|
self.streams.send(conn, *stream_id, &[], true)?;
|
|
None
|
|
}
|
|
|
|
// Use the existing stream
|
|
Some(stream_id) => Some(*stream_id),
|
|
|
|
// No existing stream.
|
|
_ => None,
|
|
};
|
|
|
|
let stream_id = match stream_id {
|
|
// Use the existing stream,
|
|
Some(stream_id) => stream_id,
|
|
|
|
// Open a new stream.
|
|
None => {
|
|
// Create a new unidirectional stream.
|
|
let stream_id = session.open_stream(conn, false)?;
|
|
|
|
// Set the stream priority to be equal to the timestamp.
|
|
// We subtract from u64::MAX so newer media is sent important.
|
|
// TODO prioritize audio
|
|
let order = u64::MAX - fragment.timestamp;
|
|
self.streams.send_order(conn, stream_id, order);
|
|
|
|
// Encode a JSON header indicating this is a new track.
|
|
let mut message: message::Message = message::Message::new();
|
|
message.segment = Some(message::Segment {
|
|
track_id: fragment.track_id,
|
|
});
|
|
|
|
// Write the header.
|
|
let data = message.serialize()?;
|
|
self.streams.send(conn, stream_id, &data, false)?;
|
|
|
|
// Keep a mapping from the track id to the current stream id.
|
|
self.tracks.insert(fragment.track_id, stream_id);
|
|
|
|
stream_id
|
|
}
|
|
};
|
|
|
|
// Write the current fragment.
|
|
let data = fragment.data.as_slice();
|
|
self.streams.send(conn, stream_id, data, false)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|