diff options
Diffstat (limited to 'matroska')
-rw-r--r-- | matroska/Cargo.toml | 10 | ||||
-rw-r--r-- | matroska/src/bin/mkvdump.rs | 28 | ||||
-rw-r--r-- | matroska/src/block.rs | 91 | ||||
-rw-r--r-- | matroska/src/error.rs | 24 | ||||
-rw-r--r-- | matroska/src/lib.rs | 27 | ||||
-rw-r--r-- | matroska/src/matroska.rs | 335 | ||||
-rw-r--r-- | matroska/src/read.rs | 298 | ||||
-rw-r--r-- | matroska/src/size.rs | 25 | ||||
-rw-r--r-- | matroska/src/unflatten.rs | 83 | ||||
-rw-r--r-- | matroska/src/write.rs | 382 |
10 files changed, 0 insertions, 1303 deletions
diff --git a/matroska/Cargo.toml b/matroska/Cargo.toml deleted file mode 100644 index 23c9c5a..0000000 --- a/matroska/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "jellymatroska" -version = "0.1.0" -edition = "2021" - -[dependencies] -ebml_derive = { path = "../ebml_derive" } -log = "0.4.25" -env_logger = "0.11.6" -thiserror = "2.0.11" diff --git a/matroska/src/bin/mkvdump.rs b/matroska/src/bin/mkvdump.rs deleted file mode 100644 index 48420c6..0000000 --- a/matroska/src/bin/mkvdump.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use jellymatroska::{matroska::MatroskaTag, read::EbmlReader}; -use std::{fs::File, io::BufReader}; - -fn main() { - env_logger::init_from_env("LOG"); - let path = std::env::args().nth(1).unwrap(); - let r = EbmlReader::new(BufReader::new(File::open(path).unwrap())); - - for tag in r { - let (position, tag) = tag.unwrap(); - match tag { - MatroskaTag::SimpleBlock(b) | MatroskaTag::Block(b) => { - println!( - "block t={} kf={} ts_off={}", - b.track, - b.flags.keyframe(), - b.timestamp_off - ) - } - _ => println!("{} {tag:?}", position.unwrap_or(0)), - } - } -} diff --git a/matroska/src/block.rs b/matroska/src/block.rs deleted file mode 100644 index 54d9de5..0000000 --- a/matroska/src/block.rs +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use crate::{ - read::ReadExt, - write::{vint_length, write_vint}, - ReadValue, Result, WriteValue, -}; -use std::io::{Cursor, Write}; - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum LacingType { - None, - Xiph, - FixedSize, - Ebml, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Block { - pub track: u64, - pub flags: Flags, - pub timestamp_off: i16, - pub data: Vec<u8>, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Flags(u8); - -impl Flags { - pub fn keyframe(&self) -> bool { - self.0 & 0b10000000 != 0 - } - pub fn lacing(&self) -> LacingType { - match self.0 & 0b00000110 { - 0b000 => LacingType::None, - 0b010 => LacingType::Xiph, - 0b100 => LacingType::FixedSize, - 0b110 => LacingType::Ebml, - _ => unreachable!(), - } - } - pub fn discardable(&self) -> bool { - self.0 & 0b00000001 != 0 - } - pub fn invisible(&self) -> bool { - self.0 & 0b00001000 != 0 - } -} - -impl ReadValue for Block { - fn from_buf(buf: &[u8]) -> Result<Self> { - let (track, c) = Cursor::new(buf).read_vint_len()?; - let timestamp_off = i16::from_be_bytes(buf[c..c + 2].try_into().unwrap()); - let flags = Flags(buf[c + 2]); - let data = Vec::from(&buf[c + 3..]); - - Ok(Self { - track, - data, - flags, - timestamp_off, - }) - } -} - -impl WriteValue for Block { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - write_vint(w, self.inner_len() as u64)?; - write_vint(w, self.track)?; - w.write_all(&self.timestamp_off.to_be_bytes())?; - w.write_all(&[self.flags.0])?; - w.write_all(&self.data)?; - Ok(()) - } - fn size(&self) -> usize { - let il = self.inner_len(); - vint_length(il as u64) + il - } -} - -impl Block { - fn inner_len(&self) -> usize { - vint_length(self.track) - + 2 // timestamp - + 1 // flags - + self.data.len() - } -} diff --git a/matroska/src/error.rs b/matroska/src/error.rs deleted file mode 100644 index d2bbcbf..0000000 --- a/matroska/src/error.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum Error { - #[error("invalid padding")] - InvalidPadding, - #[error("varint too long")] - VarintTooLong, - #[error("global tags dont provide any context")] - GlobalTagsAsContext, - #[error("invalid length of a exact size type")] - InvalidTypeLen, - #[error("invalid utf8")] - InvalidUTF8, - #[error("unknown id")] - UnknownID, - #[error("io: {0}")] - Io(#[from] std::io::Error), -} diff --git a/matroska/src/lib.rs b/matroska/src/lib.rs deleted file mode 100644 index 1098ca6..0000000 --- a/matroska/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -pub mod block; -pub mod error; -pub mod matroska; -pub mod read; -pub mod size; -pub mod unflatten; -pub mod write; - -pub use matroska::MatroskaTag; -pub use read::ReadValue; -pub use write::WriteValue; - -#[derive(Debug, Clone, PartialEq)] -pub enum Master { - Collected(Vec<MatroskaTag>), - Start, - End, -} - -pub(crate) use block::Block; -pub(crate) use error::Error; -pub(crate) type Result<T> = core::result::Result<T, Error>; diff --git a/matroska/src/matroska.rs b/matroska/src/matroska.rs deleted file mode 100644 index 50e12b9..0000000 --- a/matroska/src/matroska.rs +++ /dev/null @@ -1,335 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use ebml_derive::define_ebml; - -define_ebml! { - global Crc32[0xbf]: Binary, - global Void[0xec]: Binary, - - Ebml[0x1a45dfa3]: { - EbmlVersion[0x4286]: Uint, - EbmlReadVersion[0x42f7]: Uint, - EbmlMaxIdLength[0x42f2]: Uint, - EbmlMaxSizeLength[0x42f3]: Uint, - DocType[0x4282]: Utf8, - DocTypeVersion[0x4287]: Uint, - DocTypeReadVersion[0x4285]: Uint, - DocTypeExtension[0x4281]: { - DocTypeExtensionName[0x4283]: Utf8, - DocTypeExtensionVersion[0x4284]: Uint, - }, - }, - - Segment[0x18538067]: { - Attachments[0x1941A469]: { - AttachedFile[0x61A7]: { - FileData[0x465C]: Binary, - FileDescription[0x467E]: Utf8, - FileMimeType[0x4660]: Utf8, - FileName[0x466E]: Utf8, - FileReferral[0x4675]: Binary, - FileUID[0x46AE]: Uint, - FileUsedEndTime[0x4662]: Uint, - FileUsedStartTime[0x4661]: Uint, - }, - }, - - Chapters[0x1043A770]: { - EditionEntry[0x45B9]: { - ChapterAtom[0xB6]: { - ChapProcess[0x6944]: { - ChapProcessCodecID[0x6955]: Uint, - ChapProcessCommand[0x6911]: { - ChapProcessData[0x6933]: Binary, - ChapProcessTime[0x6922]: Uint, - }, - ChapProcessPrivate[0x450D]: Binary, - }, - ChapterDisplay[0x80]: { - ChapCountry[0x437E]: Utf8, - ChapLanguage[0x437C]: Utf8, - ChapLanguageIETF[0x437D]: Utf8, - ChapString[0x85]: Utf8, - }, - ChapterFlagEnabled[0x4598]: Uint, - ChapterFlagHidden[0x98]: Uint, - ChapterPhysicalEquiv[0x63C3]: Uint, - ChapterSegmentEditionUID[0x6EBC]: Uint, - ChapterSegmentUID[0x6E67]: Binary, - ChapterStringUID[0x5654]: Utf8, - ChapterTimeEnd[0x92]: Uint, - ChapterTimeStart[0x91]: Uint, - ChapterUID[0x73C4]: Uint, - ChapterTrack[0x8F]: { - ChapterTrackUID[0x89]: Uint, - }, - }, - EditionFlagDefault[0x45DB]: Uint, - EditionFlagHidden[0x45BD]: Uint, - EditionFlagOrdered[0x45DD]: Uint, - EditionUID[0x45BC]: Uint, - }, - }, - - Cluster[0x1F43B675]: { - BlockGroup[0xA0]: { - Block[0xA1]: Block, - BlockAdditions[0x75A1]: { - BlockMore[0xA6]: { - BlockAddID[0xEE]: Uint, - BlockAdditional[0xA5]: Binary, - }, - }, - BlockDuration[0x9B]: Uint, - BlockVirtual[0xA2]: Binary, - CodecState[0xA4]: Binary, - DiscardPadding[0x75A2]: Int, - ReferenceBlock[0xFB]: Int, - ReferenceFrame[0xC8]: { - ReferenceOffset[0xC9]: Uint, - ReferenceTimestamp[0xCA]: Uint, - }, - ReferencePriority[0xFA]: Uint, - ReferenceVirtual[0xFD]: Int, - Slices[0x8E]: { - TimeSlice[0xE8]: { - BlockAdditionID[0xCB]: Uint, - Delay[0xCE]: Uint, - FrameNumber[0xCD]: Uint, - LaceNumber[0xCC]: Uint, - SliceDuration[0xCF]: Uint, - }, - }, - }, - EncryptedBlock[0xAF]: Binary, - Position[0xA7]: Uint, - PrevSize[0xAB]: Uint, - SilentTracks[0x5854]: { - SilentTrackNumber[0x58D7]: Uint, - }, - SimpleBlock[0xA3]: Block, - Timestamp[0xE7]: Uint, - }, - - Cues[0x1C53BB6B]: { - CuePoint[0xBB]: { - CueTime[0xB3]: Uint, - CueTrackPositions[0xB7]: { - CueBlockNumber[0x5378]: Uint, - CueClusterPosition[0xF1]: Uint, - CueCodecState[0xEA]: Uint, - CueDuration[0xB2]: Uint, - CueReference[0xDB]: { - CueRefCluster[0x97]: Uint, - CueRefCodecState[0xEB]: Uint, - CueRefNumber[0x535F]: Uint, - CueRefTime[0x96]: Uint, - }, - CueRelativePosition[0xF0]: Uint, - CueTrack[0xF7]: Uint, - }, - }, - }, - - Info[0x1549A966]: { - ChapterTranslate[0x6924]: { - ChapterTranslateCodec[0x69BF]: Uint, - ChapterTranslateEditionUID[0x69FC]: Uint, - ChapterTranslateID[0x69A5]: Binary, - }, - DateUTC[0x4461]: Int, - Duration[0x4489]: Float, - MuxingApp[0x4D80]: Utf8, - NextFilename[0x3E83BB]: Utf8, - NextUID[0x3EB923]: Binary, - PrevFilename[0x3C83AB]: Utf8, - PrevUID[0x3CB923]: Binary, - SegmentFamily[0x4444]: Binary, - SegmentFilename[0x7384]: Utf8, - SegmentUID[0x73A4]: Binary, - TimestampScale[0x2AD7B1]: Uint, - Title[0x7BA9]: Utf8, - WritingApp[0x5741]: Utf8, - }, - - SeekHead[0x114D9B74]: { - Seek[0x4DBB]: { - SeekID[0x53AB]: Binary, - SeekPosition[0x53AC]: Uint, - }, - }, - - Tags[0x1254C367]: { - Tag[0x7373]: { - SimpleTag[0x67C8]: { - TagBinary[0x4485]: Binary, - TagDefault[0x4484]: Uint, - TagDefaultBogus[0x44B4]: Uint, - TagLanguage[0x447A]: Utf8, - TagLanguageIETF[0x447B]: Utf8, - TagName[0x45A3]: Utf8, - TagString[0x4487]: Utf8, - }, - Targets[0x63C0]: { - TagAttachmentUID[0x63C6]: Uint, - TagChapterUID[0x63C4]: Uint, - TagEditionUID[0x63C9]: Uint, - TagTrackUID[0x63C5]: Uint, - TargetType[0x63CA]: Utf8, - TargetTypeValue[0x68CA]: Uint, - }, - }, - }, - - Tracks[0x1654AE6B]: { - TrackEntry[0xAE]: { - AttachmentLink[0x7446]: Uint, - Audio[0xE1]: { - BitDepth[0x6264]: Uint, - ChannelPositions[0x7D7B]: Binary, - Channels[0x9F]: Uint, - OutputSamplingFrequency[0x78B5]: Float, - SamplingFrequency[0xB5]: Float, - }, - BlockAdditionMapping[0x41E4]: { - BlockAddIDExtraData[0x41ED]: Binary, - BlockAddIDName[0x41A4]: Utf8, - BlockAddIDType[0x41E7]: Uint, - BlockAddIDValue[0x41F0]: Uint, - }, - CodecDecodeAll[0xAA]: Uint, - CodecDelay[0x56AA]: Uint, - CodecDownloadURL[0x26B240]: Utf8, - CodecID[0x86]: Utf8, - CodecInfoURL[0x3B4040]: Utf8, - CodecName[0x258688]: Utf8, - CodecPrivate[0x63A2]: Binary, - CodecSettings[0x3A9697]: Utf8, - ContentEncodings[0x6D80]: { - ContentEncoding[0x6240]: { - ContentCompression[0x5034]: { - ContentCompAlgo[0x4254]: Uint, - ContentCompSettings[0x4255]: Binary, - }, - ContentEncodingOrder[0x5031]: Uint, - ContentEncodingScope[0x5032]: Uint, - ContentEncodingType[0x5033]: Uint, - ContentEncryption[0x5035]: { - ContentEncAESSettings[0x47E7]: { - AESSettingsCipherMode[0x47E8]: Uint, - }, - ContentEncAlgo[0x47E1]: Uint, - ContentEncKeyID[0x47E2]: Binary, - ContentSigAlgo[0x47E5]: Uint, - ContentSigHashAlgo[0x47E6]: Uint, - ContentSigKeyID[0x47E4]: Binary, - ContentSignature[0x47E3]: Binary, - }, - }, - }, - DefaultDecodedFieldDuration[0x234E7A]: Uint, - DefaultDuration[0x23E383]: Uint, - FlagCommentary[0x55AF]: Uint, - FlagDefault[0x88]: Uint, - FlagEnabled[0xB9]: Uint, - FlagForced[0x55AA]: Uint, - FlagHearingImpaired[0x55AB]: Uint, - FlagLacing[0x9C]: Uint, - FlagOriginal[0x55AE]: Uint, - FlagTextDescriptions[0x55AD]: Uint, - FlagVisualImpaired[0x55AC]: Uint, - Language[0x22B59C]: Utf8, - LanguageIETF[0x22B59D]: Utf8, - MaxBlockAdditionID[0x55EE]: Uint, - MaxCache[0x6DF8]: Uint, - MinCache[0x6DE7]: Uint, - Name[0x536E]: Utf8, - SeekPreRoll[0x56BB]: Uint, - TrackNumber[0xD7]: Uint, - TrackOffset[0x537F]: Int, - TrackOperation[0xE2]: { - TrackCombinePlanes[0xE3]: { - TrackPlane[0xE4]: { - TrackPlaneType[0xE6]: Uint, - TrackPlaneUID[0xE5]: Uint, - }, - }, - TrackJoinBlocks[0xE9]: { - TrackJoinUID[0xED]: Uint, - }, - }, - TrackOverlay[0x6FAB]: Uint, - TrackTimestampScale[0x23314F]: Float, - TrackTranslate[0x6624]: { - TrackTranslateCodec[0x66BF]: Uint, - TrackTranslateEditionUID[0x66FC]: Uint, - TrackTranslateTrackID[0x66A5]: Binary, - }, - TrackType[0x83]: Uint, - TrackUID[0x73C5]: Uint, - TrickMasterTrackSegmentUID[0xC4]: Binary, - TrickMasterTrackUID[0xC7]: Uint, - TrickTrackFlag[0xC6]: Uint, - TrickTrackSegmentUID[0xC1]: Binary, - TrickTrackUID[0xC0]: Uint, - Video[0xE0]: { - AlphaMode[0x53C0]: Uint, - AspectRatioType[0x54B3]: Uint, - Colour[0x55B0]: { - BitsPerChannel[0x55B2]: Uint, - CbSubsamplingHorz[0x55B5]: Uint, - CbSubsamplingVert[0x55B6]: Uint, - ChromaSitingHorz[0x55B7]: Uint, - ChromaSitingVert[0x55B8]: Uint, - ChromaSubsamplingHorz[0x55B3]: Uint, - ChromaSubsamplingVert[0x55B4]: Uint, - MasteringMetadata[0x55D0]: { - LuminanceMax[0x55D9]: Float, - LuminanceMin[0x55DA]: Float, - PrimaryBChromaticityX[0x55D5]: Float, - PrimaryBChromaticityY[0x55D6]: Float, - PrimaryGChromaticityX[0x55D3]: Float, - PrimaryGChromaticityY[0x55D4]: Float, - PrimaryRChromaticityX[0x55D1]: Float, - PrimaryRChromaticityY[0x55D2]: Float, - WhitePointChromaticityX[0x55D7]: Float, - WhitePointChromaticityY[0x55D8]: Float, - }, - MatrixCoefficients[0x55B1]: Uint, - MaxCLL[0x55BC]: Uint, - MaxFALL[0x55BD]: Uint, - Primaries[0x55BB]: Uint, - Range[0x55B9]: Uint, - TransferCharacteristics[0x55BA]: Uint, - }, - DisplayHeight[0x54BA]: Uint, - DisplayUnit[0x54B2]: Uint, - DisplayWidth[0x54B0]: Uint, - FieldOrder[0x9D]: Uint, - FlagInterlaced[0x9A]: Uint, - FrameRate[0x2383E3]: Float, - GammaValue[0x2FB523]: Float, - OldStereoMode[0x53B9]: Uint, - PixelCropBottom[0x54AA]: Uint, - PixelCropLeft[0x54CC]: Uint, - PixelCropRight[0x54DD]: Uint, - PixelCropTop[0x54BB]: Uint, - PixelHeight[0xBA]: Uint, - PixelWidth[0xB0]: Uint, - Projection[0x7670]: { - ProjectionPosePitch[0x7674]: Float, - ProjectionPoseRoll[0x7675]: Float, - ProjectionPoseYaw[0x7673]: Float, - ProjectionPrivate[0x7672]: Binary, - ProjectionType[0x7671]: Uint, - }, - StereoMode[0x53B8]: Uint, - UncompressedFourCC[0x2EB524]: Binary, - }, - }, - }, - }, -} diff --git a/matroska/src/read.rs b/matroska/src/read.rs deleted file mode 100644 index c3d06fa..0000000 --- a/matroska/src/read.rs +++ /dev/null @@ -1,298 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use crate::{error::Error, matroska::MatroskaTag, size::EbmlSize, Master, Result}; -use log::{debug, warn}; -use std::{ - collections::VecDeque, - io::{Read, Seek, SeekFrom}, -}; - -trait ReadAndSeek: Read + Seek {} -impl<T: Read + Seek> ReadAndSeek for T {} - -#[derive(Debug, Clone, Copy)] -pub struct StackTag { - end: Option<u64>, - id: u64, -} - -pub struct EbmlReader { - inner: Box<dyn ReadAndSeek>, - stack: Vec<StackTag>, - queue: VecDeque<(Option<u64>, MatroskaTag)>, - position: u64, -} - -impl Read for EbmlReader { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { - let r = self.inner.read(buf)?; - self.position += r as u64; - Ok(r) - } -} - -impl EbmlReader { - pub fn new<T: Seek + Read + 'static>(inner: T) -> Self { - Self { - queue: VecDeque::new(), - inner: Box::new(inner), - stack: vec![], - position: 0, - } - } - - #[inline] - pub fn read_byte(&mut self) -> Result<u8> { - let mut b = [0u8]; - self.inner.read_exact(&mut b).map_err(Error::Io)?; - self.position += 1; - Ok(b[0]) - } - - pub fn read_buf(&mut self, size: impl Into<usize>) -> Result<Vec<u8>> { - let size = size.into(); - let mut b = vec![0u8; size]; - self.inner.read_exact(&mut b).map_err(Error::Io)?; - self.position += size as u64; - Ok(b) - } - - pub fn read_vint_len(&mut self) -> Result<(u64, usize)> { - let s = self.read_byte()?; - let len = s.leading_zeros() + 1; - if len > 8 { - Err(Error::VarintTooLong)? - } - let mut value = s as u64; - value -= 1 << (8 - len); - for _ in 1..len { - value <<= 8; - value += self.read_byte()? as u64; - } - Ok((value, len as usize)) - } - - #[inline] - pub fn read_vint(&mut self) -> Result<u64> { - Ok(self.read_vint_len()?.0) - } - - #[inline] - pub fn read_utf8(&mut self, size: impl Into<usize>) -> Result<String> { - let b = self.read_buf(size)?; - String::from_utf8(b).map_err(|_| Error::InvalidUTF8) - } - - #[inline] - pub fn read_tag_id(&mut self) -> Result<u64> { - let (value, len) = self.read_vint_len()?; - Ok(value + (1 << (7 * len))) - } - - #[inline] - pub fn read_tag_size(&mut self) -> Result<EbmlSize> { - Ok(EbmlSize::from_vint(self.read_vint_len()?)) - } - - /// reads *some* amount of tags from the stream and pushes it to the queue. - pub fn read_stuff(&mut self) -> Result<()> { - while let Some(e) = self.stack.last().copied() { - if let Some(end) = e.end { - if self.position >= end { - if self.position != end { - warn!("we missed the end by {} bytes", self.position - end) - } - self.stack.pop(); - self.queue - .push_back((None, MatroskaTag::construct_master(e.id, Master::End)?)); - } else { - break; - } - } else { - break; - } - } - - let start_position = self.position; - let id = self.read_tag_id()?; - let size = self.read_tag_size()?; - let is_master = MatroskaTag::is_master(id)?; - let tag = if is_master { - MatroskaTag::construct_master(id, Master::Start)? - } else { - let data = self.read_buf(size.some().unwrap())?; - MatroskaTag::parse(id, &data)? - }; - - if let Some(path) = tag.path() { - // we have slightly different rules for closing tags implicitly - // this closes as many tags as needed to make the next tag a valid child - while let Some(stag @ StackTag { end: None, .. }) = self.stack.last() { - if path.last() == Some(&stag.id) { - break; - } else { - let end = - MatroskaTag::construct_master(self.stack.pop().unwrap().id, Master::End)?; - self.queue.push_back((None, end)); - } - } - } - - if is_master { - self.stack.push(StackTag { - end: size.some().map(|s| s as u64 + self.position), - id, - }); - } - self.queue.push_back((Some(start_position), tag)); - Ok(()) - } - - /// context should be the next expected tag, such that the stack can be derived from its path. - pub fn seek(&mut self, position: u64, context: MatroskaTag) -> Result<()> { - let path = context.path().ok_or(Error::GlobalTagsAsContext)?; - debug!( - "seeking to {position} with a context restored from path {:x?}", - path - ); - self.queue.clear(); - self.position = position; - self.inner.seek(SeekFrom::Start(position))?; - self.stack = path - .iter() - .map(|id| StackTag { id: *id, end: None }) - .collect(); - Ok(()) - } -} - -impl Iterator for EbmlReader { - type Item = Result<(Option<u64>, MatroskaTag)>; - fn next(&mut self) -> Option<Self::Item> { - if let Some(t) = self.queue.pop_front() { - // match t { - // MatroskaTag::SimpleBlock(_) | MatroskaTag::Block(_) => (), - // _ => debug!("reader yield: {t:?}"), - // }; - Some(Ok(t)) - } else { - match self.read_stuff() { - Ok(()) => self.next(), - // in case we reached the end (error: failed to fill whole buffer), - // return the rest in the queue and pop all items of the stack - Err(e) => { - // TODO this is horrible, should use a custom error enum instead - if format!("{e}").as_str() == "failed to fill whole buffer" { - match self.queue.pop_front() { - Some(q) => Some(Ok(q)), - None => match self.stack.pop() { - Some(q) => Some(Ok(( - None, - MatroskaTag::construct_master(q.id, Master::End).unwrap(), - ))), - None => Some(Err(e)), - }, - } - } else { - Some(Err(e)) - } - } - } - } - } -} - -pub trait ReadValue: Sized { - fn from_buf(buf: &[u8]) -> Result<Self>; -} - -impl ReadValue for u64 { - fn from_buf(buf: &[u8]) -> Result<Self> { - if buf.len() > 8 { - Err(Error::InvalidTypeLen)? - } - let mut val = 0u64; - for byte in buf { - val <<= 8; - val |= *byte as u64; - } - Ok(val) - } -} -impl ReadValue for i64 { - fn from_buf(buf: &[u8]) -> Result<Self> { - if buf.len() > 8 { - Err(Error::InvalidTypeLen)? - } - Ok(if buf[0] > 127 { - if buf.len() == 8 { - i64::from_be_bytes(buf.try_into().unwrap()) - } else { - -((1 << (buf.len() * 8)) - (u64::from_buf(buf)? as i64)) - } - } else { - u64::from_buf(buf)? as i64 - }) - } -} -impl ReadValue for f64 { - fn from_buf(buf: &[u8]) -> Result<Self> { - Ok(if buf.len() == 4 { - f32::from_be_bytes(buf.try_into().unwrap()) as f64 - } else if buf.len() == 8 { - f64::from_be_bytes(buf.try_into().unwrap()) - } else { - Err(Error::InvalidTypeLen)? - }) - } -} - -impl ReadValue for Vec<u8> { - fn from_buf(buf: &[u8]) -> Result<Self> { - Ok(buf.to_vec()) - } -} -impl ReadValue for String { - fn from_buf(buf: &[u8]) -> Result<Self> { - String::from_utf8(Vec::from(buf)).map_err(|_| Error::InvalidUTF8) - } -} -impl ReadValue for Master { - fn from_buf(_: &[u8]) -> Result<Self> { - panic!("master shall not be read like this") - } -} - -pub trait ReadExt: Read { - fn read_byte(&mut self) -> Result<u8>; - fn read_vint_len(&mut self) -> Result<(u64, usize)>; - fn read_vint(&mut self) -> Result<u64>; -} -impl<T: Read> ReadExt for T { - fn read_byte(&mut self) -> Result<u8> { - let mut b = [0u8]; - self.read_exact(&mut b).map_err(Error::Io)?; - Ok(b[0]) - } - fn read_vint_len(&mut self) -> Result<(u64, usize)> { - let s = self.read_byte()?; - let len = s.leading_zeros() + 1; - if len > 8 { - Err(Error::VarintTooLong)? - } - let mut value = s as u64; - value -= 1 << (8 - len); - for _ in 1..len { - value <<= 8; - value += self.read_byte()? as u64; - } - Ok((value, len as usize)) - } - #[inline] - fn read_vint(&mut self) -> Result<u64> { - Ok(self.read_vint_len()?.0) - } -} diff --git a/matroska/src/size.rs b/matroska/src/size.rs deleted file mode 100644 index 7ef0de9..0000000 --- a/matroska/src/size.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum EbmlSize { - Exact(usize), - Unknown, -} -impl EbmlSize { - pub fn from_vint((value, len): (u64, usize)) -> EbmlSize { - if value == ((1 << (7 * len)) - 1) { - Self::Unknown - } else { - Self::Exact(value as usize) - } - } - pub fn some(self) -> Option<usize> { - match self { - EbmlSize::Exact(s) => Some(s), - EbmlSize::Unknown => None, - } - } -} diff --git a/matroska/src/unflatten.rs b/matroska/src/unflatten.rs deleted file mode 100644 index d5fb887..0000000 --- a/matroska/src/unflatten.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use crate::{matroska::MatroskaTag, Master, Result}; - -pub struct Unflat<'a> { - pub item: MatroskaTag, - pub children: Option<Unflatten<'a>>, - pub position: Option<u64>, -} - -pub struct Unflatten<'a> { - inner: &'a mut dyn Iterator<Item = Result<(Option<u64>, MatroskaTag)>>, - stop: bool, - end: Option<MatroskaTag>, -} - -impl<'a> Unflatten<'a> { - pub fn new(inner: &'a mut dyn Iterator<Item = Result<(Option<u64>, MatroskaTag)>>) -> Self { - Self { - inner, - stop: false, - end: None, - } - } - pub fn new_with_end( - inner: &'a mut dyn Iterator<Item = Result<(Option<u64>, MatroskaTag)>>, - start: MatroskaTag, - ) -> Self { - Self { - inner, - stop: false, - end: Some(MatroskaTag::construct_master(start.id(), Master::End).unwrap()), - } - } - pub fn exit_dirty(&mut self) { - self.stop = true; - } - - pub fn n(&mut self) -> Option<Result<Unflat<'_>>> { - if self.stop { - return None; - } - match self.inner.next() { - None => None, - Some(Err(e)) => Some(Err(e)), - Some(Ok((position, item))) => { - let master = MatroskaTag::is_master(item.id()).unwrap(); - if Some(&item) == self.end.as_ref() { - self.stop = true; - None - } else { - Some(Ok(Unflat { - position, - children: if master { - let end = - MatroskaTag::construct_master(item.id(), Master::End).unwrap(); - if end == item { - return None; - } - Some(Unflatten { - inner: self.inner, - stop: false, - end: Some(end), - }) - } else { - None - }, - item, - })) - } - } - } - } -} - -impl Drop for Unflatten<'_> { - fn drop(&mut self) { - while self.n().is_some() {} - } -} diff --git a/matroska/src/write.rs b/matroska/src/write.rs deleted file mode 100644 index 58923c6..0000000 --- a/matroska/src/write.rs +++ /dev/null @@ -1,382 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use crate::{error::Error, matroska::MatroskaTag, size::EbmlSize, Master, Result}; -use log::debug; -use std::io::{Seek, Write}; - -pub struct EbmlWriter<W> { - inner: W, - position: usize, -} - -impl<W: Write> EbmlWriter<W> { - pub fn new(inner: W, position: usize) -> Self { - Self { inner, position } - } - - pub fn position(&self) -> usize { - self.position - } - - pub fn write_padding(&mut self, position: usize) -> Result<()> { - debug!("padding up to {position}"); - let mut size = position - self.position; - match size { - 0 => return Ok(()), - 1 => Err(Error::InvalidPadding)?, - _ => (), - } - size -= 1; // subtract tag size - size -= 4; // subtract vint size - - // match size { - // _ if size < (1 << 7) => size -= 1, - // _ if size < (1 << 14) => size -= 2, - // _ if size < (1 << 21) => size -= 3, - // _ if size < (1 << 28) => size -= 4, - // _ if size < (1 << 35) => size -= 5, - // _ => bail!("padding too large"), - // } - - self.write_all(&[0xec])?; - self.write_vint_len(size.try_into().unwrap(), 4)?; - self.write_all(&vec![0; size])?; - Ok(()) - } - - #[inline] - pub fn write_tag(&mut self, tag: &MatroskaTag) -> Result<()> { - tag.write_full(self)?; - Ok(()) - } - - pub fn write_vint_len(&mut self, i: u64, len: usize) -> Result<()> { - let mut bytes = i.to_be_bytes(); - let trunc = &mut bytes[(8 - len)..]; - trunc[0] |= 1 << (8 - len); - self.write_all(trunc)?; - Ok(()) - } -} - -impl<W: Seek> Seek for EbmlWriter<W> { - fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { - self.inner.seek(pos)?; - match pos { - std::io::SeekFrom::Start(s) => self.position = s as usize, - std::io::SeekFrom::End(_) => unimplemented!(), - std::io::SeekFrom::Current(s) => self.position += s as usize, - } - Ok(self.position as u64) - } -} - -impl<W: Write> Write for EbmlWriter<W> { - fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { - let size = self.inner.write(buf)?; - self.position += size; - Ok(size) - } - - fn flush(&mut self) -> std::io::Result<()> { - todo!() - } -} - -impl MatroskaTag { - pub fn write_full(&self, w: &mut impl Write) -> Result<()> { - for b in self.id().to_be_bytes().iter().skip_while(|&v| *v == 0u8) { - w.write_all(&[*b])?; - } - self.write(w)?; - Ok(()) - } - pub fn size_full(&self) -> usize { - (8 - self.id().leading_zeros() as usize / 8) + self.size() - } -} - -pub fn write_vint(w: &mut impl Write, i: u64) -> Result<()> { - if i > (1 << 56) - 1 { - Err(Error::VarintTooLong)? - } - let len = (64 - i.leading_zeros() as usize) / 7 + 1; - let mut bytes = i.to_be_bytes(); - let trunc = &mut bytes[(8 - len)..]; - trunc[0] |= 1 << (8 - len); - w.write_all(trunc)?; - Ok(()) -} - -/// this routine works only, if the varint is as small as it can possibly be. -/// thats not always what we do though - see below -pub fn vint_length(v: u64) -> usize { - let mut len = 1; - while len <= 8 { - if v < (1 << ((7 * len) - 1)) { - break; - } - len += 1; - } - len -} -pub fn bad_vint_length(v: u64) -> usize { - match 64 - v.leading_zeros() { - x if x <= 8 => 1, - x if x <= 16 => 2, - x if x <= 32 => 4, - _ => 8, - } -} - -pub trait WriteValue { - /// writes the contents of a tag, including the size but excluding the id. - fn write_to(&self, w: &mut impl Write) -> Result<()>; - fn size(&self) -> usize; -} - -impl WriteValue for i64 { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - match 64 - self.leading_zeros() { - x if x <= 8 => { - w.write_all(&[0x81])?; - w.write_all(&(*self as i8).to_be_bytes())?; - } - x if x <= 16 => { - w.write_all(&[0x82])?; - w.write_all(&(*self as i16).to_be_bytes())?; - } - x if x <= 32 => { - w.write_all(&[0x84])?; - w.write_all(&(*self as i32).to_be_bytes())?; - } - _ => { - w.write_all(&[0x88])?; - w.write_all(&self.to_be_bytes())?; - } - }; - Ok(()) - } - - fn size(&self) -> usize { - 1 + match 64 - self.leading_zeros() { - x if x <= 8 => 1, - x if x <= 16 => 2, - x if x <= 32 => 4, - _ => 8, - } - } -} -impl WriteValue for u64 { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - match 64 - self.leading_zeros() { - x if x <= 8 => { - w.write_all(&[0x81])?; - w.write_all(&(*self as u8).to_be_bytes())?; - } - x if x <= 16 => { - w.write_all(&[0x82])?; - w.write_all(&(*self as u16).to_be_bytes())?; - } - x if x <= 32 => { - w.write_all(&[0x84])?; - w.write_all(&(*self as u32).to_be_bytes())?; - } - _ => { - w.write_all(&[0x88])?; - w.write_all(&self.to_be_bytes())?; - } - }; - Ok(()) - } - fn size(&self) -> usize { - 1 + match 64 - self.leading_zeros() { - x if x <= 8 => 1, - x if x <= 16 => 2, - x if x <= 32 => 4, - _ => 8, - } - } -} -impl WriteValue for f64 { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - w.write_all(&[0x88])?; - w.write_all(&self.to_be_bytes())?; - Ok(()) - } - fn size(&self) -> usize { - 1 + 8 - } -} -impl WriteValue for Vec<u8> { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - write_vint(w, self.len() as u64)?; - w.write_all(self)?; - Ok(()) - } - - fn size(&self) -> usize { - vint_length(self.len() as u64) + self.len() - } -} -impl WriteValue for String { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - let sl = self.as_bytes(); - write_vint(w, sl.len() as u64)?; - w.write_all(sl)?; - Ok(()) - } - - fn size(&self) -> usize { - vint_length(self.len() as u64) + self.len() - } -} -impl WriteValue for EbmlSize { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - match self { - EbmlSize::Exact(s) => write_vint(w, *s as u64)?, - EbmlSize::Unknown => w.write_all(&[0xff])?, - } - Ok(()) - } - fn size(&self) -> usize { - match self { - EbmlSize::Exact(s) => vint_length(*s as u64), - EbmlSize::Unknown => 1, - } - } -} - -impl WriteValue for Master { - fn write_to(&self, w: &mut impl Write) -> Result<()> { - match self { - Master::Start => EbmlSize::Unknown.write_to(w), - Master::End => { - unreachable!() - } - Master::Collected(c) => { - let mut size = 0; - for c in c { - size += c.size_full(); - } - EbmlSize::Exact(size).write_to(w)?; - for c in c { - c.write_full(w)?; - } - Ok(()) - } - } - } - fn size(&self) -> usize { - match self { - Master::Start => EbmlSize::Unknown.size(), - Master::End => unreachable!(), - Master::Collected(c) => { - let mut size = 0; - for c in c { - size += c.size_full(); - } - EbmlSize::Exact(size).size() + size - } - } - } -} - -#[cfg(test)] -mod test { - use crate::{Master, MatroskaTag, WriteValue}; - - #[test] - fn test_int_size() { - let test = |x: i64| { - eprintln!("{x:?}"); - let mut out = Vec::new(); - x.write_to(&mut out).unwrap(); - assert_eq!(out.len(), x.size()) - }; - test(1); - test(2); - test(20); - test(200); - test(2000); - test(20000); - test(200000); - } - - #[test] - fn test_uint_size() { - let test = |x: u64| { - eprintln!("{x:?}"); - let mut out = Vec::new(); - x.write_to(&mut out).unwrap(); - assert_eq!(out.len(), x.size()) - }; - test(1); - test(2); - test(20); - test(200); - test(2000); - test(20000); - test(200000); - } - - #[test] - fn test_string_size() { - let test = |x: &str| { - eprintln!("{x:?}"); - let x = x.to_owned(); - let mut out = Vec::new(); - x.write_to(&mut out).unwrap(); - assert_eq!(out.len(), x.size()) - }; - test(""); - test("x"); - test("wub"); - test("Hello world"); - test("just making sure that"); - test("this is actually working *exactly* how i want it to"); - test("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - } - - #[test] - fn test_vec_size() { - let test = |x: &[u8]| { - eprintln!("{x:?}"); - let x = x.to_owned(); - let mut out = Vec::new(); - x.write_to(&mut out).unwrap(); - assert_eq!(out.len(), x.size()) - }; - test(&[]); - test(&[1]); - test(&[1, 2]); - test(&[23, 4, 4, 23, 4, 234, 232, 4, 234, 23, 1]); - test(&[ - 34, 123, 5, 1, 3, 13, 1, 23, 12, 5, 5, 3, 123, 12, 3, 13, 12, 5, 3, 123, 13, 1, 3, - ]); - } - - #[test] - fn test_master_size() { - let test = |x: Master| { - eprintln!("{x:?}"); - let x = x.to_owned(); - let mut out = Vec::new(); - x.write_to(&mut out).unwrap(); - assert_eq!(out.len(), x.size()) - }; - test(Master::Start); - // test(Master::End); - test(Master::Collected(vec![])); - test(Master::Collected(vec![MatroskaTag::EbmlVersion(1)])); - test(Master::Collected(vec![ - MatroskaTag::EbmlVersion(1), - MatroskaTag::EbmlMaxSizeLength(4), - MatroskaTag::EbmlReadVersion(3), - MatroskaTag::EbmlMaxIdLength(4), - ])); - } -} |