Skip to main content

libfreemkv/mux/
stdio.rs

1//! StdioStream — PES frames via stdin/stdout with FMKV metadata header.
2//!
3//! The FMKV header carries stream metadata (PIDs, codecs, languages, codec_privates)
4//! so the receiving end can set up muxing without scanning the content.
5
6use super::meta;
7use crate::disc::DiscTitle;
8use std::io::{self, Write};
9
10/// Stdio stream — reads PES from stdin, writes PES to stdout.
11/// FMKV metadata header is written/read automatically.
12pub struct StdioStream {
13    disc_title: DiscTitle,
14    reader: Option<io::Stdin>,
15    writer: Option<io::BufWriter<io::Stdout>>,
16    header_written: bool,
17    header_read: bool,
18    stored_codec_privates: Vec<Option<Vec<u8>>>,
19}
20
21impl StdioStream {
22    /// Create a stdio stream for reading (stdin).
23    pub fn input() -> Self {
24        Self {
25            disc_title: DiscTitle::empty(),
26            reader: Some(io::stdin()),
27            writer: None,
28            header_written: false,
29            header_read: false,
30            stored_codec_privates: Vec::new(),
31        }
32    }
33
34    /// Create a stdio stream for writing (stdout).
35    pub fn output(title: &DiscTitle) -> Self {
36        Self {
37            disc_title: title.clone(),
38            reader: None,
39            writer: Some(io::BufWriter::new(io::stdout())),
40            header_written: false,
41            header_read: false,
42            stored_codec_privates: Vec::new(),
43        }
44    }
45
46    /// Read the FMKV metadata header from stdin on first read.
47    fn ensure_header_read(&mut self) -> io::Result<()> {
48        if self.header_read {
49            return Ok(());
50        }
51        self.header_read = true;
52        if let Some(ref mut r) = self.reader {
53            if let Ok(Some(m)) = meta::read_header(r) {
54                let title = m.to_title();
55                self.stored_codec_privates = title.codec_privates.clone();
56                self.disc_title = title;
57            }
58        }
59        Ok(())
60    }
61}
62
63impl crate::pes::Stream for StdioStream {
64    fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
65        self.ensure_header_read()?;
66        match &mut self.reader {
67            Some(r) => crate::pes::PesFrame::deserialize(r),
68            None => Err(crate::error::Error::StreamWriteOnly.into()),
69        }
70    }
71    fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
72        match &mut self.writer {
73            Some(ref mut w) => {
74                if !self.header_written {
75                    if !self.disc_title.streams.is_empty() {
76                        let m = meta::M2tsMeta::from_title(&self.disc_title);
77                        meta::write_header(w, &m)?;
78                    }
79                    self.header_written = true;
80                }
81                frame.serialize(w)
82            }
83            None => Err(crate::error::Error::StreamReadOnly.into()),
84        }
85    }
86    fn finish(&mut self) -> io::Result<()> {
87        if let Some(w) = &mut self.writer {
88            w.flush()?;
89        }
90        Ok(())
91    }
92    fn info(&self) -> &DiscTitle {
93        &self.disc_title
94    }
95
96    fn codec_private(&self, track: usize) -> Option<Vec<u8>> {
97        self.stored_codec_privates
98            .get(track)
99            .and_then(|c| c.clone())
100    }
101
102    fn headers_ready(&self) -> bool {
103        // After first read(), header is parsed and codec_privates populated
104        self.header_read || self.writer.is_some()
105    }
106}