use mp4_track::{ChunkOffsetChoice, Mp4Reader, Mp4Writer, SampleInput, TrackConfig, WriterConfig};
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args().skip(1);
let input = args.next().ok_or("usage: remux INPUT OUTPUT [co64]")?;
let output = args.next().ok_or("usage: remux INPUT OUTPUT [co64]")?;
let force_co64 = args.next().as_deref() == Some("co64");
let mut reader = Mp4Reader::open(File::open(input)?)?;
let config = WriterConfig {
chunk_offsets: if force_co64 {
ChunkOffsetChoice::Co64
} else {
ChunkOffsetChoice::Auto
},
..WriterConfig::default()
};
let mut writer = Mp4Writer::new(File::create(output)?, config)?;
let track_configs: Vec<_> = reader
.tracks()
.iter()
.map(|track| TrackConfig {
kind: track.kind,
timescale: track.timescale,
language: track.language.clone(),
handler_name: track.handler_name.clone(),
codec: track.codec.clone(),
width: track.width,
height: track.height,
edit_list: track.edit_list.clone(),
})
.collect();
let mut output_ids = Vec::new();
for track in track_configs {
output_ids.push(writer.add_track(track)?);
}
let mut order = Vec::new();
for track in reader.tracks() {
for sample in reader.samples(track.id)? {
order.push((track.id, sample.index, sample.dts, track.timescale));
}
}
order.sort_by(|left, right| {
let a = u128::from(left.2).saturating_mul(u128::from(right.3));
let b = u128::from(right.2).saturating_mul(u128::from(left.3));
a.cmp(&b)
.then_with(|| left.0.cmp(&right.0))
.then_with(|| left.1.cmp(&right.1))
});
for (track_id, index, _, _) in order {
let sample = reader.read_sample(track_id, index)?;
let output_id = *output_ids
.get(usize::try_from(track_id.0.saturating_sub(1))?)
.ok_or("track id is not contiguous")?;
writer.write_sample(
output_id,
&SampleInput {
data: &sample.data,
duration: sample.info.duration,
cts_offset: i32::try_from(sample.info.cts_offset)?,
is_sync: sample.info.is_sync,
},
)?;
}
writer.finish()?;
Ok(())
}