mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-11-14 11:11:16 +00:00
Maintain a separate indexing from track_id to index to allow for non continues track ids (#43) (#58)
This commit is contained in:
parent
94f9e88b2d
commit
8fe009e248
8 changed files with 59 additions and 68 deletions
|
@ -49,45 +49,42 @@ fn copy<P: AsRef<Path>>(src_filename: &P, dst_filename: &P) -> Result<()> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO interleaving
|
// TODO interleaving
|
||||||
for track_idx in 0..mp4_reader.tracks().len() {
|
for track in mp4_reader.tracks().values() {
|
||||||
if let Some(ref track) = mp4_reader.tracks().get(track_idx) {
|
let media_conf = match track.media_type()? {
|
||||||
let media_conf = match track.media_type()? {
|
MediaType::H264 => MediaConfig::AvcConfig(AvcConfig {
|
||||||
MediaType::H264 => MediaConfig::AvcConfig(AvcConfig {
|
width: track.width(),
|
||||||
width: track.width(),
|
height: track.height(),
|
||||||
height: track.height(),
|
seq_param_set: track.sequence_parameter_set()?.to_vec(),
|
||||||
seq_param_set: track.sequence_parameter_set()?.to_vec(),
|
pic_param_set: track.picture_parameter_set()?.to_vec(),
|
||||||
pic_param_set: track.picture_parameter_set()?.to_vec(),
|
}),
|
||||||
}),
|
MediaType::H265 => MediaConfig::HevcConfig(HevcConfig {
|
||||||
MediaType::H265 => MediaConfig::HevcConfig(HevcConfig {
|
width: track.width(),
|
||||||
width: track.width(),
|
height: track.height(),
|
||||||
height: track.height(),
|
}),
|
||||||
}),
|
MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config {
|
||||||
MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config {
|
width: track.width(),
|
||||||
width: track.width(),
|
height: track.height(),
|
||||||
height: track.height(),
|
}),
|
||||||
}),
|
MediaType::AAC => MediaConfig::AacConfig(AacConfig {
|
||||||
MediaType::AAC => MediaConfig::AacConfig(AacConfig {
|
bitrate: track.bitrate(),
|
||||||
bitrate: track.bitrate(),
|
profile: track.audio_profile()?,
|
||||||
profile: track.audio_profile()?,
|
freq_index: track.sample_freq_index()?,
|
||||||
freq_index: track.sample_freq_index()?,
|
chan_conf: track.channel_config()?,
|
||||||
chan_conf: track.channel_config()?,
|
}),
|
||||||
}),
|
MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}),
|
||||||
MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let track_conf = TrackConfig {
|
let track_conf = TrackConfig {
|
||||||
track_type: track.track_type()?,
|
track_type: track.track_type()?,
|
||||||
timescale: track.timescale(),
|
timescale: track.timescale(),
|
||||||
language: track.language().to_string(),
|
language: track.language().to_string(),
|
||||||
media_conf,
|
media_conf,
|
||||||
};
|
};
|
||||||
|
|
||||||
mp4_writer.add_track(&track_conf)?;
|
mp4_writer.add_track(&track_conf)?;
|
||||||
} else {
|
}
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
let track_id = track_idx as u32 + 1;
|
for track_id in mp4_reader.tracks().keys().copied().collect::<Vec<u32>>() {
|
||||||
let sample_count = mp4_reader.sample_count(track_id)?;
|
let sample_count = mp4_reader.sample_count(track_id)?;
|
||||||
for sample_idx in 0..sample_count {
|
for sample_idx in 0..sample_count {
|
||||||
let sample_id = sample_idx + 1;
|
let sample_id = sample_idx + 1;
|
||||||
|
|
|
@ -61,7 +61,7 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// trak.
|
// trak.
|
||||||
for track in mp4.tracks().iter() {
|
for track in mp4.tracks().values() {
|
||||||
boxes.push(build_box(&track.trak));
|
boxes.push(build_box(&track.trak));
|
||||||
boxes.push(build_box(&track.trak.tkhd));
|
boxes.push(build_box(&track.trak.tkhd));
|
||||||
if let Some(ref edts) = track.trak.edts {
|
if let Some(ref edts) = track.trak.edts {
|
||||||
|
|
|
@ -44,7 +44,7 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
|
||||||
println!(" timescale: {:?}\n", mp4.timescale());
|
println!(" timescale: {:?}\n", mp4.timescale());
|
||||||
|
|
||||||
println!("Found {} Tracks", mp4.tracks().len());
|
println!("Found {} Tracks", mp4.tracks().len());
|
||||||
for track in mp4.tracks().iter() {
|
for track in mp4.tracks().values() {
|
||||||
let media_info = match track.track_type()? {
|
let media_info = match track.track_type()? {
|
||||||
TrackType::Video => video_info(track)?,
|
TrackType::Video => video_info(track)?,
|
||||||
TrackType::Audio => audio_info(track)?,
|
TrackType::Audio => audio_info(track)?,
|
||||||
|
|
|
@ -26,8 +26,7 @@ fn samples<P: AsRef<Path>>(filename: &P) -> Result<()> {
|
||||||
|
|
||||||
let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
|
let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
|
||||||
|
|
||||||
for track_idx in 0..mp4.tracks().len() {
|
for track_id in mp4.tracks().keys().copied().collect::<Vec<u32>>() {
|
||||||
let track_id = track_idx as u32 + 1;
|
|
||||||
let sample_count = mp4.sample_count(track_id).unwrap();
|
let sample_count = mp4.sample_count(track_id).unwrap();
|
||||||
|
|
||||||
for sample_idx in 0..sample_count {
|
for sample_idx in 0..sample_count {
|
||||||
|
|
|
@ -16,7 +16,7 @@ fn main() {
|
||||||
|
|
||||||
println!("Major Brand: {}", mp4.major_brand());
|
println!("Major Brand: {}", mp4.major_brand());
|
||||||
|
|
||||||
for track in mp4.tracks().iter() {
|
for track in mp4.tracks().values() {
|
||||||
println!("Track: #{}({}) {} {}",
|
println!("Track: #{}({}) {} {}",
|
||||||
track.track_id(),
|
track.track_id(),
|
||||||
track.language(),
|
track.language(),
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
//! println!("duration: {:?}", mp4.duration());
|
//! println!("duration: {:?}", mp4.duration());
|
||||||
//!
|
//!
|
||||||
//! // Track info.
|
//! // Track info.
|
||||||
//! for track in mp4.tracks().iter() {
|
//! for track in mp4.tracks().values() {
|
||||||
//! println!(
|
//! println!(
|
||||||
//! "track: #{}({}) {} : {}",
|
//! "track: #{}({}) {} : {}",
|
||||||
//! track.track_id(),
|
//! track.track_id(),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::mp4box::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mp4Reader<R> {
|
pub struct Mp4Reader<R> {
|
||||||
|
@ -11,7 +12,7 @@ pub struct Mp4Reader<R> {
|
||||||
pub moov: MoovBox,
|
pub moov: MoovBox,
|
||||||
pub moofs: Vec<MoofBox>,
|
pub moofs: Vec<MoofBox>,
|
||||||
|
|
||||||
tracks: Vec<Mp4Track>,
|
tracks: HashMap<u32, Mp4Track>,
|
||||||
size: u64,
|
size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,16 +65,14 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
|
|
||||||
let size = current - start;
|
let size = current - start;
|
||||||
let mut tracks = if let Some(ref moov) = moov {
|
let mut tracks = if let Some(ref moov) = moov {
|
||||||
let mut tracks = Vec::with_capacity(moov.traks.len());
|
if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) {
|
||||||
for (i, trak) in moov.traks.iter().enumerate() {
|
return Err(Error::InvalidData("illegal track id 0"));
|
||||||
if trak.tkhd.track_id != i as u32 + 1 {
|
|
||||||
return Err(Error::InvalidData("tracks out of order"));
|
|
||||||
}
|
|
||||||
tracks.push(Mp4Track::from(trak));
|
|
||||||
}
|
}
|
||||||
tracks
|
moov.traks.iter()
|
||||||
|
.map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak)))
|
||||||
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
HashMap::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update tracks if any fragmented (moof) boxes are found.
|
// Update tracks if any fragmented (moof) boxes are found.
|
||||||
|
@ -87,9 +86,13 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
|
|
||||||
for moof in moofs.iter() {
|
for moof in moofs.iter() {
|
||||||
for traf in moof.trafs.iter() {
|
for traf in moof.trafs.iter() {
|
||||||
let track_id = traf.tfhd.track_id as usize - 1;
|
let track_id = traf.tfhd.track_id;
|
||||||
tracks[track_id].default_sample_duration = default_sample_duration;
|
if let Some(track) = tracks.get_mut(&track_id) {
|
||||||
tracks[track_id].trafs.push(traf.clone());
|
track.default_sample_duration = default_sample_duration;
|
||||||
|
track.trafs.push(traf.clone())
|
||||||
|
} else {
|
||||||
|
return Err(Error::TrakNotFound(track_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,16 +135,12 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
self.moofs.len() != 0
|
self.moofs.len() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tracks(&self) -> &[Mp4Track] {
|
pub fn tracks(&self) -> &HashMap<u32, Mp4Track> {
|
||||||
&self.tracks
|
&self.tracks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_count(&self, track_id: u32) -> Result<u32> {
|
pub fn sample_count(&self, track_id: u32) -> Result<u32> {
|
||||||
if track_id == 0 {
|
if let Some(track) = self.tracks.get(&track_id) {
|
||||||
return Err(Error::TrakNotFound(track_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(track) = self.tracks.get(track_id as usize - 1) {
|
|
||||||
Ok(track.sample_count())
|
Ok(track.sample_count())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::TrakNotFound(track_id))
|
Err(Error::TrakNotFound(track_id))
|
||||||
|
@ -149,11 +148,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
|
pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
|
||||||
if track_id == 0 {
|
if let Some(track) = self.tracks.get(&track_id) {
|
||||||
return Err(Error::TrakNotFound(track_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref track) = self.tracks.get(track_id as usize - 1) {
|
|
||||||
track.read_sample(&mut self.reader, sample_id)
|
track.read_sample(&mut self.reader, sample_id)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::TrakNotFound(track_id))
|
Err(Error::TrakNotFound(track_id))
|
||||||
|
|
|
@ -94,7 +94,7 @@ fn test_read_mp4() {
|
||||||
assert!(eos.is_none());
|
assert!(eos.is_none());
|
||||||
|
|
||||||
// track #1
|
// track #1
|
||||||
let track1 = mp4.tracks().get(0).unwrap();
|
let track1 = mp4.tracks().get(&1).unwrap();
|
||||||
assert_eq!(track1.track_id(), 1);
|
assert_eq!(track1.track_id(), 1);
|
||||||
assert_eq!(track1.track_type().unwrap(), TrackType::Video);
|
assert_eq!(track1.track_type().unwrap(), TrackType::Video);
|
||||||
assert_eq!(track1.media_type().unwrap(), MediaType::H264);
|
assert_eq!(track1.media_type().unwrap(), MediaType::H264);
|
||||||
|
@ -105,7 +105,7 @@ fn test_read_mp4() {
|
||||||
assert_eq!(track1.frame_rate(), 25.00); // XXX
|
assert_eq!(track1.frame_rate(), 25.00); // XXX
|
||||||
|
|
||||||
// track #2
|
// track #2
|
||||||
let track2 = mp4.tracks().get(1).unwrap();
|
let track2 = mp4.tracks().get(&2).unwrap();
|
||||||
assert_eq!(track2.track_type().unwrap(), TrackType::Audio);
|
assert_eq!(track2.track_type().unwrap(), TrackType::Audio);
|
||||||
assert_eq!(track2.media_type().unwrap(), MediaType::AAC);
|
assert_eq!(track2.media_type().unwrap(), MediaType::AAC);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
Loading…
Reference in a new issue