diff options
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | Cargo.lock | 29 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | framework/Cargo.toml | 7 | ||||
-rw-r--r-- | framework/src/lib.rs | 137 | ||||
-rw-r--r-- | mtree-test/Cargo.toml | 8 | ||||
-rw-r--r-- | mtree-test/src/bin/decode.rs | 24 | ||||
-rw-r--r-- | mtree-test/src/bin/encode.rs | 24 | ||||
-rw-r--r-- | mtree-test/src/lib.rs | 0 |
9 files changed, 239 insertions, 0 deletions
@@ -1,2 +1,9 @@ /target /old/target + + +# Added by cargo +# +# already existing elements were commented out + +#/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..00dd49d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,29 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ebml-struct" +version = "0.1.0" +source = "git+https://codeberg.org/metamuffin/ebml-struct#fcefaa67b85b96b17cec2d1c7f7c53998520559b" + +[[package]] +name = "framework" +version = "0.1.0" +dependencies = [ + "ebml-struct", +] + +[[package]] +name = "glam" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" + +[[package]] +name = "mtree-test" +version = "0.1.0" +dependencies = [ + "framework", + "glam", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..eccbbe0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["framework", "mtree-test"] +resolver = "3" diff --git a/framework/Cargo.toml b/framework/Cargo.toml new file mode 100644 index 0000000..939ff59 --- /dev/null +++ b/framework/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "framework" +version = "0.1.0" +edition = "2024" + +[dependencies] +ebml-struct = { git = "https://codeberg.org/metamuffin/ebml-struct" } diff --git a/framework/src/lib.rs b/framework/src/lib.rs new file mode 100644 index 0000000..84b6de9 --- /dev/null +++ b/framework/src/lib.rs @@ -0,0 +1,137 @@ +use ebml_struct::{ + Size, + ids::{ + EL_ATTACHMENTS, EL_CHAPTERS, EL_CLUSTER, EL_CRC32, EL_CUES, EL_EBML, EL_INFO, EL_SEEKHEAD, + EL_SEGMENT, EL_TAGS, EL_TRACKS, EL_VOID, + }, + matroska::{Cluster, Ebml, Info, Tags, Tracks}, + read::{EbmlReadExt, TagRead}, + write::{EbmlWriteExt, TagWrite}, +}; +use std::io::{BufReader, BufWriter, ErrorKind, Read, Result, stdin, stdout}; + +pub trait BitstreamFilter: Sized { + const INPUT_CODEC_ID: &str; + const OUTPUT_CODEC_ID: &str; + fn new(width: u32, height: u32) -> Self; + fn process_block(&mut self, a: Vec<u8>) -> Vec<u8>; +} + +pub fn bitstream_filter_main<F: BitstreamFilter>() -> Result<()> { + let mut inp = BufReader::new(stdin()); + let mut out = BufWriter::new(stdout()); + + let (id, inner) = inp.read_tag()?; + assert_eq!(id, EL_EBML); + inner.consume()?; + + Ebml { + ebml_version: 1, + ebml_read_version: 1, + ebml_max_id_length: 4, + ebml_max_size_length: 8, + doc_type: "matroska".to_string(), + doc_type_version: 4, + doc_type_read_version: 2, + doc_type_extensions: vec![], + } + .write(&mut out)?; + + let (id, mut segment) = inp.read_tag()?; + assert_eq!(id, EL_SEGMENT); + + out.write_tag_id(EL_SEGMENT)?; + out.write_size(Size::Unknown)?; + + let (mut info, mut tracks) = read_info_and_tracks(&mut segment)?; + info.muxing_app = "ebml-struct".to_string(); + info.writing_app = "video-codec-experiments".to_string(); + + let mut encode_track = None; + for t in &mut tracks.entries { + if t.track_type == 1 && encode_track.is_none() { + assert_eq!(t.codec_id, F::INPUT_CODEC_ID); + t.codec_id = F::OUTPUT_CODEC_ID.to_string(); + t.codec_private = None; + t.codec_download_urls = + vec!["https://codeberg.org/metamuffin/video-codec-experiments".to_string()]; + + encode_track = Some(t.clone()); + } + } + let filter_track = encode_track.expect("no video track found"); + + tracks.write(&mut out)?; + info.write(&mut out)?; + + let mut filter = F::new( + filter_track.video.as_ref().unwrap().pixel_width as u32, + filter_track.video.as_ref().unwrap().pixel_height as u32, + ); + + loop { + let (id, mut element) = match segment.read_tag() { + Ok(x) => x, + Err(e) if e.kind() == ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e), + }; + match id { + EL_CUES | EL_ATTACHMENTS | EL_SEEKHEAD | EL_CHAPTERS | EL_CRC32 | EL_VOID => { + element.consume()?; + } + EL_TAGS => { + Tags::read(&mut element)?.write(&mut out)?; + } + EL_CLUSTER => { + let mut cluster = Cluster::read(&mut element)?; + + for b in &mut cluster.simple_blocks { + if b.track == filter_track.track_number { + assert_eq!(b.lacing, None); + assert_eq!(b.discardable, false); + assert_eq!(b.keyframe, true); + b.data = filter.process_block(b.data.clone()); + } + } + + cluster.write(&mut out)?; + } + _ => { + eprintln!("unhandled element {id:x}"); + element.consume()?; + } + } + } + + Ok(()) +} + +fn read_info_and_tracks(mut segment: impl Read) -> Result<(Info, Tracks)> { + let mut info = None; + let mut tracks = None; + loop { + let (id, mut element) = segment.read_tag()?; + match id { + EL_CUES | EL_TAGS | EL_ATTACHMENTS | EL_SEEKHEAD | EL_CHAPTERS | EL_CRC32 | EL_VOID => { + element.consume()?; + } + EL_INFO => { + info = Some(Info::read(&mut element)?); + } + EL_TRACKS => { + tracks = Some(Tracks::read(&mut element)?); + } + EL_CLUSTER => { + eprintln!("not ready for clusters yet"); + element.consume()?; + } + _ => { + eprintln!("unhandled element {id:x}"); + element.consume()?; + } + }; + if tracks.is_some() && info.is_some() { + break Ok((info.unwrap(), tracks.unwrap())); + } + } +} diff --git a/mtree-test/Cargo.toml b/mtree-test/Cargo.toml new file mode 100644 index 0000000..5dbe38c --- /dev/null +++ b/mtree-test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mtree-test" +version = "0.1.0" +edition = "2024" + +[dependencies] +framework = { path = "../framework" } +glam = "0.30.3" diff --git a/mtree-test/src/bin/decode.rs b/mtree-test/src/bin/decode.rs new file mode 100644 index 0000000..6ff2cb3 --- /dev/null +++ b/mtree-test/src/bin/decode.rs @@ -0,0 +1,24 @@ +use framework::{BitstreamFilter, bitstream_filter_main}; +use glam::{I16Vec2, i16vec2}; +use std::io::Result; + +fn main() -> Result<()> { + bitstream_filter_main::<Dec>() +} + +struct Dec { + res: I16Vec2, +} +impl BitstreamFilter for Dec { + const INPUT_CODEC_ID: &str = "V_VCEMTREE"; + const OUTPUT_CODEC_ID: &str = "V_UNCOMPRESSED"; + + fn new(width: u32, height: u32) -> Self { + Self { + res: i16vec2(width as i16, height as i16), + } + } + fn process_block(&mut self, a: Vec<u8>) -> Vec<u8> { + a + } +} diff --git a/mtree-test/src/bin/encode.rs b/mtree-test/src/bin/encode.rs new file mode 100644 index 0000000..f85e4c0 --- /dev/null +++ b/mtree-test/src/bin/encode.rs @@ -0,0 +1,24 @@ +use framework::{BitstreamFilter, bitstream_filter_main}; +use glam::{I16Vec2, i16vec2}; +use std::io::Result; + +fn main() -> Result<()> { + bitstream_filter_main::<Enc>() +} + +struct Enc { + res: I16Vec2, +} +impl BitstreamFilter for Enc { + const INPUT_CODEC_ID: &str = "V_UNCOMPRESSED"; + const OUTPUT_CODEC_ID: &str = "V_VCEMTREE"; + + fn new(width: u32, height: u32) -> Self { + Self { + res: i16vec2(width as i16, height as i16), + } + } + fn process_block(&mut self, a: Vec<u8>) -> Vec<u8> { + a + } +} diff --git a/mtree-test/src/lib.rs b/mtree-test/src/lib.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mtree-test/src/lib.rs |