Files
pmomusic/pmoparadise/examples/stream_block.rs

351 lines
15 KiB
Rust
Raw Normal View History

//! Streams a Radio Paradise block via HTTP using pmoserver
//!
//! This example demonstrates streaming a single Radio Paradise block
//! using the StreamingFlacSink over HTTP via pmoserver. Perfect for
//! testing with VLC or other media players that support HTTP streaming.
//!
Replace HTTP timeout with idle mode + END_OF_BLOCKS_SIGNAL MAJOR ARCHITECTURAL IMPROVEMENT: Instead of using arbitrary timeouts that don't solve the real problem, implement proper idle mode and explicit end-of-stream signaling. Changes: 1. **Remove block_id timeout completely** - No more BLOCK_ID_TIMEOUT_SECS - Source enters idle mode when queue is empty - Waits indefinitely for new block_ids (poll every 100ms) - Only exits on cancellation or END_OF_BLOCKS_SIGNAL 2. **Introduce END_OF_BLOCKS_SIGNAL (EventId::MAX)** - Special block_id value to signal "no more blocks" - Source terminates cleanly after processing current block - Allows proper shutdown without cancellation - Exported from pmoparadise crate for public use 3. **Update HTTP timeout to 24 hours** - Effectively infinite timeout for block downloads - HTTP stream stays open as long as needed - Closed by pipeline termination, not arbitrary timeout 4. **Update stream_block example** - Push END_OF_BLOCKS_SIGNAL after the single block - Demonstrates clean termination after one block - Documents pattern for continuous vs. bounded streaming Benefits: - No arbitrary timeouts that might truncate valid streams - Clean separation: cancellation (external) vs. completion (internal) - Supports both continuous radio and bounded playlists - Proper idle mode for on-demand streaming applications Usage pattern: ```rust // Single block then stop source.push_block_id(block_id); source.push_block_id(END_OF_BLOCKS_SIGNAL); // Continuous streaming source.push_block_id(block1); source.push_block_id(block2); // ... keep pushing or wait in idle mode // Graceful shutdown source.push_block_id(END_OF_BLOCKS_SIGNAL); ```
2025-11-12 11:32:40 +00:00
//! The example streams ONE block then terminates cleanly using END_OF_BLOCKS_SIGNAL.
//! For continuous streaming, push multiple block_ids without the END signal.
//!
//! Architecture:
//! ```text
//! RadioParadiseStreamSource → TimerNode → StreamingFlacSink
//! ↓
//! StreamHandle
//! ↓
//! pmoserver (Axum)
//! ↓
//! VLC / Media Player Client
//! ```
//!
//! Usage:
//! cargo run --example stream_block --features full -- <channel_id>
//!
//! Example:
//! cargo run --example stream_block --features full -- 0 # Main Mix
//!
//! Then open in VLC:
//! vlc http://localhost:8080/test/stream (pure FLAC)
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
//! vlc http://localhost:8080/test/stream-ogg (OGG-FLAC streaming container)
//! vlc http://localhost:8080/test/stream-icy (FLAC + ICY metadata)
//!
//! To check current metadata:
//! curl http://localhost:8080/test/metadata
use axum::{
body::Body,
extract::State,
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
use pmoaudio::{AudioPipelineNode, TimerNode};
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
use pmoaudio_ext::{StreamingFlacSink, StreamingOggFlacSink};
use pmoflac::EncoderOptions;
Replace HTTP timeout with idle mode + END_OF_BLOCKS_SIGNAL MAJOR ARCHITECTURAL IMPROVEMENT: Instead of using arbitrary timeouts that don't solve the real problem, implement proper idle mode and explicit end-of-stream signaling. Changes: 1. **Remove block_id timeout completely** - No more BLOCK_ID_TIMEOUT_SECS - Source enters idle mode when queue is empty - Waits indefinitely for new block_ids (poll every 100ms) - Only exits on cancellation or END_OF_BLOCKS_SIGNAL 2. **Introduce END_OF_BLOCKS_SIGNAL (EventId::MAX)** - Special block_id value to signal "no more blocks" - Source terminates cleanly after processing current block - Allows proper shutdown without cancellation - Exported from pmoparadise crate for public use 3. **Update HTTP timeout to 24 hours** - Effectively infinite timeout for block downloads - HTTP stream stays open as long as needed - Closed by pipeline termination, not arbitrary timeout 4. **Update stream_block example** - Push END_OF_BLOCKS_SIGNAL after the single block - Demonstrates clean termination after one block - Documents pattern for continuous vs. bounded streaming Benefits: - No arbitrary timeouts that might truncate valid streams - Clean separation: cancellation (external) vs. completion (internal) - Supports both continuous radio and bounded playlists - Proper idle mode for on-demand streaming applications Usage pattern: ```rust // Single block then stop source.push_block_id(block_id); source.push_block_id(END_OF_BLOCKS_SIGNAL); // Continuous streaming source.push_block_id(block1); source.push_block_id(block2); // ... keep pushing or wait in idle mode // Graceful shutdown source.push_block_id(END_OF_BLOCKS_SIGNAL); ```
2025-11-12 11:32:40 +00:00
use pmoparadise::{RadioParadiseClient, RadioParadiseStreamSource, END_OF_BLOCKS_SIGNAL};
use pmoserver::{ServerBuilder, init_logging};
use std::env;
use std::sync::Arc;
use tokio_util::io::ReaderStream;
use tokio_util::sync::CancellationToken;
/// Shared application state
struct AppState {
stream_handle: pmoaudio_ext::StreamHandle,
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
ogg_handle: pmoaudio_ext::OggFlacStreamHandle,
}
/// Main HTTP handler for streaming (pure FLAC, no ICY metadata)
async fn stream_handler(
State(state): State<Arc<AppState>>,
_headers: HeaderMap,
) -> Result<Response, StatusCode> {
tracing::info!("New client connected (pure FLAC mode)");
// Pure FLAC stream without ICY metadata
let flac_stream = state.stream_handle.subscribe_flac();
Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "audio/flac")
.header("Cache-Control", "no-cache, no-store")
.body(Body::from_stream(ReaderStream::new(flac_stream)))
.unwrap())
}
/// ICY streaming handler (FLAC with embedded metadata)
async fn stream_icy_handler(
State(state): State<Arc<AppState>>,
_headers: HeaderMap,
) -> Result<Response, StatusCode> {
tracing::info!("New client connected (ICY mode)");
// FLAC stream with ICY metadata
let icy_stream = state.stream_handle.subscribe_icy();
Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "audio/flac")
.header("icy-metaint", "16000")
.header("icy-name", "Radio Paradise Stream Test")
.header("icy-genre", "Eclectic")
.header("icy-pub", "1")
.header("Cache-Control", "no-cache, no-store")
.body(Body::from_stream(ReaderStream::new(icy_stream)))
.unwrap())
}
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
/// OGG-FLAC streaming handler
async fn stream_ogg_handler(
State(state): State<Arc<AppState>>,
_headers: HeaderMap,
) -> Result<Response, StatusCode> {
tracing::info!("New client connected (OGG-FLAC mode)");
// OGG-FLAC stream
let ogg_stream = state.ogg_handle.subscribe();
Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "audio/ogg")
.header("Cache-Control", "no-cache, no-store")
.body(Body::from_stream(ReaderStream::new(ogg_stream)))
.unwrap())
}
/// Metadata endpoint (JSON)
async fn metadata_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let metadata = state.stream_handle.get_metadata().await;
axum::Json(metadata)
}
/// Health check endpoint
async fn health_handler() -> &'static str {
"OK"
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging via pmoserver
let _log_state = init_logging();
tracing::info!("=== Radio Paradise HTTP Streaming Test ===");
// Parse arguments
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <channel_id>", args[0]);
eprintln!();
eprintln!("Streams a Radio Paradise block via HTTP for testing.");
eprintln!();
eprintln!("Channel IDs:");
eprintln!(" 0 - Main Mix (eclectic, diverse mix)");
eprintln!(" 1 - Mellow Mix (smooth, chilled music)");
eprintln!(" 2 - Rock Mix (classic & modern rock)");
eprintln!(" 3 - World/Etc Mix (global sounds)");
eprintln!();
eprintln!("After starting, open in VLC:");
eprintln!(" vlc http://localhost:8080/test/stream (pure FLAC)");
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
eprintln!(" vlc http://localhost:8080/test/stream-ogg (OGG-FLAC container)");
eprintln!(" vlc http://localhost:8080/test/stream-icy (FLAC + ICY metadata)");
std::process::exit(1);
}
let channel_id: u8 = match args[1].parse() {
Ok(id) if id <= 3 => id,
_ => {
eprintln!("Error: channel_id must be a number between 0 and 3");
std::process::exit(1);
}
};
tracing::info!("Channel ID: {}", channel_id);
// ═══════════════════════════════════════════════════════════════════════════
// Fetch block metadata
// ═══════════════════════════════════════════════════════════════════════════
tracing::info!("Fetching current block metadata...");
let client = RadioParadiseClient::builder()
.channel(channel_id)
.build()
.await?;
let block = client.get_block(None).await?;
tracing::info!("Block Information:");
tracing::info!(" Event ID: {}", block.event);
tracing::info!(" Songs: {}", block.song_count());
tracing::info!(" Duration: {:.1} minutes", block.length as f64 / 60000.0);
tracing::info!("");
tracing::info!("Tracklist:");
for (index, song) in block.songs_ordered() {
tracing::info!(
" {:2}. {} - {} ({})",
index + 1,
song.artist,
song.title,
song.album.as_deref().unwrap_or("Unknown Album")
);
}
tracing::info!("");
// ═══════════════════════════════════════════════════════════════════════════
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Create streaming pipelines (FLAC and OGG-FLAC)
// ═══════════════════════════════════════════════════════════════════════════
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
tracing::info!("Creating streaming pipelines...");
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Encoder options (shared)
let encoder_options = EncoderOptions {
compression_level: 5,
verify: false,
..Default::default()
};
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// ─────────────────────────────────────────────────────────────────────────
// Pipeline 1: FLAC streaming
// ─────────────────────────────────────────────────────────────────────────
let mut source_flac = RadioParadiseStreamSource::new(client.clone());
source_flac.push_block_id(block.event);
Replace HTTP timeout with idle mode + END_OF_BLOCKS_SIGNAL MAJOR ARCHITECTURAL IMPROVEMENT: Instead of using arbitrary timeouts that don't solve the real problem, implement proper idle mode and explicit end-of-stream signaling. Changes: 1. **Remove block_id timeout completely** - No more BLOCK_ID_TIMEOUT_SECS - Source enters idle mode when queue is empty - Waits indefinitely for new block_ids (poll every 100ms) - Only exits on cancellation or END_OF_BLOCKS_SIGNAL 2. **Introduce END_OF_BLOCKS_SIGNAL (EventId::MAX)** - Special block_id value to signal "no more blocks" - Source terminates cleanly after processing current block - Allows proper shutdown without cancellation - Exported from pmoparadise crate for public use 3. **Update HTTP timeout to 24 hours** - Effectively infinite timeout for block downloads - HTTP stream stays open as long as needed - Closed by pipeline termination, not arbitrary timeout 4. **Update stream_block example** - Push END_OF_BLOCKS_SIGNAL after the single block - Demonstrates clean termination after one block - Documents pattern for continuous vs. bounded streaming Benefits: - No arbitrary timeouts that might truncate valid streams - Clean separation: cancellation (external) vs. completion (internal) - Supports both continuous radio and bounded playlists - Proper idle mode for on-demand streaming applications Usage pattern: ```rust // Single block then stop source.push_block_id(block_id); source.push_block_id(END_OF_BLOCKS_SIGNAL); // Continuous streaming source.push_block_id(block1); source.push_block_id(block2); // ... keep pushing or wait in idle mode // Graceful shutdown source.push_block_id(END_OF_BLOCKS_SIGNAL); ```
2025-11-12 11:32:40 +00:00
source_flac.push_block_id(END_OF_BLOCKS_SIGNAL); // Signal: no more blocks after this one
tracing::debug!("RadioParadiseStreamSource (FLAC) created with block {} + END signal", block.event);
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Use SMALL channel size to make backpressure more reactive
// Instead of trying to buffer 3s of audio (60 chunks), use a much smaller buffer
// This forces tighter backpressure control
Fix REAL bug: HTTP timeout was truncating Radio Paradise blocks ROOT CAUSE IDENTIFIED: The previous "wait for playback duration" workaround was masking the real issue. Radio Paradise blocks last ~20 minutes (1200s), but the HTTP timeout was only 180 seconds, causing premature stream termination. With backpressure from the audio pipeline, HTTP download proceeds at real-time pace. A 20-minute block takes ~20 minutes to download. The 180s timeout was killing the connection after 3 minutes, resulting in incomplete blocks. Changes: 1. **Increase HTTP block_timeout: 180s → 7200s (2 hours)** - Allows complete download of even the longest blocks - Comment explains why such a long timeout is needed 2. **Increase MPSC channel sizes: 16 → 60 chunks** - Matches TimerNode max_lead_time (3.0s / 0.05s = 60 chunks) - Prevents stop-and-go backpressure pattern - Allows smooth buffering as intended 3. **Replace workaround with proper channel drainage** - Use tx.closed().await instead of sleep() - Guarantees all buffered chunks are processed - More architecturally sound solution 4. **Add comprehensive diagnostic traces** - Log expected vs actual block duration - Detect premature EOF (< 95% of expected duration) - Track bytes decoded and HTTP Content-Length - Monitor backpressure blocking with timing This fixes the streaming completely. The block will now: - Download for the full ~20 minutes (real-time with backpressure) - Decode all audio data without truncation - Process all chunks before pipeline shutdown
2025-11-12 10:32:47 +00:00
let max_lead_time = 3.0;
let channel_size = 8; // Small buffer for reactive backpressure
tracing::debug!("Using channel size: {} chunks ({:.1}s buffer at 50ms/chunk)", channel_size, channel_size as f64 * 0.05);
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
Fix REAL bug: HTTP timeout was truncating Radio Paradise blocks ROOT CAUSE IDENTIFIED: The previous "wait for playback duration" workaround was masking the real issue. Radio Paradise blocks last ~20 minutes (1200s), but the HTTP timeout was only 180 seconds, causing premature stream termination. With backpressure from the audio pipeline, HTTP download proceeds at real-time pace. A 20-minute block takes ~20 minutes to download. The 180s timeout was killing the connection after 3 minutes, resulting in incomplete blocks. Changes: 1. **Increase HTTP block_timeout: 180s → 7200s (2 hours)** - Allows complete download of even the longest blocks - Comment explains why such a long timeout is needed 2. **Increase MPSC channel sizes: 16 → 60 chunks** - Matches TimerNode max_lead_time (3.0s / 0.05s = 60 chunks) - Prevents stop-and-go backpressure pattern - Allows smooth buffering as intended 3. **Replace workaround with proper channel drainage** - Use tx.closed().await instead of sleep() - Guarantees all buffered chunks are processed - More architecturally sound solution 4. **Add comprehensive diagnostic traces** - Log expected vs actual block duration - Detect premature EOF (< 95% of expected duration) - Track bytes decoded and HTTP Content-Length - Monitor backpressure blocking with timing This fixes the streaming completely. The block will now: - Download for the full ~20 minutes (real-time with backpressure) - Decode all audio data without truncation - Process all chunks before pipeline shutdown
2025-11-12 10:32:47 +00:00
let mut timer_flac = TimerNode::with_channel_size(max_lead_time, channel_size);
tracing::debug!("TimerNode (FLAC) created with {:.1}s max lead time, {} chunk buffer", max_lead_time, channel_size);
// StreamingFlacSink doesn't take channel_size - it uses bits_per_sample (16, 24, or 32)
let (streaming_sink, stream_handle) = StreamingFlacSink::new(encoder_options.clone(), 16);
tracing::debug!("StreamingFlacSink created");
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
timer_flac.register(Box::new(streaming_sink));
source_flac.register(Box::new(timer_flac));
tracing::info!("Pipeline 1 connected: RadioParadiseStreamSource → TimerNode → StreamingFlacSink");
// ─────────────────────────────────────────────────────────────────────────
// Pipeline 2: OGG-FLAC streaming
// ─────────────────────────────────────────────────────────────────────────
let mut source_ogg = RadioParadiseStreamSource::new(client);
source_ogg.push_block_id(block.event);
Replace HTTP timeout with idle mode + END_OF_BLOCKS_SIGNAL MAJOR ARCHITECTURAL IMPROVEMENT: Instead of using arbitrary timeouts that don't solve the real problem, implement proper idle mode and explicit end-of-stream signaling. Changes: 1. **Remove block_id timeout completely** - No more BLOCK_ID_TIMEOUT_SECS - Source enters idle mode when queue is empty - Waits indefinitely for new block_ids (poll every 100ms) - Only exits on cancellation or END_OF_BLOCKS_SIGNAL 2. **Introduce END_OF_BLOCKS_SIGNAL (EventId::MAX)** - Special block_id value to signal "no more blocks" - Source terminates cleanly after processing current block - Allows proper shutdown without cancellation - Exported from pmoparadise crate for public use 3. **Update HTTP timeout to 24 hours** - Effectively infinite timeout for block downloads - HTTP stream stays open as long as needed - Closed by pipeline termination, not arbitrary timeout 4. **Update stream_block example** - Push END_OF_BLOCKS_SIGNAL after the single block - Demonstrates clean termination after one block - Documents pattern for continuous vs. bounded streaming Benefits: - No arbitrary timeouts that might truncate valid streams - Clean separation: cancellation (external) vs. completion (internal) - Supports both continuous radio and bounded playlists - Proper idle mode for on-demand streaming applications Usage pattern: ```rust // Single block then stop source.push_block_id(block_id); source.push_block_id(END_OF_BLOCKS_SIGNAL); // Continuous streaming source.push_block_id(block1); source.push_block_id(block2); // ... keep pushing or wait in idle mode // Graceful shutdown source.push_block_id(END_OF_BLOCKS_SIGNAL); ```
2025-11-12 11:32:40 +00:00
source_ogg.push_block_id(END_OF_BLOCKS_SIGNAL); // Signal: no more blocks after this one
tracing::debug!("RadioParadiseStreamSource (OGG) created with block {} + END signal", block.event);
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
Fix REAL bug: HTTP timeout was truncating Radio Paradise blocks ROOT CAUSE IDENTIFIED: The previous "wait for playback duration" workaround was masking the real issue. Radio Paradise blocks last ~20 minutes (1200s), but the HTTP timeout was only 180 seconds, causing premature stream termination. With backpressure from the audio pipeline, HTTP download proceeds at real-time pace. A 20-minute block takes ~20 minutes to download. The 180s timeout was killing the connection after 3 minutes, resulting in incomplete blocks. Changes: 1. **Increase HTTP block_timeout: 180s → 7200s (2 hours)** - Allows complete download of even the longest blocks - Comment explains why such a long timeout is needed 2. **Increase MPSC channel sizes: 16 → 60 chunks** - Matches TimerNode max_lead_time (3.0s / 0.05s = 60 chunks) - Prevents stop-and-go backpressure pattern - Allows smooth buffering as intended 3. **Replace workaround with proper channel drainage** - Use tx.closed().await instead of sleep() - Guarantees all buffered chunks are processed - More architecturally sound solution 4. **Add comprehensive diagnostic traces** - Log expected vs actual block duration - Detect premature EOF (< 95% of expected duration) - Track bytes decoded and HTTP Content-Length - Monitor backpressure blocking with timing This fixes the streaming completely. The block will now: - Download for the full ~20 minutes (real-time with backpressure) - Decode all audio data without truncation - Process all chunks before pipeline shutdown
2025-11-12 10:32:47 +00:00
let mut timer_ogg = TimerNode::with_channel_size(max_lead_time, channel_size);
tracing::debug!("TimerNode (OGG) created with {:.1}s max lead time, {} chunk buffer", max_lead_time, channel_size);
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// StreamingOggFlacSink doesn't take channel_size - it uses bits_per_sample (16, 24, or 32)
let (ogg_sink, ogg_handle) = StreamingOggFlacSink::new(encoder_options, 16);
tracing::debug!("StreamingOggFlacSink created");
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
timer_ogg.register(Box::new(ogg_sink));
source_ogg.register(Box::new(timer_ogg));
tracing::info!("Pipeline 2 connected: RadioParadiseStreamSource → TimerNode → StreamingOggFlacSink");
// ═══════════════════════════════════════════════════════════════════════════
// Setup pmoserver with streaming routes
// ═══════════════════════════════════════════════════════════════════════════
tracing::info!("Setting up pmoserver...");
let mut server = ServerBuilder::new("RadioParadiseStreamTest", "http://localhost", 8080)
.build();
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
let app_state = Arc::new(AppState {
stream_handle,
ogg_handle,
});
// Add streaming routes
server.add_handler_with_state("/test/stream", stream_handler, app_state.clone()).await;
server.add_handler_with_state("/test/stream-icy", stream_icy_handler, app_state.clone()).await;
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
server.add_handler_with_state("/test/stream-ogg", stream_ogg_handler, app_state.clone()).await;
// Add metadata route
server.add_handler_with_state("/test/metadata", metadata_handler, app_state.clone()).await;
// Add health check
server.add_handler("/test/health", health_handler).await;
tracing::info!("");
tracing::info!("========================================");
tracing::info!("Ready to stream!");
tracing::info!("");
tracing::info!("Pure FLAC stream (for VLC, standard players):");
tracing::info!(" vlc http://localhost:8080/test/stream");
tracing::info!("");
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
tracing::info!("OGG-FLAC stream (streaming container with metadata support):");
tracing::info!(" vlc http://localhost:8080/test/stream-ogg");
tracing::info!("");
tracing::info!("FLAC + ICY metadata stream (for ICY-aware clients):");
tracing::info!(" http://localhost:8080/test/stream-icy");
tracing::info!("");
tracing::info!("Metadata endpoint (JSON):");
tracing::info!(" curl http://localhost:8080/test/metadata");
tracing::info!("========================================");
tracing::info!("");
// ═══════════════════════════════════════════════════════════════════════════
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Start pipelines and server
// ═══════════════════════════════════════════════════════════════════════════
let stop_token = CancellationToken::new();
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
let stop_token_flac = stop_token.clone();
let stop_token_ogg = stop_token.clone();
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Start FLAC pipeline in background
let pipeline_flac_handle = tokio::spawn(async move {
tracing::info!("[PIPELINE-FLAC] Starting...");
let result = Box::new(source_flac).run(stop_token_flac).await;
match &result {
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
Ok(()) => tracing::info!("[PIPELINE-FLAC] Completed successfully"),
Err(e) => tracing::error!("[PIPELINE-FLAC] Error: {}", e),
}
result
});
// Start OGG-FLAC pipeline in background
let pipeline_ogg_handle = tokio::spawn(async move {
tracing::info!("[PIPELINE-OGG] Starting...");
let result = Box::new(source_ogg).run(stop_token_ogg).await;
match &result {
Ok(()) => tracing::info!("[PIPELINE-OGG] Completed successfully"),
Err(e) => tracing::error!("[PIPELINE-OGG] Error: {}", e),
}
result
});
// Start pmoserver (blocks until Ctrl+C)
tracing::info!("[SERVER] Starting pmoserver...");
server.start().await;
server.wait().await;
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Server stopped, cancel pipelines
tracing::info!("Server stopped, canceling pipelines...");
stop_token.cancel();
Implement complete OGG-FLAC streaming with proper container wrapping This commit implements full OGG container support for FLAC streaming, wrapping FLAC frames in proper OGG pages with CRC32 validation. ## Changes ### pmoaudio-ext/src/sinks/streaming_ogg_flac_sink.rs - Implemented `broadcast_ogg_flac_stream()` with actual OGG wrapping - Added `OggPageWriter` struct for generating OGG pages with proper: - BOS (Beginning of Stream) flag for stream start - EOS (End of Stream) flag for stream end - Page segmentation (255-byte chunks) - CRC32 checksum calculation - Added `read_flac_header()` to extract FLAC header for OGG BOS packet - Added `create_empty_vorbis_comment()` for metadata block - Header caching: BOS + Vorbis Comment pages sent to late-joining clients - Streaming architecture: FLAC frames wrapped in ~4KB OGG pages ### pmoparadise/examples/stream_block.rs - Added dual pipeline support (FLAC + OGG-FLAC) - Added `/test/stream-ogg` endpoint for OGG-FLAC streaming - Updated help messages and documentation - Both pipelines run in parallel with separate sources ### pmoaudio-ext/Cargo.toml - Added `rand = "0.8"` dependency for OGG stream serial generation ## Architecture ``` PCM Input → FLAC Encoder → OGG Wrapper → Broadcast ↓ ↓ ↓ FLAC frames OGG pages HTTP clients ``` ## OGG-FLAC Format 1. BOS page: Contains FLAC identification ("fLaC" + STREAMINFO) 2. Comment page: Contains Vorbis Comment block (metadata) 3. Data pages: Contain FLAC audio frames (~4KB per page) 4. EOS page: Marks end of logical bitstream ## Testing Verified with Radio Paradise streaming: - OGG-FLAC encoder initializes correctly (44100 Hz) - FLAC header extracted (86 bytes) - OGG header cached (176 bytes: BOS + Comment) - Stream generates proper OGG pages (654KB test stream) ## Endpoints - `/test/stream` - Pure FLAC - `/test/stream-ogg` - OGG-FLAC container (NEW) - `/test/stream-icy` - FLAC + ICY metadata - `/test/metadata` - JSON metadata ## TODO (Deferred) OGG chaining on TrackBoundary: Would require encoder restart and new logical bitstream per track. Currently metadata is served via `/test/metadata` endpoint for real-time updates.
2025-11-12 00:26:00 +00:00
// Wait for both pipelines to finish
match pipeline_flac_handle.await {
Ok(Ok(())) => tracing::info!("FLAC pipeline completed successfully"),
Ok(Err(e)) => tracing::error!("FLAC pipeline error: {}", e),
Err(e) => tracing::error!("FLAC pipeline task error: {}", e),
}
match pipeline_ogg_handle.await {
Ok(Ok(())) => tracing::info!("OGG-FLAC pipeline completed successfully"),
Ok(Err(e)) => tracing::error!("OGG-FLAC pipeline error: {}", e),
Err(e) => tracing::error!("OGG-FLAC pipeline task error: {}", e),
}
tracing::info!("Shutdown complete");
Ok(())
}