use std::num::NonZeroUsize;
use std::time::Duration;
use broadcast_common::Timestamp;
use ll_hls_runtime::client::{Action, LlHlsClient};
use ll_hls_runtime::server::{
BlockingQuery, DEFAULT_TRACK_ID, LlHlsBody, LlHlsOrigin, LlHlsRequest,
};
use media_plane::egress::{AwaitPolicy, EgressResponse, ServedEgress};
use media_plane::trunk::{PartEntry, SegmentEntry, Trunk, TrunkConfig};
use transmux::SegmentMeta;
const PLAYLIST_URL: &str = "http://origin/live/media.m3u8";
fn nz(n: usize) -> NonZeroUsize {
NonZeroUsize::new(n).expect("example capacity must be non-zero")
}
fn canned_playlist() -> String {
let trunk = Trunk::new(TrunkConfig::new(nz(16), nz(4), nz(8), nz(4), nz(16)));
let writer = trunk
.segment_writer()
.expect("first (and only) segment writer");
let origin = LlHlsOrigin::new(std::sync::Arc::clone(&trunk), 1.0, 500, nz(4));
origin.set_init(vec![0xAA; 32]);
writer.publish_part(PartEntry::new(
vec![0x01; 16],
1,
0,
Duration::from_millis(500),
true,
));
writer.publish_segment(SegmentEntry::new(
vec![0x02; 32],
1,
Duration::from_secs(1),
Timestamp::from_nanos(0),
SegmentMeta {
discontinuous: false,
},
));
writer.publish_part(PartEntry::new(
vec![0x03; 16],
2,
0,
Duration::from_millis(500),
true,
));
match origin.resolve(
LlHlsRequest::Playlist {
track_id: DEFAULT_TRACK_ID,
query: BlockingQuery::default(),
},
Timestamp::from_nanos(0),
AwaitPolicy::new(Timestamp::from_nanos(0)),
) {
EgressResponse::Ready {
body: LlHlsBody::Playlist(m),
..
} => m,
other => panic!("expected Ready(Playlist), got {other:?}"),
}
}
fn main() {
let playlist = canned_playlist();
println!("--- canned media.m3u8 ---\n{playlist}");
let mut client = LlHlsClient::new(PLAYLIST_URL);
match client.poll() {
Some(Action::FetchPlaylist {
url,
blocking,
skip,
}) => {
assert_eq!(url, PLAYLIST_URL);
assert!(blocking.is_none());
assert!(!skip);
println!("action: FetchPlaylist {{ url: {url:?}, blocking: None }}");
}
other => panic!("expected the seeded FetchPlaylist, got {other:?}"),
}
client
.on_playlist(playlist.as_bytes())
.expect("the canned playlist parses");
let mut saw_blocking_reload = false;
while let Some(action) = client.poll() {
match &action {
Action::FetchResource { id, url, .. } => {
println!("action: FetchResource {{ id: {id:?}, url: {url:?} }}");
}
Action::FetchPlaylist {
url,
blocking: Some(b),
..
} => {
println!(
"action: FetchPlaylist {{ url: {url:?}, blocking: {b:?} }} <- blocking reload"
);
saw_blocking_reload = true;
}
Action::FetchPlaylist {
url,
blocking: None,
..
} => {
println!("action: FetchPlaylist {{ url: {url:?}, blocking: None }}");
}
Action::WaitMs(ms) => println!("action: WaitMs({ms})"),
_ => {}
}
}
assert!(
saw_blocking_reload,
"this crate's own origin renderer defaults CAN-BLOCK-RELOAD=YES, so the \
next reload the client schedules must be a blocking one"
);
}