aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--Cargo.lock29
-rw-r--r--Cargo.toml3
-rw-r--r--framework/Cargo.toml7
-rw-r--r--framework/src/lib.rs137
-rw-r--r--mtree-test/Cargo.toml8
-rw-r--r--mtree-test/src/bin/decode.rs24
-rw-r--r--mtree-test/src/bin/encode.rs24
-rw-r--r--mtree-test/src/lib.rs0
9 files changed, 239 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 9ff28b4..b7afe61 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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