1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-02 13:39:54 +00:00

Refactor common types

This commit is contained in:
Ian Jun 2020-08-04 11:01:52 +09:00
parent 18bc289cab
commit c83a81f174
7 changed files with 426 additions and 282 deletions

View file

@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::{Result, Mp4Track, TrackType};
use mp4::{Mp4Track, Result, TrackType};
fn main() {
let args: Vec<String> = env::args().collect();
@ -42,21 +42,32 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
);
for track in mp4.tracks().iter() {
let media_info = match track.track_type() {
let media_info = match track.track_type()? {
TrackType::Video => video_info(track),
TrackType::Audio => audio_info(track),
_ => String::from("error")
};
println!(" Track: #{}({}) {}: {} ({:?}), {}", track.track_id(), track.language(),
track.track_type(), track.media_type(), track.box_type(), media_info);
println!(
" Track: #{}({}) {}: {} ({:?}), {}",
track.track_id(),
track.language(),
track.track_type()?,
track.media_type()?,
track.box_type(),
media_info
);
}
Ok(())
}
fn video_info(track: &Mp4Track) -> String {
format!("{}x{}, {} kb/s, {:.2} fps", track.width(), track.height(),
track.bitrate() / 1000, track.frame_rate())
format!(
"{}x{}, {} kb/s, {:.2} fps",
track.width(),
track.height(),
track.bitrate() / 1000,
track.frame_rate()
)
}
fn audio_info(track: &Mp4Track) -> String {
@ -65,5 +76,10 @@ fn audio_info(track: &Mp4Track) -> String {
2 => String::from("stereo"),
n => format!("{}-ch", n),
};
format!("{} Hz, {}, {} kb/s", track.sample_rate(), ch, track.bitrate() / 1000)
format!(
"{} Hz, {}, {} kb/s",
track.sample_rate(),
ch,
track.bitrate() / 1000
)
}

View file

