aboutsummaryrefslogtreecommitdiff
path: root/remuxer/mp4/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-03-05 14:58:26 +0100
committermetamuffin <metamuffin@disroot.org>2026-03-05 14:58:26 +0100
commit8fa153c30c00210ac6512d73268a3adc930c5fbd (patch)
treeaeaea006953fd1add29c675dab3d3deb2a009ea5 /remuxer/mp4/src
parentec2228f6ee9349c0866483abc21124dae31f2b52 (diff)
downloadjellything-8fa153c30c00210ac6512d73268a3adc930c5fbd.tar
jellything-8fa153c30c00210ac6512d73268a3adc930c5fbd.tar.bz2
jellything-8fa153c30c00210ac6512d73268a3adc930c5fbd.tar.zst
add crate for dealing with mp4; convert fragmentmuxer output from writer to vec
Diffstat (limited to 'remuxer/mp4/src')
-rw-r--r--remuxer/mp4/src/boxes.rs268
-rw-r--r--remuxer/mp4/src/lib.rs37
2 files changed, 305 insertions, 0 deletions
diff --git a/remuxer/mp4/src/boxes.rs b/remuxer/mp4/src/boxes.rs
new file mode 100644
index 0000000..d2c3918
--- /dev/null
+++ b/remuxer/mp4/src/boxes.rs
@@ -0,0 +1,268 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+use crate::{BoxW, WriteBox};
+
+macro_rules! container_box {
+ ($typ:tt, $iden:literal) => {
+ pub struct $typ<T: Fn(&mut BoxW)>(pub T);
+ impl<T: Fn(&mut BoxW)> WriteBox for $typ<T> {
+ const BOXTYPE: [u8; 4] = *$iden;
+ fn write(&self, buf: &mut Vec<u8>) {
+ self.0(&mut BoxW(buf))
+ }
+ }
+ };
+}
+
+container_box!(Movie, b"moov");
+container_box!(Track, b"trak");
+container_box!(Edit, b"edts");
+container_box!(Media, b"mdia");
+container_box!(MediaInformation, b"minf");
+container_box!(DataInformation, b"dinf");
+container_box!(SampleTable, b"stbl");
+container_box!(MovieFragment, b"stbl");
+container_box!(TrackFragment, b"stbl");
+
+pub struct FileType<'a> {
+ pub major_brand: [u8; 4],
+ pub minor_version: u32,
+ pub compatible_brands: &'a [[u8; 4]],
+}
+impl WriteBox for FileType<'_> {
+ const BOXTYPE: [u8; 4] = *b"ftyp";
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.major_brand);
+ buf.extend(self.minor_version.to_be_bytes());
+ for cb in self.compatible_brands {
+ buf.extend(cb);
+ }
+ }
+}
+
+pub struct MovieHeader {
+ pub creation_time: u64,
+ pub modification_time: u64,
+ pub timescale: u32,
+ pub duration: u64,
+ pub rate: u32,
+ pub volume: u16,
+ pub matrix: [u32; 9],
+ pub pre_defined: [u32; 6],
+ pub next_track_id: u32,
+}
+
+impl WriteBox for MovieHeader {
+ const BOXTYPE: [u8; 4] = *b"mvhd";
+ const VERSION: Option<u8> = Some(1);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.creation_time.to_be_bytes());
+ buf.extend(self.modification_time.to_be_bytes());
+ buf.extend(self.timescale.to_be_bytes());
+ buf.extend(self.duration.to_be_bytes());
+ buf.extend(self.rate.to_be_bytes());
+ buf.extend(self.volume.to_be_bytes());
+ buf.extend([0; 10]);
+ for e in self.matrix {
+ buf.extend(e.to_be_bytes());
+ }
+ buf.extend([0; 24]);
+ buf.extend(self.next_track_id.to_be_bytes());
+ }
+}
+impl Default for MovieHeader {
+ fn default() -> Self {
+ Self {
+ creation_time: 0,
+ modification_time: 0,
+ timescale: 0,
+ duration: 0,
+ rate: 0x00010000,
+ volume: 0x0100,
+ matrix: [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x00010000],
+ pre_defined: [0; 6],
+ next_track_id: 0,
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct TrackHeader {
+ pub creation_time: u64,
+ pub modification_time: u64,
+ pub track_id: u32,
+ pub duration: u64,
+ pub layer: u16,
+ pub alternate_group: u16,
+ pub volume: u16,
+ pub matrix: [u32; 9],
+ pub width: u32,
+ pub height: u32,
+}
+impl WriteBox for TrackHeader {
+ const BOXTYPE: [u8; 4] = *b"tkhd";
+ const VERSION: Option<u8> = Some(1);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.creation_time.to_be_bytes());
+ buf.extend(self.modification_time.to_be_bytes());
+ buf.extend(self.track_id.to_be_bytes());
+ buf.extend([0; 4]);
+ buf.extend(self.duration.to_be_bytes());
+ buf.extend([0; 8]);
+ buf.extend(self.layer.to_be_bytes());
+ buf.extend(self.alternate_group.to_be_bytes());
+ buf.extend(self.volume.to_be_bytes());
+ buf.extend([0; 2]);
+ for e in self.matrix {
+ buf.extend(e.to_be_bytes());
+ }
+ buf.extend(self.width.to_be_bytes());
+ buf.extend(self.height.to_be_bytes());
+ }
+}
+
+pub struct EditList<'a> {
+ pub entries: &'a [EditListEntry],
+}
+pub struct EditListEntry {
+ pub edit_duration: u64,
+ pub media_time: u64,
+ pub media_rate: u32,
+}
+impl WriteBox for EditList<'_> {
+ const BOXTYPE: [u8; 4] = *b"elst";
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend((self.entries.len() as u32).to_be_bytes());
+ for e in self.entries {
+ buf.extend(e.edit_duration.to_be_bytes());
+ buf.extend(e.media_time.to_be_bytes());
+ buf.extend(e.media_rate.to_be_bytes());
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct MediaHeader {
+ pub creation_time: u64,
+ pub modification_time: u64,
+ pub timescale: u32,
+ pub duration: u64,
+ pub language: [u8; 3],
+ pub pre_defined: u16,
+}
+impl WriteBox for MediaHeader {
+ const BOXTYPE: [u8; 4] = *b"mdhd";
+ const VERSION: Option<u8> = Some(1);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.creation_time.to_be_bytes());
+ buf.extend(self.modification_time.to_be_bytes());
+ buf.extend(self.timescale.to_be_bytes());
+ buf.extend(self.duration.to_be_bytes());
+ buf.extend(
+ ((self.language[0] as u16 & 0b11111 << 10)
+ | (self.language[1] as u16 & 0b11111 << 5)
+ | (self.language[2] as u16 & 0b11111 << 0))
+ .to_be_bytes(),
+ );
+ buf.extend([0; 2]);
+ }
+}
+
+pub struct Handler {
+ pub handler_type: u32,
+ pub name: String,
+}
+impl WriteBox for Handler {
+ const BOXTYPE: [u8; 4] = *b"hdlr";
+ const VERSION: Option<u8> = Some(0);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend([0; 4]);
+ buf.extend(self.handler_type.to_be_bytes());
+ buf.extend([0; 12]);
+ buf.extend(self.name.as_bytes());
+ }
+}
+
+#[derive(Default)]
+pub struct VideoMediaHandler {
+ pub graphicsmode: u16,
+ pub opcolor: [u16; 3],
+}
+impl WriteBox for VideoMediaHandler {
+ const BOXTYPE: [u8; 4] = *b"vmhd";
+ const VERSION: Option<u8> = Some(0);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.graphicsmode.to_be_bytes());
+ buf.extend(self.opcolor[0].to_be_bytes());
+ buf.extend(self.opcolor[1].to_be_bytes());
+ buf.extend(self.opcolor[2].to_be_bytes());
+ }
+}
+
+#[derive(Default)]
+pub struct SoundMediaHandler {
+ pub balance: u16,
+}
+impl WriteBox for SoundMediaHandler {
+ const BOXTYPE: [u8; 4] = *b"smhd";
+ const VERSION: Option<u8> = Some(0);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.balance.to_be_bytes());
+ }
+}
+
+pub struct DataReference<'a> {
+ pub entries: &'a [()],
+}
+impl WriteBox for DataReference<'_> {
+ const BOXTYPE: [u8; 4] = *b"dref";
+ const VERSION: Option<u8> = Some(0);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend([0; 4]);
+ }
+}
+
+pub struct MovieFragmentHeader {
+ pub sequence_number: u32,
+}
+impl WriteBox for MovieFragmentHeader {
+ const BOXTYPE: [u8; 4] = *b"mfhd";
+ const VERSION: Option<u8> = Some(0);
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.sequence_number.to_be_bytes());
+ }
+}
+
+pub struct TrackFragmentHeader {
+ pub track_id: u32,
+}
+impl WriteBox for TrackFragmentHeader {
+ const BOXTYPE: [u8; 4] = *b"tfhd";
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.track_id.to_be_bytes());
+ }
+}
+
+pub struct TrackFragmentBaseMediaDecodeTime {
+ pub base_media_decode_time: u64,
+}
+impl WriteBox for TrackFragmentBaseMediaDecodeTime {
+ const BOXTYPE: [u8; 4] = *b"tfdt";
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.base_media_decode_time.to_be_bytes());
+ }
+}
+
+pub struct TrackRun {
+ pub data_offset: i32,
+}
+impl WriteBox for TrackRun {
+ const BOXTYPE: [u8; 4] = *b"trun";
+ fn write(&self, buf: &mut Vec<u8>) {
+ buf.extend(self.data_offset.to_be_bytes());
+ }
+}
diff --git a/remuxer/mp4/src/lib.rs b/remuxer/mp4/src/lib.rs
new file mode 100644
index 0000000..cb728fa
--- /dev/null
+++ b/remuxer/mp4/src/lib.rs
@@ -0,0 +1,37 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+mod boxes;
+pub use boxes::*;
+
+pub struct BoxW<'a>(&'a mut Vec<u8>);
+impl<'a> BoxW<'a> {
+ pub fn new(buffer: &'a mut Vec<u8>) -> Self {
+ Self(buffer)
+ }
+ pub fn write<T: WriteBox>(&mut self, b: T) {
+ let start = self.0.len();
+ self.0.extend(0u32.to_be_bytes());
+ self.0.extend(T::BOXTYPE);
+
+ if let Some(ver) = T::VERSION {
+ self.0.push(ver);
+ self.0.extend(&b.flags().to_be_bytes()[1..]);
+ }
+ b.write(self.0);
+ let size = (self.0.len() - start) as u32;
+ self.0[start..start + 4].copy_from_slice(&size.to_be_bytes());
+ }
+}
+
+pub trait WriteBox {
+ const BOXTYPE: [u8; 4];
+ const VERSION: Option<u8> = None;
+ fn write(&self, buf: &mut Vec<u8>);
+ fn flags(&self) -> u32 {
+ 0
+ }
+}