diff options
author | metamuffin <metamuffin@disroot.org> | 2025-09-13 16:08:42 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-09-13 16:08:42 +0200 |
commit | 044c7e1c75145f1ec9d002b4f6fc4433ff7f9540 (patch) | |
tree | db326c8f2327396ed443a1822936927e7c847494 /remuxer/src/fragment.rs | |
parent | e99bde7a00a161ff5dd91eaf1ce546a9d98cef05 (diff) | |
download | jellything-044c7e1c75145f1ec9d002b4f6fc4433ff7f9540.tar jellything-044c7e1c75145f1ec9d002b4f6fc4433ff7f9540.tar.bz2 jellything-044c7e1c75145f1ec9d002b4f6fc4433ff7f9540.tar.zst |
start remuxer crate rewrite; added matroska demuxer and format detection
Diffstat (limited to 'remuxer/src/fragment.rs')
-rw-r--r-- | remuxer/src/fragment.rs | 219 |
1 files changed, 0 insertions, 219 deletions
diff --git a/remuxer/src/fragment.rs b/remuxer/src/fragment.rs deleted file mode 100644 index 45a671f..0000000 --- a/remuxer/src/fragment.rs +++ /dev/null @@ -1,219 +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::{ - ebml_header, ebml_segment_info, ebml_track_entry, - metadata::{matroska_metadata, MatroskaMetadata}, - seek_index::get_seek_index, - segment_extractor::SegmentExtractIter, -}; -use anyhow::{anyhow, Context, Result}; -use jellymatroska::{read::EbmlReader, write::EbmlWriter, Master, MatroskaTag}; -use log::{debug, info}; -use std::{ - fs::File, - io::{BufReader, BufWriter, Write}, - ops::Range, - path::Path, -}; - -const FRAGMENT_LENGTH: f64 = 4.; - -pub fn fragment_index(path: &Path, track: u64) -> Result<Vec<Range<f64>>> { - let meta = matroska_metadata(path)?; - let duration = media_duration(&meta); - let force_kf = meta - .as_ref() - .tracks - .as_ref() - .unwrap() - .entries - .iter() - .find(|t| t.track_number == track) - .unwrap() - .track_type - == 17; - - let index = get_seek_index(path)?; - let index = index - .get(&track) - .ok_or(anyhow!("seek index track missing"))?; - - let n_kf = if force_kf { - index.blocks.len() - } else { - index.keyframes.len() - }; - - let average_kf_interval = duration / n_kf as f64; - let kf_per_frag = (FRAGMENT_LENGTH / average_kf_interval).ceil() as usize; - debug!("average keyframe interval: {average_kf_interval}"); - debug!(" => keyframes per frag {kf_per_frag}"); - - let n_frags = n_kf.div_ceil(kf_per_frag); - Ok((0..n_frags) - .map(|i| { - let start = index.blocks[if force_kf { - i * kf_per_frag - } else { - index.keyframes[i * kf_per_frag] - }] - .pts as f64 - / 1000.; - let end = if force_kf { - let n = (i + 1) * kf_per_frag; - if n >= index.blocks.len() { - None - } else { - Some(n) - } - } else { - index.keyframes.get((i + 1) * kf_per_frag).copied() - } - .map(|i| index.blocks[i].pts as f64 / 1000.) - .unwrap_or(duration); - start..end - }) - .collect()) -} - -pub fn write_fragment_into( - writer: impl Write, - path: &Path, - track: u64, - webm: bool, - title: &str, - n: usize, -) -> anyhow::Result<()> { - let meta = matroska_metadata(path)?; - let duration = media_duration(&meta); - let track_meta = meta - .as_ref() - .tracks - .as_ref() - .unwrap() - .entries - .iter() - .find(|t| t.track_number == track) - .unwrap(); - let force_kf = track_meta.track_type == 17; - - info!("writing fragment {n} of {:?} (track {track})", title); - let mut output = EbmlWriter::new(BufWriter::new(writer), 0); - let mapped = 1; - info!("\t- {track} {path:?} ({} => {mapped})", track); - // info!("\t {}", info); - let file = File::open(path).context("opening source file")?; - let index = get_seek_index(path)?; - let index = index - .get(&track) - .ok_or(anyhow!("track missing 2"))? - .to_owned(); - debug!("\t seek index: {} blocks loaded", index.blocks.len()); - let mut reader = EbmlReader::new(BufReader::new(file)); - - let n_kf = if force_kf { - index.blocks.len() - } else { - index.keyframes.len() - }; - debug!("{duration} {n_kf}"); - let average_kf_interval = duration / n_kf as f64; - let kf_per_frag = (FRAGMENT_LENGTH / average_kf_interval).ceil() as usize; - debug!("average keyframe interval: {average_kf_interval}"); - debug!(" => keyframes per frag {kf_per_frag}"); - - let (start_block_index, end_block_index) = if force_kf { - (n * kf_per_frag, (n + 1) * kf_per_frag) - } else { - ( - *index - .keyframes - .get(n * kf_per_frag) - .ok_or(anyhow!("fragment index out of range"))?, - *index - .keyframes - .get((n + 1) * kf_per_frag) - .unwrap_or(&index.blocks.len()), - ) - }; - debug!("writing blocks {start_block_index} to {end_block_index}."); - - let start_block = &index.blocks[start_block_index]; - let last_block_pts = index - .blocks - .get(end_block_index) - .map(|b| b.pts) - .unwrap_or((duration * 1000.) as u64); - - output.write_tag(&ebml_header(webm))?; - output.write_tag(&MatroskaTag::Segment(Master::Start))?; - output.write_tag(&ebml_segment_info( - title.to_string(), - (last_block_pts - start_block.pts) as f64 / 1000., - ))?; - output.write_tag(&MatroskaTag::Tracks(Master::Collected(vec![ - ebml_track_entry(mapped, track_meta), - ])))?; - - reader.seek(start_block.source_off, MatroskaTag::Cluster(Master::Start))?; - let mut reader = SegmentExtractIter::new(&mut reader, track); - - { - // TODO this one caused fragments to get dropped by MSE for no reason - // for i in start_block_index..end_block_index { - // let index_block = &index.blocks[i]; - // let (mut block, duration) = reader.next()?; - - // assert_eq!(index_block.size, block.data.len(), "seek index is wrong"); - - // block.track = 1; - // block.timestamp_off = 0; - // output.write_tag(&MatroskaTag::Cluster(Master::Collected(vec![ - // MatroskaTag::Timestamp(index_block.pts - start_block.pts), - // if let Some(duration) = duration { - // MatroskaTag::BlockGroup(Master::Collected(vec![ - // MatroskaTag::BlockDuration(duration), - // MatroskaTag::Block(block), - // ])) - // } else { - // MatroskaTag::SimpleBlock(block) - // }, - // ])))?; - // } - } - { - let mut blocks = vec![MatroskaTag::Timestamp(start_block.pts)]; - for i in start_block_index..end_block_index { - let index_block = &index.blocks[i]; - let (mut block, duration) = reader.next_block()?; - - assert_eq!(index_block.size, block.data.len(), "seek index is wrong"); - - block.track = 1; - // TODO this does generate overflows sometimes - block.timestamp_off = (index_block.pts as i64 - start_block.pts as i64) - .try_into() - .unwrap(); - if let Some(duration) = duration { - blocks.push(MatroskaTag::BlockGroup(Master::Collected(vec![ - MatroskaTag::BlockDuration(duration), - MatroskaTag::Block(block), - ]))) - } else { - blocks.push(MatroskaTag::SimpleBlock(block)) - } - } - output.write_tag(&MatroskaTag::Cluster(Master::Collected(blocks)))?; - } - debug!("wrote {} bytes", output.position()); - Ok(()) -} - -fn media_duration(m: &MatroskaMetadata) -> f64 { - let info = m.info.as_ref().unwrap(); - (info.duration.unwrap_or_default() * info.timestamp_scale as f64) / 1_000_000_000. -} |