@ -1,6 +1,5 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::convert::TryInto;
use std::fmt;
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
@ -99,99 +98,6 @@ boxtype! {
EsdsBox => 0x65736473
}
impl fmt::Debug for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(self.clone());
write!(f, "{}", fourcc)
}
}
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,
}
impl From<u32> for FourCC {
fn from(number: u32) -> Self {
let mut box_chars = Vec::new();
for x in 0..4 {
let c = (number >> (x * 8) & 0x0000_00FF) as u8;
box_chars.push(c);
}
box_chars.reverse();
let box_string = match String::from_utf8(box_chars) {
Ok(t) => t,
_ => String::from("null"), // error to retrieve fourcc
};
FourCC { value: box_string }
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> u32 {
(&fourcc).into()
}
}
impl From<&FourCC> for u32 {
fn from(fourcc: &FourCC) -> u32 {
let mut b: [u8; 4] = Default::default();
b.copy_from_slice(fourcc.value.as_bytes());
u32::from_be_bytes(b)
}
}
impl From<String> for FourCC {
fn from(fourcc: String) -> FourCC {
let value = if fourcc.len() > 4 {
fourcc[0..4].to_string()
} else {
fourcc
};
FourCC { value }
}
}
impl From<&str> for FourCC {
fn from(fourcc: &str) -> FourCC {
let value = if fourcc.len() > 4 {
fourcc[0..4].to_string()
} else {
fourcc.to_string()
};
FourCC { value }
}
}
impl From<BoxType> for FourCC {
fn from(t: BoxType) -> FourCC {
let box_num: u32 = Into::into(t);
From::from(box_num)
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let code: u32 = self.into();
write!(f, "{} / {:#010X}", self.value, code)
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
pub trait Mp4Box: Sized {
fn box_type() -> BoxType;
fn box_size(&self) -> u64;

View file

@ -1,58 +1,15 @@
use std::fmt;
pub use bytes::Bytes;
mod error;
pub use error::Error;
mod types;
pub use types::*;
mod atoms;
mod reader;
pub use reader::Mp4Reader;
mod track;
pub use track::{Mp4Track, TrackType, MediaType};
pub use track::Mp4Track;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Mp4Sample {
pub start_time: u64,
pub duration: u32,
pub rendering_offset: i32,
pub is_sync: bool,
pub bytes: Bytes,
}
impl PartialEq for Mp4Sample {
fn eq(&self, other: &Self) -> bool {
self.start_time == other.start_time
&& self.duration == other.duration
&& self.rendering_offset == other.rendering_offset
&& self.is_sync == other.is_sync
&& self.bytes.len() == other.bytes.len() // XXX for easy check
}
}
impl fmt::Display for Mp4Sample {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
self.start_time,
self.duration,
self.rendering_offset,
self.is_sync,
self.bytes.len()
)
}
}
pub fn creation_time(creation_time: u64) -> u64 {
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
if creation_time >= 2082844800 {
creation_time - 2082844800
} else {
creation_time
}
}

View file

@ -1,7 +1,7 @@
use std::io::{Read, Seek, SeekFrom};
use crate::atoms::*;
use crate::{Error, Mp4Sample, Mp4Track, Result};
use crate::*;
#[derive(Debug)]
pub struct Mp4Reader<R> {
@ -62,9 +62,8 @@ impl<R: Read + Seek> Mp4Reader<R> {
let tracks = if let Some(ref moov) = moov {
let mut tracks = Vec::with_capacity(moov.traks.len());
for (i, trak) in moov.traks.iter().enumerate() {
let track_id = i as u32 + 1;
assert_eq!(track_id, trak.tkhd.track_id);
tracks.push(Mp4Track::new(track_id, trak));
assert_eq!(trak.tkhd.track_id, i as u32 + 1);
tracks.push(Mp4Track::from(trak));
}
tracks
} else {

View file

@ -1,110 +1,47 @@
use std::fmt;
use std::time::Duration;
use std::convert::TryFrom;
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use crate::atoms::trak::TrakBox;
use crate::atoms::*;
use crate::atoms::{trak::TrakBox, stbl::StblBox};
use crate::{Bytes, Error, Mp4Sample, Result};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrackType {
Video,
Audio,
Hint,
Text,
Unknown,
}
impl fmt::Display for TrackType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
TrackType::Video => "video",
TrackType::Audio => "audio",
TrackType::Hint => "hint",
TrackType::Text => "text",
TrackType::Unknown => "unknown", // XXX
};
write!(f, "{}", s)
}
}
impl From<&str> for TrackType {
fn from(handler: &str) -> TrackType {
match handler {
"vide" => TrackType::Video,
"soun" => TrackType::Audio,
"hint" => TrackType::Hint,
"text" => TrackType::Text,
_ => TrackType::Unknown,
}
}
}
impl From<&FourCC> for TrackType {
fn from(fourcc: &FourCC) -> TrackType {
TrackType::from(fourcc.value.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MediaType {
H264,
AAC,
Unknown,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
MediaType::H264 => "h264",
MediaType::AAC => "aac",
MediaType::Unknown => "unknown", // XXX
};
write!(f, "{}", s)
}
}
use crate::*;
#[derive(Debug)]
pub struct Mp4Track {
track_id: u32,
track_type: TrackType,
media_type: MediaType,
trak: TrakBox,
}
impl Mp4Track {
pub(crate) fn new(track_id: u32, trak: &TrakBox) -> Self {
pub(crate) fn from(trak: &TrakBox) -> Self {
let trak = trak.clone();
let track_type = (&trak.mdia.hdlr.handler_type).into();
let media_type = if trak.mdia.minf.stbl.stsd.avc1.is_some() {
MediaType::H264
} else if trak.mdia.minf.stbl.stsd.mp4a.is_some() {
MediaType::AAC
} else {
MediaType::Unknown
};
Self { track_id, track_type, media_type, trak }
Self { trak }
}
pub fn track_id(&self) -> u32 {
self.track_id
self.trak.tkhd.track_id
}
pub fn track_type(&self) -> TrackType {
self.track_type
pub fn track_type(&self) -> Result<TrackType> {
TrackType::try_from(&self.trak.mdia.hdlr.handler_type)
}
pub fn media_type(&self) -> MediaType {
self.media_type
}
pub fn box_type(&self) -> FourCC {
pub fn media_type(&self) -> Result<MediaType> {
if self.trak.mdia.minf.stbl.stsd.avc1.is_some() {
FourCC::from(BoxType::Avc1Box)
Ok(MediaType::H264)
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
FourCC::from(BoxType::Mp4aBox)
Ok(MediaType::AAC)
} else {
FourCC::from("null") // XXX
Err(Error::InvalidData("unsupported media type"))
}
}
pub fn box_type(&self) -> Result<FourCC> {
if self.trak.mdia.minf.stbl.stsd.avc1.is_some() {
Ok(FourCC::from(BoxType::Avc1Box))
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(FourCC::from(BoxType::Mp4aBox))
} else {
Err(Error::InvalidData("unsupported sample entry box"))
}
}
@ -158,8 +95,9 @@ impl Mp4Track {
}
pub fn duration(&self) -> Duration {
Duration::from_micros(self.trak.mdia.mdhd.duration * 1_000_000
/ self.trak.mdia.mdhd.timescale as u64)
Duration::from_micros(
self.trak.mdia.mdhd.duration * 1_000_000 / self.trak.mdia.mdhd.timescale as u64,
)
}
pub fn bitrate(&self) -> u32 {
@ -173,48 +111,39 @@ impl Mp4Track {
}
pub fn sample_count(&self) -> u32 {
let stsz = &self.stbl().stsz;
stsz.sample_sizes.len() as u32
}
fn stbl(&self) -> &StblBox {
&self.trak.mdia.minf.stbl
self.trak.mdia.minf.stbl.stsz.sample_sizes.len() as u32
}
fn stsc_index(&self, sample_id: u32) -> usize {
let stsc = &self.stbl().stsc;
for (i, entry) in stsc.entries.iter().enumerate() {
for (i, entry) in self.trak.mdia.minf.stbl.stsc.entries.iter().enumerate() {
if sample_id < entry.first_sample {
assert_ne!(i, 0);
return i - 1;
}
}
assert_ne!(stsc.entries.len(), 0);
stsc.entries.len() - 1
assert_ne!(self.trak.mdia.minf.stbl.stsc.entries.len(), 0);
self.trak.mdia.minf.stbl.stsc.entries.len() - 1
}
fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
let stbl = self.stbl();
if let Some(ref stco) = stbl.stco {
if let Some(ref stco) = self.trak.mdia.minf.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.track_id,
self.track_id(),
BoxType::StcoBox,
chunk_id,
));
}
} else {
if let Some(ref co64) = stbl.co64 {
if let Some(ref co64) = self.trak.mdia.minf.stbl.co64 {
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
return Ok(*offset);
} else {
return Err(Error::EntryInStblNotFound(
self.track_id,
self.track_id(),
BoxType::Co64Box,
chunk_id,
));
@ -222,18 +151,16 @@ impl Mp4Track {
}
}
assert!(stbl.stco.is_some() || stbl.co64.is_some());
assert!(self.trak.mdia.minf.stbl.stco.is_some() || self.trak.mdia.minf.stbl.co64.is_some());
return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
}
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
let stbl = self.stbl();
assert!(stbl.ctts.is_some());
let ctts = if let Some(ref ctts) = stbl.ctts {
assert!(self.trak.mdia.minf.stbl.ctts.is_some());
let ctts = if let Some(ref ctts) = self.trak.mdia.minf.stbl.ctts {
ctts
} else {
return Err(Error::BoxInStblNotFound(self.track_id, BoxType::CttsBox));
return Err(Error::BoxInStblNotFound(self.track_id(), BoxType::CttsBox));
};
let mut sample_count = 1;
@ -245,14 +172,14 @@ impl Mp4Track {
}
return Err(Error::EntryInStblNotFound(
self.track_id,
self.track_id(),
BoxType::CttsBox,
sample_id,
));
}
fn sample_size(&self, sample_id: u32) -> Result<u32> {
let stsz = &self.stbl().stsz;
let stsz = &self.trak.mdia.minf.stbl.stsz;
if stsz.sample_size > 0 {
return Ok(stsz.sample_size);
}
@ -260,7 +187,7 @@ impl Mp4Track {
Ok(*size)
} else {
return Err(Error::EntryInStblNotFound(
self.track_id,
self.track_id(),
BoxType::StszBox,
sample_id,
));
@ -268,7 +195,7 @@ impl Mp4Track {
}
fn total_sample_size(&self) -> u64 {
let stsz = &self.stbl().stsz;
let stsz = &self.trak.mdia.minf.stbl.stsz;
if stsz.sample_size > 0 {
stsz.sample_size as u64 * self.sample_count() as u64
} else {
@ -283,7 +210,7 @@ impl Mp4Track {
fn sample_offset(&self, sample_id: u32) -> Result<u64> {
let stsc_index = self.stsc_index(sample_id);
let stsc = &self.stbl().stsc;
let stsc = &self.trak.mdia.minf.stbl.stsc;
let stsc_entry = stsc.entries.get(stsc_index).unwrap();
let first_chunk = stsc_entry.first_chunk;
@ -305,7 +232,7 @@ impl Mp4Track {
}
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
let stts = &self.stbl().stts;
let stts = &self.trak.mdia.minf.stbl.stts;
let mut sample_count = 1;
let mut elapsed = 0;
@ -322,16 +249,14 @@ impl Mp4Track {
}
return Err(Error::EntryInStblNotFound(
self.track_id,
self.track_id(),
BoxType::SttsBox,
sample_id,
));
}
fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
let stbl = self.stbl();
if let Some(ref ctts) = stbl.ctts {
if let Some(ref ctts) = self.trak.mdia.minf.stbl.ctts {
if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
let ctts_entry = ctts.entries.get(ctts_index).unwrap();
return ctts_entry.sample_offset;
@ -341,9 +266,7 @@ impl Mp4Track {
}
fn is_sync_sample(&self, sample_id: u32) -> bool {
let stbl = self.stbl();
if let Some(ref stss) = stbl.stss {
if let Some(ref stss) = self.trak.mdia.minf.stbl.stss {
match stss.entries.binary_search(&sample_id) {
Ok(_) => true,
Err(_) => false,

343
src/types.rs Normal file
View file

@ -0,0 +1,343 @@
use std::convert::TryFrom;
use std::fmt;
use crate::atoms::*;
use crate::*;
pub use bytes::Bytes;
impl fmt::Debug for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(self.clone());
write!(f, "{}", fourcc)
}
}
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,
}
impl From<u32> for FourCC {
fn from(number: u32) -> Self {
let mut box_chars = Vec::new();
for x in 0..4 {
let c = (number >> (x * 8) & 0x0000_00FF) as u8;
box_chars.push(c);
}
box_chars.reverse();
let box_string = match String::from_utf8(box_chars) {
Ok(t) => t,
_ => String::from("null"), // error to retrieve fourcc
};
FourCC { value: box_string }
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> u32 {
(&fourcc).into()
}
}
impl From<&FourCC> for u32 {
fn from(fourcc: &FourCC) -> u32 {
let mut b: [u8; 4] = Default::default();
b.copy_from_slice(fourcc.value.as_bytes());
u32::from_be_bytes(b)
}
}
impl From<String> for FourCC {
fn from(fourcc: String) -> FourCC {
let value = if fourcc.len() > 4 {
fourcc[0..4].to_string()
} else {
fourcc
};
FourCC { value }
}
}
impl From<&str> for FourCC {
fn from(fourcc: &str) -> FourCC {
let value = if fourcc.len() > 4 {
fourcc[0..4].to_string()
} else {
fourcc.to_string()
};
FourCC { value }
}
}
impl From<BoxType> for FourCC {
fn from(t: BoxType) -> FourCC {
let box_num: u32 = Into::into(t);
From::from(box_num)
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let code: u32 = self.into();
write!(f, "{} / {:#010X}", self.value, code)
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
const HANDLER_TYPE_VIDEO: &str = "vide";
const HANDLER_TYPE_AUDIO: &str = "soun";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrackType {
Video,
Audio,
}
impl fmt::Display for TrackType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &str = self.into();
write!(f, "{}", s)
}
}
impl TryFrom<&str> for TrackType {
type Error = Error;
fn try_from(handler: &str) -> Result<TrackType> {
match handler {
HANDLER_TYPE_VIDEO => Ok(TrackType::Video),
HANDLER_TYPE_AUDIO => Ok(TrackType::Audio),
_ => Err(Error::InvalidData("unsupported handler type")),
}
}
}
impl Into<&str> for TrackType {
fn into(self) -> &'static str {
match self {
TrackType::Video => HANDLER_TYPE_VIDEO,
TrackType::Audio => HANDLER_TYPE_AUDIO,
}
}
}
impl Into<&str> for &TrackType {
fn into(self) -> &'static str {
match self {
TrackType::Video => HANDLER_TYPE_VIDEO,
TrackType::Audio => HANDLER_TYPE_AUDIO,
}
}
}
impl TryFrom<&FourCC> for TrackType {
type Error = Error;
fn try_from(fourcc: &FourCC) -> Result<TrackType> {
TrackType::try_from(fourcc.value.as_str())
}
}
impl Into<FourCC> for TrackType {
fn into(self) -> FourCC {
let s: &str = self.into();
FourCC::from(s)
}
}
const MEDIA_TYPE_H264: &str = "h264";
const MEDIA_TYPE_AAC: &str = "aac";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MediaType {
H264,
AAC,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &str = self.into();
write!(f, "{}", s)
}
}
impl TryFrom<&str> for MediaType {
type Error = Error;
fn try_from(media: &str) -> Result<MediaType> {
match media {
MEDIA_TYPE_H264 => Ok(MediaType::H264),
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
_ => Err(Error::InvalidData("unsupported media type")),
}
}
}
impl Into<&str> for MediaType {
fn into(self) -> &'static str {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::AAC => MEDIA_TYPE_AAC,
}
}
}
impl Into<&str> for &MediaType {
fn into(self) -> &'static str {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::AAC => MEDIA_TYPE_AAC,
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SampleFreqIndex {
Freq96000 = 0x0,
Freq88200 = 0x1,
Freq64000 = 0x2,
Freq48000 = 0x3,
Freq44100 = 0x4,
Freq32000 = 0x5,
Freq24000 = 0x6,
Freq22050 = 0x7,
Freq16000 = 0x8,
Freq12000 = 0x9,
Freq11025 = 0xa,
Freq8000 = 0xb,
}
impl TryFrom<u8> for SampleFreqIndex {
type Error = Error;
fn try_from(value: u8) -> Result<SampleFreqIndex> {
match value {
0x0 => Ok(SampleFreqIndex::Freq96000),
0x1 => Ok(SampleFreqIndex::Freq88200),
0x2 => Ok(SampleFreqIndex::Freq64000),
0x3 => Ok(SampleFreqIndex::Freq48000),
0x4 => Ok(SampleFreqIndex::Freq44100),
0x5 => Ok(SampleFreqIndex::Freq32000),
0x6 => Ok(SampleFreqIndex::Freq24000),
0x7 => Ok(SampleFreqIndex::Freq22050),
0x8 => Ok(SampleFreqIndex::Freq16000),
0x9 => Ok(SampleFreqIndex::Freq12000),
0xa => Ok(SampleFreqIndex::Freq11025),
0xb => Ok(SampleFreqIndex::Freq8000),
_ => Err(Error::InvalidData("invalid sampling frequency index")),
}
}
}
impl SampleFreqIndex {
pub fn freq(&self) -> u32 {
match self {
&SampleFreqIndex::Freq96000 => 96000,
&SampleFreqIndex::Freq88200 => 88200,
&SampleFreqIndex::Freq64000 => 64000,
&SampleFreqIndex::Freq48000 => 48000,
&SampleFreqIndex::Freq44100 => 44100,
&SampleFreqIndex::Freq32000 => 32000,
&SampleFreqIndex::Freq24000 => 24000,
&SampleFreqIndex::Freq22050 => 22050,
&SampleFreqIndex::Freq16000 => 16000,
&SampleFreqIndex::Freq12000 => 12000,
&SampleFreqIndex::Freq11025 => 11025,
&SampleFreqIndex::Freq8000 => 8000,
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ChannelConfig {
Mono = 0x1,
Stereo = 0x2,
Three = 0x3,
Four = 0x4,
Five = 0x5,
FiveOne = 0x6,
SevenOne = 0x7,
}
impl TryFrom<u8> for ChannelConfig {
type Error = Error;
fn try_from(value: u8) -> Result<ChannelConfig> {
match value {
0x1 => Ok(ChannelConfig::Mono),
0x2 => Ok(ChannelConfig::Stereo),
0x3 => Ok(ChannelConfig::Three),
0x4 => Ok(ChannelConfig::Four),
0x5 => Ok(ChannelConfig::Five),
0x6 => Ok(ChannelConfig::FiveOne),
0x7 => Ok(ChannelConfig::SevenOne),
_ => Err(Error::InvalidData("invalid channel configuration")),
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum MediaConfig {
AVC {
width: u16,
height: u16,
// sps: Vec<u8>,
// pps: Vec<u8>,
},
AAC {
freq_index: SampleFreqIndex,
chan_conf: ChannelConfig,
},
}
#[derive(Debug)]
pub struct Mp4Sample {
pub start_time: u64,
pub duration: u32,
pub rendering_offset: i32,
pub is_sync: bool,
pub bytes: Bytes,
}
impl PartialEq for Mp4Sample {
fn eq(&self, other: &Self) -> bool {
self.start_time == other.start_time
&& self.duration == other.duration
&& self.rendering_offset == other.rendering_offset
&& self.is_sync == other.is_sync
&& self.bytes.len() == other.bytes.len() // XXX for easy check
}
}
impl fmt::Display for Mp4Sample {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
self.start_time,
self.duration,
self.rendering_offset,
self.is_sync,
self.bytes.len()
)
}
}
pub fn creation_time(creation_time: u64) -> u64 {
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
if creation_time >= 2082844800 {
creation_time - 2082844800
} else {
creation_time
}
}

View file

@ -1,4 +1,4 @@
use mp4::{TrackType, MediaType};
use mp4::{MediaType, TrackType};
use std::fs::File;
use std::io::BufReader;
@ -81,8 +81,8 @@ fn test_read_mp4() {
// track #1
let track1 = mp4.tracks().get(0).unwrap();
assert_eq!(track1.track_id(), 1);
assert_eq!(track1.track_type(), TrackType::Video);
assert_eq!(track1.media_type(), MediaType::H264);
assert_eq!(track1.track_type().unwrap(), TrackType::Video);
assert_eq!(track1.media_type().unwrap(), MediaType::H264);
assert_eq!(track1.width(), 320);
assert_eq!(track1.height(), 240);
assert_eq!(track1.bitrate(), 0); // XXX
@ -90,8 +90,8 @@ fn test_read_mp4() {
// track #2
let track2 = mp4.tracks().get(1).unwrap();
assert_eq!(track2.track_type(), TrackType::Audio);
assert_eq!(track2.media_type(), MediaType::AAC);
assert_eq!(track2.track_type().unwrap(), TrackType::Audio);
assert_eq!(track2.media_type().unwrap(), MediaType::AAC);
assert_eq!(track2.sample_rate(), 48000);
assert_eq!(track2.bitrate(), 0); // XXX
}