1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-05-20 01:08:06 +00:00

Add support for parsing subtitle tracks (tx3g box).

This commit is contained in:
Alf 2020-09-04 23:09:33 -07:00
parent fa53d6b138
commit 05e20124e0
7 changed files with 150 additions and 15 deletions

View file

@ -48,6 +48,7 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
let media_info = match track.track_type()? {
TrackType::Video => video_info(track)?,
TrackType::Audio => audio_info(track)?,
TrackType::Subtitle => subtitle_info(track)?,
};
println!(
" Track: #{}({}) {}: {}",
@ -88,13 +89,24 @@ fn video_info(track: &Mp4Track) -> Result<String> {
fn audio_info(track: &Mp4Track) -> Result<String> {
if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a {
if mp4a.esds.is_some() {
let profile = match track.audio_profile() {
Ok(val) => val.to_string(),
_ => "-".to_string(),
};
let channel_config = match track.channel_config() {
Ok(val) => val.to_string(),
_ => "-".to_string(),
};
Ok(format!(
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
track.media_type()?,
track.audio_profile()?,
profile,
track.box_type()?,
track.sample_freq_index()?.freq(),
track.channel_config()?,
channel_config,
track.bitrate() / 1000
))
} else {
@ -110,6 +122,18 @@ fn audio_info(track: &Mp4Track) -> Result<String> {
}
}
fn subtitle_info(track: &Mp4Track) -> Result<String> {
if track.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
Ok(format!(
"{} ({:?})",
track.media_type()?,
track.box_type()?,
))
} else {
Ok("subtitle test".to_string())
}
}
fn creation_time(creation_time: u64) -> u64 {
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
if creation_time >= 2082844800 {

View file

@ -5,12 +5,12 @@ use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
pub(crate) mod avc1;
pub(crate) mod hev1;
pub(crate) mod co64;
pub(crate) mod ctts;
pub(crate) mod edts;
pub(crate) mod elst;
pub(crate) mod ftyp;
pub(crate) mod hev1;
pub(crate) mod hdlr;
pub(crate) mod mdhd;
pub(crate) mod mdia;
@ -32,6 +32,7 @@ pub(crate) mod tkhd;
pub(crate) mod tfhd;
pub(crate) mod trak;
pub(crate) mod traf;
pub(crate) mod tx3g;
pub(crate) mod vmhd;
pub use ftyp::FtypBox;
@ -106,7 +107,8 @@ boxtype! {
Hev1Box => 0x68657631,
HvcCBox => 0x68766343,
Mp4aBox => 0x6d703461,
EsdsBox => 0x65736473
EsdsBox => 0x65736473,
Tx3gBox => 0x74783367
}
pub trait Mp4Box: Sized {

View file

@ -304,13 +304,9 @@ impl<R: Read + Seek> ReadDesc<&mut R> for ESDescriptor {
current = reader.seek(SeekFrom::Current(0))?;
}
if dec_config.is_none() {
return Err(Error::InvalidData("DecoderConfigDescriptor not found"));
}
Ok(ESDescriptor {
es_id,
dec_config: dec_config.unwrap(),
dec_config: dec_config.unwrap_or(DecoderConfigDescriptor::default()),
sl_config: sl_config.unwrap_or(SLConfigDescriptor::default()),
})
}
@ -397,10 +393,6 @@ impl<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
current = reader.seek(SeekFrom::Current(0))?;
}
if dec_specific.is_none() {
return Err(Error::InvalidData("DecoderSpecificDescriptor not found"));
}
Ok(DecoderConfigDescriptor {
object_type_indication,
stream_type,
@ -408,7 +400,7 @@ impl<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
buffer_size_db,
max_bitrate,
avg_bitrate,
dec_specific: dec_specific.unwrap(),
dec_specific: dec_specific.unwrap_or(DecoderSpecificDescriptor::default()),
})
}
}

View file

@ -2,7 +2,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::mp4box::*;
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox};
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StsdBox {
@ -11,6 +11,7 @@ pub struct StsdBox {
pub avc1: Option<Avc1Box>,
pub hev1: Option<Hev1Box>,
pub mp4a: Option<Mp4aBox>,
pub tx3g: Option<Tx3gBox>,
}
impl StsdBox {
@ -50,6 +51,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
let mut avc1 = None;
let mut hev1 = None;
let mut mp4a = None;
let mut tx3g = None;
// Get box header.
let header = BoxHeader::read(reader)?;
@ -65,6 +67,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
BoxType::Mp4aBox => {
mp4a = Some(Mp4aBox::read_box(reader, s)?);
}
BoxType::Tx3gBox => {
tx3g = Some(Tx3gBox::read_box(reader, s)?);
}
_ => {}
}
@ -76,6 +81,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
avc1,
hev1,
mp4a,
tx3g,
})
}
}
@ -91,8 +97,12 @@ impl<W: Write> WriteBox<&mut W> for StsdBox {
if let Some(ref avc1) = self.avc1 {
avc1.write_box(writer)?;
} else if let Some(ref hev1) = self.hev1 {
hev1.write_box(writer)?;
} else if let Some(ref mp4a) = self.mp4a {
mp4a.write_box(writer)?;
} else if let Some(ref tx3g) = self.tx3g {
tx3g.write_box(writer)?;
}
Ok(size)

91
src/mp4box/tx3g.rs Normal file
View file

@ -0,0 +1,91 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq)]
pub struct Tx3gBox {
pub data_reference_index: u16,
}
impl Default for Tx3gBox {
fn default() -> Self {
Tx3gBox {
data_reference_index: 0,
}
}
}
impl Tx3gBox {
pub fn get_type(&self) -> BoxType {
BoxType::Tx3gBox
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + 8
}
}
impl Mp4Box for Tx3gBox {
fn box_type(&self) -> BoxType {
return self.get_type();
}
fn box_size(&self) -> u64 {
return self.get_size();
}
}
impl<R: Read + Seek> ReadBox<&mut R> for Tx3gBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
skip_bytes_to(reader, start + size)?;
Ok(Tx3gBox {
data_reference_index,
})
}
}
impl<W: Write> WriteBox<&mut W> for Tx3gBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.data_reference_index)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_tx3g() {
let src_box = Tx3gBox {
data_reference_index: 1,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Tx3gBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Tx3gBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -98,6 +98,8 @@ impl Mp4Track {
Ok(MediaType::H265)
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(MediaType::AAC)
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
Ok(MediaType::TTXT)
} else {
Err(Error::InvalidData("unsupported media type"))
}
@ -110,6 +112,8 @@ impl Mp4Track {
Ok(FourCC::from(BoxType::Hev1Box))
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(FourCC::from(BoxType::Mp4aBox))
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
Ok(FourCC::from(BoxType::Tx3gBox))
} else {
Err(Error::InvalidData("unsupported sample entry box"))
}

View file

@ -165,14 +165,17 @@ impl fmt::Display for FourCC {
const DISPLAY_TYPE_VIDEO: &str = "Video";
const DISPLAY_TYPE_AUDIO: &str = "Audio";
const DISPLAY_TYPE_SUBTITLE: &str = "Subtitle";
const HANDLER_TYPE_VIDEO: &str = "vide";
const HANDLER_TYPE_AUDIO: &str = "soun";
const HANDLER_TYPE_SUBTITLE: &str = "sbtl";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrackType {
Video,
Audio,
Subtitle,
}
impl fmt::Display for TrackType {
@ -180,6 +183,7 @@ impl fmt::Display for TrackType {
let s = match self {
TrackType::Video => DISPLAY_TYPE_VIDEO,
TrackType::Audio => DISPLAY_TYPE_AUDIO,
TrackType::Subtitle => DISPLAY_TYPE_SUBTITLE,
};
write!(f, "{}", s)
}
@ -191,6 +195,7 @@ impl TryFrom<&str> for TrackType {
match handler {
HANDLER_TYPE_VIDEO => Ok(TrackType::Video),
HANDLER_TYPE_AUDIO => Ok(TrackType::Audio),
HANDLER_TYPE_SUBTITLE => Ok(TrackType::Subtitle),
_ => Err(Error::InvalidData("unsupported handler type")),
}
}
@ -201,6 +206,7 @@ impl Into<&str> for TrackType {
match self {
TrackType::Video => HANDLER_TYPE_VIDEO,
TrackType::Audio => HANDLER_TYPE_AUDIO,
TrackType::Subtitle => HANDLER_TYPE_SUBTITLE,
}
}
}
@ -210,6 +216,7 @@ impl Into<&str> for &TrackType {
match self {
TrackType::Video => HANDLER_TYPE_VIDEO,
TrackType::Audio => HANDLER_TYPE_AUDIO,
TrackType::Subtitle => HANDLER_TYPE_SUBTITLE,
}
}
}
@ -231,12 +238,14 @@ impl Into<FourCC> for TrackType {
const MEDIA_TYPE_H264: &str = "h264";
const MEDIA_TYPE_H265: &str = "h265";
const MEDIA_TYPE_AAC: &str = "aac";
const MEDIA_TYPE_TTXT: &str = "ttxt";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MediaType {
H264,
H265,
AAC,
TTXT,
}
impl fmt::Display for MediaType {
@ -253,6 +262,7 @@ impl TryFrom<&str> for MediaType {
MEDIA_TYPE_H264 => Ok(MediaType::H264),
MEDIA_TYPE_H265 => Ok(MediaType::H265),
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
MEDIA_TYPE_TTXT => Ok(MediaType::TTXT),
_ => Err(Error::InvalidData("unsupported media type")),
}
}
@ -264,6 +274,7 @@ impl Into<&str> for MediaType {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
}
}
@ -274,6 +285,7 @@ impl Into<&str> for &MediaType {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
}
}