mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-06-10 17:09:22 +00:00
Add some functions to get offset and size of sample
This commit is contained in:
parent
27bdcbb504
commit
5e8d7d6b25
|
@ -20,4 +20,5 @@ license = "MIT"
|
|||
[dependencies]
|
||||
thiserror = "^1.0"
|
||||
byteorder = "1"
|
||||
bytes = "0.5"
|
||||
num-rational = "0.3"
|
|
@ -103,6 +103,13 @@ impl fmt::Debug for BoxType {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BoxType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let fourcc: FourCC = From::from(self.clone());
|
||||
write!(f, "{}", fourcc)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Clone)]
|
||||
pub struct FourCC {
|
||||
pub value: String
|
||||
|
|
|
@ -49,7 +49,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
|||
moov.mvhd = MvhdBox::read_box(reader, s)?;
|
||||
}
|
||||
BoxType::TrakBox => {
|
||||
let trak = TrakBox::read_box(reader, s)?;
|
||||
let mut trak = TrakBox::read_box(reader, s)?;
|
||||
trak.id = moov.traks.len() as u32 + 1;
|
||||
moov.traks.push(trak);
|
||||
}
|
||||
BoxType::UdtaBox => {
|
||||
|
|
|
@ -17,6 +17,7 @@ pub struct StscEntry {
|
|||
pub first_chunk: u32,
|
||||
pub samples_per_chunk: u32,
|
||||
pub sample_description_index: u32,
|
||||
pub first_sample: u32,
|
||||
}
|
||||
|
||||
impl Mp4Box for StscBox {
|
||||
|
@ -37,15 +38,29 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
|||
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _i in 0..entry_count {
|
||||
for _ in 0..entry_count {
|
||||
let entry = StscEntry {
|
||||
first_chunk: reader.read_u32::<BigEndian>()?,
|
||||
samples_per_chunk: reader.read_u32::<BigEndian>()?,
|
||||
sample_description_index: reader.read_u32::<BigEndian>()?,
|
||||
first_sample: 0,
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
let mut sample_id = 1;
|
||||
for i in 0..entry_count {
|
||||
let (first_chunk, samples_per_chunk) = {
|
||||
let mut entry = entries.get_mut(i as usize).unwrap();
|
||||
entry.first_sample = sample_id;
|
||||
(entry.first_chunk, entry.samples_per_chunk)
|
||||
};
|
||||
if i < entry_count - 1 {
|
||||
let next_entry = entries.get(i as usize + 1).unwrap();
|
||||
sample_id += (next_entry.first_chunk - first_chunk) * samples_per_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
skip_read_to(reader, start + size)?;
|
||||
|
||||
Ok(StscBox {
|
||||
|
|
|
@ -2,11 +2,25 @@ use std::io::{Seek, SeekFrom, Read, Write};
|
|||
|
||||
use crate::*;
|
||||
use crate::atoms::*;
|
||||
use crate::atoms::{tkhd::TkhdBox, edts::EdtsBox, mdia::MdiaBox};
|
||||
use crate::atoms::{
|
||||
tkhd::TkhdBox,
|
||||
edts::EdtsBox,
|
||||
mdia::MdiaBox,
|
||||
stbl::StblBox,
|
||||
// stsd::StsdBox,
|
||||
// stts::SttsBox,
|
||||
// stss::StssBox,
|
||||
stsc::StscBox,
|
||||
stsz::StszBox,
|
||||
// stco::StcoBox,
|
||||
// co64::Co64Box,
|
||||
};
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TrakBox {
|
||||
pub id: u32,
|
||||
|
||||
pub tkhd: Option<TkhdBox>,
|
||||
pub edts: Option<EdtsBox>,
|
||||
pub mdia: Option<MdiaBox>,
|
||||
|
@ -16,6 +30,122 @@ impl TrakBox {
|
|||
pub(crate) fn new() -> TrakBox {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn stbl(&self) -> Result<&StblBox> {
|
||||
if let Some(mdia) = &self.mdia {
|
||||
if let Some(minf) = &mdia.minf {
|
||||
if let Some(stbl) = &minf.stbl {
|
||||
Ok(stbl)
|
||||
} else {
|
||||
Err(Error::BoxInTrakNotFound(self.id, BoxType::StblBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInTrakNotFound(self.id, BoxType::MinfBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInTrakNotFound(self.id, BoxType::MdiaBox))
|
||||
}
|
||||
}
|
||||
|
||||
fn stsc(&self) -> Result<&StscBox> {
|
||||
let stbl = self.stbl()?;
|
||||
|
||||
if let Some(stsc) = &stbl.stsc {
|
||||
Ok(stsc)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.id, BoxType::StscBox))
|
||||
}
|
||||
}
|
||||
|
||||
fn stsz(&self) -> Result<&StszBox> {
|
||||
let stbl = self.stbl()?;
|
||||
|
||||
if let Some(stsz) = &stbl.stsz {
|
||||
Ok(stsz)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.id, BoxType::StszBox))
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_to_stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||
let stsc = self.stsc()?;
|
||||
|
||||
for (i, entry) in stsc.entries.iter().enumerate() {
|
||||
if sample_id < entry.first_sample {
|
||||
assert_eq!(i, 0);
|
||||
return Ok(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(stsc.entries.len(), 0);
|
||||
Ok(stsc.entries.len() - 1)
|
||||
}
|
||||
|
||||
fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
|
||||
let stbl = self.stbl()?;
|
||||
|
||||
if let Some(stco) = &stbl.stco {
|
||||
if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset as u64);
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(self.id, BoxType::StcoBox,
|
||||
chunk_id));
|
||||
}
|
||||
} else {
|
||||
if let Some(co64) = &stbl.co64 {
|
||||
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset);
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(self.id, BoxType::Co64Box,
|
||||
chunk_id));
|
||||
}
|
||||
} else {
|
||||
// XXX BoxType::StcoBox & BoxType::Co64Box
|
||||
Err(Error::BoxInStblNotFound(self.id, BoxType::Co64Box))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sample_size(&self, sample_id: u32) -> Result<u32> {
|
||||
let stsz = self.stsz()?;
|
||||
if stsz.sample_size > 0 {
|
||||
return Ok(stsz.sample_size);
|
||||
}
|
||||
if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
|
||||
Ok(*size)
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(self.id, BoxType::StszBox, sample_id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
|
||||
let stsc_index = self.sample_to_stsc_index(sample_id)?;
|
||||
|
||||
let stsc = self.stsc()?;
|
||||
let stsc_entry = if let Some(entry) = stsc.entries.get(stsc_index) {
|
||||
entry
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(self.id, BoxType::StscBox,
|
||||
stsc_index as u32 + 1));
|
||||
};
|
||||
|
||||
let first_chunk = stsc_entry.first_chunk;
|
||||
let first_sample = stsc_entry.first_sample;
|
||||
let samples_per_chunk = stsc_entry.samples_per_chunk;
|
||||
|
||||
let chunk_id = first_chunk + (sample_id - first_sample) / samples_per_chunk;
|
||||
|
||||
let chunk_offset = self.chunk_offset(chunk_id)?;
|
||||
|
||||
let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
|
||||
|
||||
let mut sample_offset = 0;
|
||||
for i in first_sample_in_chunk..sample_id {
|
||||
sample_offset += self.sample_size(i)?;
|
||||
}
|
||||
|
||||
Ok(chunk_offset + sample_offset as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for TrakBox {
|
||||
|
|
12
src/error.rs
12
src/error.rs
|
@ -1,9 +1,21 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::atoms::BoxType;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("{0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
InvalidData(&'static str),
|
||||
#[error("{0} not found")]
|
||||
BoxNotFound(BoxType),
|
||||
#[error("trak[{0}] not found")]
|
||||
TrakNotFound(u32),
|
||||
#[error("trak[{0}].{1} not found")]
|
||||
BoxInTrakNotFound(u32, BoxType),
|
||||
#[error("trak[{0}].stbl.{1} not found")]
|
||||
BoxInStblNotFound(u32, BoxType),
|
||||
#[error("trak[{0}].stbl.{1}.entry[{2}] not found")]
|
||||
EntryInStblNotFound(u32, BoxType, u32),
|
||||
}
|
||||
|
|
39
src/lib.rs
39
src/lib.rs
|
@ -1,5 +1,6 @@
|
|||
use std::io::{Seek, SeekFrom, Read};
|
||||
use std::convert::TryInto;
|
||||
use bytes::Buf;
|
||||
|
||||
mod atoms;
|
||||
use crate::atoms::*;
|
||||
|
@ -17,6 +18,15 @@ pub enum TrackType {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Sample<B> {
|
||||
pub start_time: u64,
|
||||
pub duration: u32,
|
||||
pub rendering_offset: u32,
|
||||
pub is_sync: bool,
|
||||
pub data: B,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mp4Reader<R> {
|
||||
reader: R,
|
||||
|
@ -76,4 +86,33 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
|||
self.size = current - start;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_sample<B: Buf>(
|
||||
&mut self,
|
||||
track_id: u32,
|
||||
sample_id: u32,
|
||||
) -> Result<Option<Sample<B>>> {
|
||||
if track_id == 0 {
|
||||
return Err(Error::TrakNotFound(track_id));
|
||||
}
|
||||
|
||||
let moov = if let Some(moov) = &self.moov {
|
||||
moov
|
||||
} else {
|
||||
return Err(Error::BoxNotFound(MoovBox::box_type()));
|
||||
};
|
||||
|
||||
let trak = if let Some(trak) = moov.traks.get(track_id as usize - 1) {
|
||||
trak
|
||||
} else {
|
||||
return Err(Error::TrakNotFound(track_id));
|
||||
};
|
||||
|
||||
let _sample_offset = trak.sample_offset(sample_id)?;
|
||||
let _sample_size = trak.sample_size(sample_id)?;
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue