aboutsummaryrefslogtreecommitdiff
path: root/remuxer/src
diff options
context:
space:
mode:
Diffstat (limited to 'remuxer/src')
-rw-r--r--remuxer/src/import/mod.rs25
-rw-r--r--remuxer/src/lib.rs235
2 files changed, 164 insertions, 96 deletions
diff --git a/remuxer/src/import/mod.rs b/remuxer/src/import/mod.rs
index 5147a05..505fbe4 100644
--- a/remuxer/src/import/mod.rs
+++ b/remuxer/src/import/mod.rs
@@ -1,3 +1,5 @@
+use std::path::PathBuf;
+
use anyhow::{anyhow, bail, Result};
use jellycommon::{ItemInfo, SourceTrack, SourceTrackKind};
use jellymatroska::{
@@ -8,7 +10,7 @@ use jellymatroska::{
};
use log::{debug, error, info, trace, warn};
-pub fn import_read(input: &mut EbmlReader, iteminfo: &mut ItemInfo) -> Result<()> {
+pub fn import_read(path: &PathBuf, input: &mut EbmlReader, iteminfo: &mut ItemInfo) -> Result<()> {
// TODO dont traverse the entire file, if the tracks are listed at the end
while let Some(item) = input.next() {
let item = match item {
@@ -35,7 +37,7 @@ pub fn import_read(input: &mut EbmlReader, iteminfo: &mut ItemInfo) -> Result<()
MatroskaTag::Segment(_) => {
info!("segment start");
let mut children = Unflatten::new_with_end(input, item);
- import_read_segment(&mut children, iteminfo)?;
+ import_read_segment(path, &mut children, iteminfo)?;
info!("segment end");
}
_ => debug!("(r) tag ignored: {item:?}"),
@@ -45,7 +47,11 @@ pub fn import_read(input: &mut EbmlReader, iteminfo: &mut ItemInfo) -> Result<()
Ok(())
}
-fn import_read_segment(children: &mut Unflatten, iteminfo: &mut ItemInfo) -> Result<()> {
+fn import_read_segment(
+ path: &PathBuf,
+ children: &mut Unflatten,
+ iteminfo: &mut ItemInfo,
+) -> Result<()> {
let (mut timestamp_scale, mut duration) = (None, None);
while let Some(Ok(Unflat { children, item })) = children.next() {
match item {
@@ -99,7 +105,11 @@ fn import_read_segment(children: &mut Unflatten, iteminfo: &mut ItemInfo) -> Res
match item {
MatroskaTag::CuePoint(_) => {
let mut children = children.unwrap();
- while let Some(Ok(Unflat { children: _, item: _ })) = children.next() {
+ while let Some(Ok(Unflat {
+ children: _,
+ item: _,
+ })) = children.next()
+ {
// error!("{item:?}")
}
}
@@ -170,7 +180,7 @@ fn import_read_segment(children: &mut Unflatten, iteminfo: &mut ItemInfo) -> Res
_ => (),
}
}
- // let itrack_index = iteminfo.tracks.len();
+ let itrack_index = iteminfo.tracks.len();
let mtrack_index = index.unwrap();
let kind = match kind.ok_or(anyhow!("track type required"))? {
1 => SourceTrackKind::Video {
@@ -186,10 +196,11 @@ fn import_read_segment(children: &mut Unflatten, iteminfo: &mut ItemInfo) -> Res
17 => SourceTrackKind::Subtitles,
_ => bail!("invalid track type"),
};
- // track_mapping.insert(mtrack_index, itrack_index);
iteminfo.tracks.insert(
- mtrack_index,
+ itrack_index,
SourceTrack {
+ track_number: mtrack_index,
+ path: path.to_owned(),
default_duration,
codec_private,
name: name.unwrap_or_else(|| "unnamed".to_string()),
diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs
index 5536a12..fabde10 100644
--- a/remuxer/src/lib.rs
+++ b/remuxer/src/lib.rs
@@ -1,7 +1,7 @@
pub mod format;
pub mod import;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use jellycommon::{ItemInfo, SourceTrack, SourceTrackKind};
use jellymatroska::{
block::Block,
@@ -11,7 +11,7 @@ use jellymatroska::{
Master, MatroskaTag,
};
use log::{debug, info, trace, warn};
-use std::{collections::BTreeMap, fs::File, io::Write, path::PathBuf, sync::Arc};
+use std::{collections::VecDeque, fs::File, io::Write, path::PathBuf, sync::Arc};
pub struct RemuxerContext {}
@@ -26,24 +26,44 @@ impl RemuxerContext {
_offset: usize,
path_base: PathBuf,
iteminfo: ItemInfo,
- selection: Vec<u64>,
+ selection: Vec<usize>,
webm: bool,
) -> anyhow::Result<()> {
- let source_path = path_base.join(&iteminfo.path);
- info!("remuxing {source_path:?} to have tracks {selection:?}");
-
- let input = File::open(source_path)?;
- let mut input = EbmlReader::new(input);
+ info!("remuxing {:?} to have tracks {selection:?}", iteminfo.title);
let mut output = EbmlWriter::new(writer, 0);
- // maps original to remuxed
- let mapping = BTreeMap::from_iter(
- selection
- .iter()
- .enumerate()
- .map(|(i, e)| (*e, i as u64 + 1)),
- );
- info!("track mapping: {mapping:?}");
+ struct ReaderC {
+ info: SourceTrack,
+ reader: EbmlReader,
+ mapped: u64,
+ }
+
+ let mut inputs = selection
+ .iter()
+ .enumerate()
+ .map(|(index, sel)| {
+ let info = iteminfo
+ .tracks
+ .get(sel)
+ .ok_or(anyhow!("track not available"))?
+ .to_owned();
+ let source_path = path_base.join(&info.path);
+ let mapped = index as u64 + 1;
+ info!(
+ "\t- {sel} {source_path:?} ({} => {mapped})",
+ info.track_number
+ );
+ info!("\t {}", info);
+ let file = File::open(source_path)?;
+ let reader = EbmlReader::new(file);
+ Ok(ReaderC {
+ reader,
+ info,
+ mapped,
+ })
+ })
+ .into_iter()
+ .collect::<anyhow::Result<Vec<_>>>()?;
output.write_tag(&MatroskaTag::Ebml(Master::Collected(vec![
MatroskaTag::EbmlVersion(1),
@@ -71,101 +91,152 @@ impl RemuxerContext {
output.write_tag(&MatroskaTag::Tags(Master::Collected(vec![])))?;
// output.write_tag(&MatroskaTag::Cues(Master::Collected(vec![])))?;
- let tracks_header = MatroskaTag::Tracks(Master::Collected(
- mapping
- .iter()
- .map(|(norig, nrem)| track_to_ebml(*nrem, &iteminfo.tracks.get(norig).unwrap()))
- .collect(),
- ));
- info!("track header: {tracks_header:?}");
- output.write_tag(&tracks_header)?;
+ let tracks_header = inputs
+ .iter()
+ .map(|rc| track_to_ebml(rc.mapped, &rc.info))
+ .collect();
+ output.write_tag(&MatroskaTag::Tracks(Master::Collected(tracks_header)))?;
+
+ struct ReaderD<'a> {
+ info: SourceTrack,
+ peek: Option<AbsoluteBlock>,
+ stream: SegmentExtractIter<'a>,
+ mapped: u64,
+ }
- while let Some(item) = input.next() {
- let item = match item {
- Ok(item) => item,
- Err(e) => {
- warn!("{e}");
+ // read until start of the segment
+ let mut ks = vec![];
+ for i in &mut inputs {
+ loop {
+ let t = i.reader.next().ok_or(anyhow!("early eof"))??;
+ if let MatroskaTag::Segment(Master::Start) = t {
break;
}
- };
- match item {
- MatroskaTag::Ebml(_) => {
- Unflatten::new_with_end(&mut input, item);
- }
- MatroskaTag::Segment(_) => {
- info!("segment start");
- let mut children = Unflatten::new_with_end(&mut input, item);
- filter_segment(&mut children, &mut output, &iteminfo, &mapping)?;
- info!("segment end");
- }
- _ => debug!("(r) tag ignored: {item:?}"),
}
+ let mut stream = SegmentExtractIter {
+ segment: Unflatten::new_with_end(
+ &mut i.reader,
+ MatroskaTag::Segment(Master::Start),
+ ),
+ extract: i.info.track_number,
+ emission_queue: VecDeque::new(),
+ };
+ ks.push(ReaderD {
+ mapped: i.mapped,
+ peek: Some(stream.next()?),
+ stream,
+ info: i.info.clone(),
+ });
}
- output.write_tag(&MatroskaTag::Segment(Master::End))?;
+ loop {
+ let next_index = ks
+ .iter()
+ .enumerate()
+ .fold((0, u64::MAX), |(bi, bpts), (i, r)| {
+ if let Some(peek) = &r.peek {
+ let pts = peek.pts();
+ if pts < bpts {
+ return (i, pts);
+ }
+ }
+ (bi, bpts)
+ })
+ .0;
+
+ let kn = &mut ks[next_index];
+ let mut next = kn.peek.replace(kn.stream.next()?).unwrap(); //.ok_or(anyhow!("eof?"));
- Ok(())
+ let pts = next.pts();
+ next.block.track = kn.mapped;
+ next.block.timestamp_off = 0;
+ let buf = next.block.dump();
+ output.write_tag(&MatroskaTag::Cluster(Master::Collected(vec![
+ MatroskaTag::Timestamp(pts),
+ MatroskaTag::SimpleBlock(buf),
+ ])))?;
+ }
+
+ // output.write_tag(&MatroskaTag::Segment(Master::End))?;
+ // Ok(())
}
}
-fn filter_segment(
- children: &mut Unflatten,
- writer: &mut EbmlWriter,
- _iteminfo: &ItemInfo,
- mapping: &BTreeMap<u64, u64>,
-) -> Result<()> {
- while let Some(Ok(Unflat { children, item })) = children.next() {
+struct AbsoluteBlock {
+ pts_base: u64,
+ block: Block,
+}
+
+struct SegmentExtractIter<'a> {
+ segment: Unflatten<'a>,
+ extract: u64,
+ emission_queue: VecDeque<AbsoluteBlock>,
+}
+
+impl AbsoluteBlock {
+ pub fn pts(&self) -> u64 {
+ self.block.timestamp_off as u64 + self.pts_base
+ }
+}
+
+impl SegmentExtractIter<'_> {
+ pub fn next(&mut self) -> Result<AbsoluteBlock> {
+ loop {
+ if let Some(b) = self.emission_queue.pop_front() {
+ break Ok(b);
+ }
+ self.read()?;
+ }
+ }
+
+ pub fn read(&mut self) -> Result<()> {
+ let Unflat { children, item } = self.segment.next().ok_or(anyhow!("eof"))??;
+ let mut pts_base = 0;
match item {
MatroskaTag::SeekHead(_) => {}
MatroskaTag::Info(_) => {}
MatroskaTag::Cluster(_) => {
- let mut cluster = vec![];
let mut children = children.unwrap();
while let Some(Ok(Unflat { children, item })) = children.next() {
match item {
MatroskaTag::Crc32(_) => (),
MatroskaTag::Timestamp(ts) => {
info!("ts={ts}");
- cluster.push(MatroskaTag::Timestamp(ts));
+ pts_base = ts;
}
MatroskaTag::BlockGroup(_) => {
debug!("group");
let mut children = children.unwrap();
- let mut group = vec![];
+
+ let mut duration = None;
+ let mut block = None;
+
while let Some(Ok(Unflat { children: _, item })) = children.next() {
match item {
- MatroskaTag::Block(buf) => {
- let mut block = Block::parse(&buf)?;
- if let Some(outnum) = mapping.get(&block.track) {
- block.track = *outnum;
- trace!(
- "block: track={} tso={}",
- block.track,
- block.timestamp_off
- );
- group.push(MatroskaTag::Block(block.dump()));
- }
- }
- MatroskaTag::BlockDuration(v) => {
- group.push(MatroskaTag::BlockDuration(v));
- }
+ MatroskaTag::Block(buf) => block = Some(buf),
+ MatroskaTag::BlockDuration(v) => duration = Some(v),
_ => debug!("ignored {item:?}"),
}
}
- cluster.push(MatroskaTag::BlockGroup(Master::Collected(group)));
+ // TODO duration
+ let block = Block::parse(&block.unwrap())?;
+ if block.track == self.extract {
+ trace!("block: track={} tso={}", block.track, block.timestamp_off);
+ self.emission_queue
+ .push_back(AbsoluteBlock { pts_base, block });
+ }
}
MatroskaTag::SimpleBlock(buf) => {
- let mut block = Block::parse(&buf)?;
- if let Some(outnum) = mapping.get(&block.track) {
- block.track = *outnum;
+ let block = Block::parse(&buf)?;
+ if block.track == self.extract {
trace!("block: track={} tso={}", block.track, block.timestamp_off);
- cluster.push(MatroskaTag::SimpleBlock(block.dump()));
+ self.emission_queue
+ .push_back(AbsoluteBlock { pts_base, block });
}
}
_ => warn!("(rsc) tag ignored: {item:?}"),
}
}
- writer.write_tag(&MatroskaTag::Cluster(Master::Collected(cluster)))?;
}
MatroskaTag::Tags(_) => {}
MatroskaTag::Cues(_) => {}
@@ -173,8 +244,8 @@ fn filter_segment(
MatroskaTag::Tracks(_) => {}
_ => debug!("(rs) tag ignored: {item:?}"),
}
+ Ok(())
}
- Ok(())
}
pub fn track_to_ebml(number: u64, track: &SourceTrack) -> MatroskaTag {
@@ -194,7 +265,6 @@ pub fn track_to_ebml(number: u64, track: &SourceTrack) -> MatroskaTag {
fps: _,
} => {
els.push(MatroskaTag::TrackType(1));
- els.push(MatroskaTag::DefaultDuration(41708333)); // TODO pls not hardcoded
els.push(MatroskaTag::Video(Master::Collected(vec![
MatroskaTag::PixelWidth(width),
MatroskaTag::PixelHeight(height),
@@ -221,16 +291,3 @@ pub fn track_to_ebml(number: u64, track: &SourceTrack) -> MatroskaTag {
}
MatroskaTag::TrackEntry(Master::Collected(els))
}
-
-// pub struct SendWriter(pub Sender<Vec<u8>>);
-
-// impl Write for SendWriter {
-// fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
-// self.0.blocking_send(buf.to_owned()).unwrap();
-// Ok(buf.len())
-// }
-
-// fn flush(&mut self) -> std::io::Result<()> {
-// Ok(()) // TODO should we actually do something here?
-// }
-// }