[TSM.ID].[11031972] PXE : 19 Cangkang -> REAL Implementation (for/if/match/tests)

This commit is contained in:
TSM.ID
2026-05-25 05:05:13 +07:00
parent e0360b3ecd
commit 9e5f7c78a9
19 changed files with 2749 additions and 958 deletions
+84 -2
View File
@@ -1,3 +1,85 @@
#![deny(warnings)]
// [TSM.ID].[11031972] -- All Rights Reserved. Proprietary & Confidential.
pub mod rtmp_server;
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-ingest -- Media Ingestion Server (RTMP/HLS/DASH)
pub mod server;
use std::collections::HashMap;
#[derive(Debug)]
pub enum IngestError { StreamNotFound(String), TranscodeFailed(String), BufferFull(String) }
impl std::fmt::Display for IngestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { Self::StreamNotFound(e) => write!(f, "Stream: {e}"), Self::TranscodeFailed(e) => write!(f, "Transcode: {e}"), Self::BufferFull(e) => write!(f, "Buffer: {e}") }
}
}
impl std::error::Error for IngestError {}
#[derive(Debug, Clone)]
pub struct StreamConfig { pub stream_id: String, pub codec: String, pub bitrate_kbps: u32, pub width: u32, pub height: u32, pub fps: u32 }
#[derive(Debug, Clone)]
pub struct MediaChunk { pub sequence: u64, pub data: Vec<u8>, pub duration_ms: u32, pub keyframe: bool, pub timestamp: u64 }
pub struct IngestPipeline {
streams: HashMap<String, StreamState>,
max_buffer_chunks: usize,
}
struct StreamState { config: StreamConfig, buffer: Vec<MediaChunk>, total_bytes: u64, chunk_count: u64 }
impl IngestPipeline {
pub fn new(max_buffer: usize) -> Self { Self { streams: HashMap::new(), max_buffer_chunks: max_buffer } }
pub fn create_stream(&mut self, config: StreamConfig) -> Result<(), IngestError> {
let id = config.stream_id.clone();
self.streams.insert(id, StreamState { config, buffer: Vec::new(), total_bytes: 0, chunk_count: 0 });
Ok(())
}
pub fn push_chunk(&mut self, stream_id: &str, chunk: MediaChunk) -> Result<u64, IngestError> {
let state = self.streams.get_mut(stream_id).ok_or_else(|| IngestError::StreamNotFound(stream_id.into()))?;
if state.buffer.len() >= self.max_buffer_chunks {
state.buffer.remove(0); // Drop oldest (sliding window)
}
state.total_bytes += chunk.data.len() as u64;
state.chunk_count += 1;
let seq = state.chunk_count;
state.buffer.push(chunk);
Ok(seq)
}
/// Generate HLS playlist from buffer
pub fn generate_hls_playlist(&self, stream_id: &str) -> Result<String, IngestError> {
let state = self.streams.get(stream_id).ok_or_else(|| IngestError::StreamNotFound(stream_id.into()))?;
let mut m3u8 = String::from("#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:4\n");
for chunk in &state.buffer {
m3u8.push_str(&format!("#EXTINF:{:.3},\n", chunk.duration_ms as f64 / 1000.0));
m3u8.push_str(&format!("segment_{}.ts\n", chunk.sequence));
}
Ok(m3u8)
}
/// Get stream stats
pub fn stream_stats(&self, stream_id: &str) -> Result<(u64, u64, f64), IngestError> {
let state = self.streams.get(stream_id).ok_or_else(|| IngestError::StreamNotFound(stream_id.into()))?;
let bitrate = if state.chunk_count > 0 { (state.total_bytes * 8) as f64 / (state.chunk_count as f64 * 4.0) / 1000.0 } else { 0.0 };
Ok((state.chunk_count, state.total_bytes, bitrate))
}
pub fn active_streams(&self) -> usize { self.streams.len() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ingest() {
let mut p = IngestPipeline::new(10);
p.create_stream(StreamConfig { stream_id: "s1".into(), codec: "h264".into(), bitrate_kbps: 3000, width: 1920, height: 1080, fps: 30 }).unwrap();
for i in 0..5 {
p.push_chunk("s1", MediaChunk { sequence: i, data: vec![0; 1000], duration_ms: 4000, keyframe: i == 0, timestamp: i * 4000 }).unwrap();
}
let playlist = p.generate_hls_playlist("s1").unwrap();
assert!(playlist.contains("#EXTM3U"));
assert!(playlist.contains("segment_"));
}
}