diff --git a/examples/mp4copy.rs b/examples/mp4copy.rs index 449e782..3c4d4cf 100644 --- a/examples/mp4copy.rs +++ b/examples/mp4copy.rs @@ -49,45 +49,42 @@ fn copy>(src_filename: &P, dst_filename: &P) -> Result<()> { )?; // TODO interleaving - for track_idx in 0..mp4_reader.tracks().len() { - if let Some(ref track) = mp4_reader.tracks().get(track_idx) { - let media_conf = match track.media_type()? { - MediaType::H264 => MediaConfig::AvcConfig(AvcConfig { - width: track.width(), - height: track.height(), - seq_param_set: track.sequence_parameter_set()?.to_vec(), - pic_param_set: track.picture_parameter_set()?.to_vec(), - }), - MediaType::H265 => MediaConfig::HevcConfig(HevcConfig { - width: track.width(), - height: track.height(), - }), - MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config { - width: track.width(), - height: track.height(), - }), - MediaType::AAC => MediaConfig::AacConfig(AacConfig { - bitrate: track.bitrate(), - profile: track.audio_profile()?, - freq_index: track.sample_freq_index()?, - chan_conf: track.channel_config()?, - }), - MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}), - }; + for track in mp4_reader.tracks().values() { + let media_conf = match track.media_type()? { + MediaType::H264 => MediaConfig::AvcConfig(AvcConfig { + width: track.width(), + height: track.height(), + seq_param_set: track.sequence_parameter_set()?.to_vec(), + pic_param_set: track.picture_parameter_set()?.to_vec(), + }), + MediaType::H265 => MediaConfig::HevcConfig(HevcConfig { + width: track.width(), + height: track.height(), + }), + MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config { + width: track.width(), + height: track.height(), + }), + MediaType::AAC => MediaConfig::AacConfig(AacConfig { + bitrate: track.bitrate(), + profile: track.audio_profile()?, + freq_index: track.sample_freq_index()?, + chan_conf: track.channel_config()?, + }), + MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}), + }; - let track_conf = TrackConfig { - track_type: track.track_type()?, - timescale: track.timescale(), - language: track.language().to_string(), - media_conf, - }; + let track_conf = TrackConfig { + track_type: track.track_type()?, + timescale: track.timescale(), + language: track.language().to_string(), + media_conf, + }; - mp4_writer.add_track(&track_conf)?; - } else { - unreachable!() - } + mp4_writer.add_track(&track_conf)?; + } - let track_id = track_idx as u32 + 1; + for track_id in mp4_reader.tracks().keys().copied().collect::>() { let sample_count = mp4_reader.sample_count(track_id)?; for sample_idx in 0..sample_count { let sample_id = sample_idx + 1; diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs index 2882e32..b24ddc6 100644 --- a/examples/mp4dump.rs +++ b/examples/mp4dump.rs @@ -61,7 +61,7 @@ fn get_boxes(file: File) -> Result> { } // 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.tkhd)); if let Some(ref edts) = track.trak.edts { diff --git a/examples/mp4info.rs b/examples/mp4info.rs index a263777..ff2a379 100644 --- a/examples/mp4info.rs +++ b/examples/mp4info.rs @@ -44,7 +44,7 @@ fn info>(filename: &P) -> Result<()> { println!(" timescale: {:?}\n", mp4.timescale()); 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()? { TrackType::Video => video_info(track)?, TrackType::Audio => audio_info(track)?, diff --git a/examples/mp4sample.rs b/examples/mp4sample.rs index eb4604c..a34bda7 100644 --- a/examples/mp4sample.rs +++ b/examples/mp4sample.rs @@ -26,8 +26,7 @@ fn samples>(filename: &P) -> Result<()> { let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?; - for track_idx in 0..mp4.tracks().len() { - let track_id = track_idx as u32 + 1; + for track_id in mp4.tracks().keys().copied().collect::>() { let sample_count = mp4.sample_count(track_id).unwrap(); for sample_idx in 0..sample_count { diff --git a/examples/simple.rs b/examples/simple.rs index c676679..e629ea7 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -16,7 +16,7 @@ fn main() { println!("Major Brand: {}", mp4.major_brand()); - for track in mp4.tracks().iter() { + for track in mp4.tracks().values() { println!("Track: #{}({}) {} {}", track.track_id(), track.language(), diff --git a/src/lib.rs b/src/lib.rs index eb5c24d..9966ede 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,7 @@ //! println!("duration: {:?}", mp4.duration()); //! //! // Track info. -//! for track in mp4.tracks().iter() { +//! for track in mp4.tracks().values() { //! println!( //! "track: #{}({}) {} : {}", //! track.track_id(), diff --git a/src/reader.rs b/src/reader.rs index 4c77e17..72aae72 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use std::io::{Read, Seek, SeekFrom}; use std::time::Duration; -use crate::mp4box::*; use crate::*; +use crate::mp4box::*; #[derive(Debug)] pub struct Mp4Reader { @@ -11,7 +12,7 @@ pub struct Mp4Reader { pub moov: MoovBox, pub moofs: Vec, - tracks: Vec, + tracks: HashMap, size: u64, } @@ -64,16 +65,14 @@ impl Mp4Reader { let size = current - start; let mut tracks = if let Some(ref moov) = moov { - let mut tracks = Vec::with_capacity(moov.traks.len()); - for (i, trak) in moov.traks.iter().enumerate() { - if trak.tkhd.track_id != i as u32 + 1 { - return Err(Error::InvalidData("tracks out of order")); - } - tracks.push(Mp4Track::from(trak)); + if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) { + return Err(Error::InvalidData("illegal track id 0")); } - tracks + moov.traks.iter() + .map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak))) + .collect() } else { - Vec::new() + HashMap::new() }; // Update tracks if any fragmented (moof) boxes are found. @@ -87,9 +86,13 @@ impl Mp4Reader { for moof in moofs.iter() { for traf in moof.trafs.iter() { - let track_id = traf.tfhd.track_id as usize - 1; - tracks[track_id].default_sample_duration = default_sample_duration; - tracks[track_id].trafs.push(traf.clone()); + let track_id = traf.tfhd.track_id; + if let Some(track) = tracks.get_mut(&track_id) { + track.default_sample_duration = default_sample_duration; + track.trafs.push(traf.clone()) + } else { + return Err(Error::TrakNotFound(track_id)); + } } } } @@ -132,16 +135,12 @@ impl Mp4Reader { self.moofs.len() != 0 } - pub fn tracks(&self) -> &[Mp4Track] { + pub fn tracks(&self) -> &HashMap { &self.tracks } pub fn sample_count(&self, track_id: u32) -> Result { - if track_id == 0 { - return Err(Error::TrakNotFound(track_id)); - } - - if let Some(track) = self.tracks.get(track_id as usize - 1) { + if let Some(track) = self.tracks.get(&track_id) { Ok(track.sample_count()) } else { Err(Error::TrakNotFound(track_id)) @@ -149,11 +148,7 @@ impl Mp4Reader { } pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result> { - if track_id == 0 { - return Err(Error::TrakNotFound(track_id)); - } - - if let Some(ref track) = self.tracks.get(track_id as usize - 1) { + if let Some(track) = self.tracks.get(&track_id) { track.read_sample(&mut self.reader, sample_id) } else { Err(Error::TrakNotFound(track_id)) diff --git a/tests/lib.rs b/tests/lib.rs index 4208e3e..0fa0b25 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -94,7 +94,7 @@ fn test_read_mp4() { assert!(eos.is_none()); // 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_type().unwrap(), TrackType::Video); assert_eq!(track1.media_type().unwrap(), MediaType::H264); @@ -105,7 +105,7 @@ fn test_read_mp4() { assert_eq!(track1.frame_rate(), 25.00); // XXX // 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.media_type().unwrap(), MediaType::AAC); assert_eq!(