/* 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 */ #![feature(random, exit_status_error)] pub mod extract; pub mod fragment; pub mod metadata; pub mod matroska_to_mpeg4; pub mod remux; pub mod seek_index; pub mod segment_extractor; pub mod trim_writer; pub mod matroska_to_webm; use ebml_struct::matroska::TrackEntry; pub use fragment::write_fragment_into; use jellymatroska::{Master, MatroskaTag}; pub use matroska_to_mpeg4::matroska_to_mpeg4; pub use remux::remux_stream_into; pub fn ebml_header(webm: bool) -> MatroskaTag { MatroskaTag::Ebml(Master::Collected(vec![ MatroskaTag::EbmlVersion(1), MatroskaTag::EbmlReadVersion(1), MatroskaTag::EbmlMaxIdLength(4), MatroskaTag::EbmlMaxSizeLength(8), MatroskaTag::DocType(if webm { "webm".to_string() } else { "matroska".to_string() }), MatroskaTag::DocTypeVersion(4), MatroskaTag::DocTypeReadVersion(2), ])) } pub fn ebml_segment_info(title: String, duration: f64) -> MatroskaTag { MatroskaTag::Info(Master::Collected(vec![ MatroskaTag::TimestampScale(1_000_000), MatroskaTag::Duration(duration * 1000.0), MatroskaTag::Title(title), MatroskaTag::MuxingApp("jellyremux".to_string()), MatroskaTag::WritingApp("jellything".to_string()), ])) } pub fn ebml_track_entry(number: u64, track: &TrackEntry) -> MatroskaTag { let mut els = vec![ MatroskaTag::TrackNumber(number), MatroskaTag::FlagLacing(track.flag_lacing), MatroskaTag::Language(track.language.clone()), MatroskaTag::CodecID(track.codec_id.clone()), MatroskaTag::CodecDelay(track.codec_delay), MatroskaTag::SeekPreRoll(track.seek_pre_roll), ]; if let Some(d) = &track.default_duration { els.push(MatroskaTag::DefaultDuration(*d)); } match track.track_type { 1 => { let video = track.video.as_ref().unwrap(); els.push(MatroskaTag::TrackType(1)); let mut props = vec![ MatroskaTag::PixelWidth(video.pixel_width), MatroskaTag::PixelHeight(video.pixel_height), ]; props.push(MatroskaTag::DisplayWidth( video.display_width.unwrap_or(video.pixel_width), )); props.push(MatroskaTag::DisplayHeight( video.display_height.unwrap_or(video.pixel_height), )); props.push(MatroskaTag::DisplayUnit(video.display_unit)); if let Some(fps) = video.frame_rate { props.push(MatroskaTag::FrameRate(fps)) } els.push(MatroskaTag::Video(Master::Collected(props))) } 2 => { let audio = track.audio.as_ref().unwrap(); els.push(MatroskaTag::TrackType(2)); let mut props = vec![ MatroskaTag::SamplingFrequency(audio.sampling_frequency), MatroskaTag::Channels(audio.channels), ]; if let Some(bit_depth) = audio.bit_depth { props.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap())); } els.push(MatroskaTag::Audio(Master::Collected(props))); } 17 => { els.push(MatroskaTag::TrackType(17)); } _ => unreachable!(), } if let Some(d) = &track.codec_private { els.push(MatroskaTag::CodecPrivate(d.clone())); } MatroskaTag::TrackEntry(Master::Collected(els)) }