/* 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 */ pub mod extract; pub mod fragment; pub mod remux; pub mod seek_index; pub mod segment_extractor; pub mod trim_writer; pub use fragment::write_fragment_into; pub use remux::remux_stream_into; use jellybase::common::{SourceTrack, SourceTrackKind}; use jellymatroska::{Master, MatroskaTag}; 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, uid: u64, track: &SourceTrack, codec_private: Option>, ) -> MatroskaTag { let mut els = vec![ MatroskaTag::TrackNumber(number), MatroskaTag::TrackUID(uid), MatroskaTag::FlagLacing(track.flag_lacing), MatroskaTag::Language(track.language.clone()), MatroskaTag::CodecID(track.codec.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.kind { SourceTrackKind::Video { width, height, display_height, display_width, display_unit, fps, } => { els.push(MatroskaTag::TrackType(1)); let mut props = vec![ MatroskaTag::PixelWidth(width), MatroskaTag::PixelHeight(height), ]; props.push(MatroskaTag::DisplayWidth(display_width.unwrap_or(width))); props.push(MatroskaTag::DisplayHeight(display_height.unwrap_or(height))); props.push(MatroskaTag::DisplayUnit(display_unit)); if let Some(fps) = fps { props.push(MatroskaTag::FrameRate(fps)) } els.push(MatroskaTag::Video(Master::Collected(props))) } SourceTrackKind::Audio { channels, sample_rate, bit_depth, } => { els.push(MatroskaTag::TrackType(2)); let mut props = vec![ MatroskaTag::SamplingFrequency(sample_rate), MatroskaTag::Channels(channels.try_into().unwrap()), ]; if let Some(bit_depth) = bit_depth { props.push(MatroskaTag::BitDepth(bit_depth.try_into().unwrap())); } els.push(MatroskaTag::Audio(Master::Collected(props))); } SourceTrackKind::Subtitles => { els.push(MatroskaTag::TrackType(17)); } } if let Some(d) = &codec_private { els.push(MatroskaTag::CodecPrivate(d.clone())); } MatroskaTag::TrackEntry(Master::Collected(els)) }