Skip to content

Commit

Permalink
Merge pull request #26 from alfg/hev1-box
Browse files Browse the repository at this point in the history
Hev1 Box Support
  • Loading branch information
alfg authored Sep 2, 2020
2 parents 65b3408 + 3b5a728 commit e4c7cf4
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 14 deletions.
6 changes: 5 additions & 1 deletion examples/mp4copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter};
use std::path::Path;

use mp4::{AacConfig, AvcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig};
use mp4::{AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig};

fn main() {
let args: Vec<String> = env::args().collect();
Expand Down Expand Up @@ -48,6 +48,10 @@ fn copy<P: AsRef<Path>>(src_filename: &P, dst_filename: &P) -> Result<()> {
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::AAC => MediaConfig::AacConfig(AacConfig {
bitrate: track.bitrate(),
profile: track.audio_profile()?,
Expand Down
3 changes: 3 additions & 0 deletions examples/mp4dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
if let Some(ref avc1) = &stbl.stsd.avc1 {
boxes.push(build_box(avc1));
}
if let Some(ref hev1) = &stbl.stsd.hev1 {
boxes.push(build_box(hev1));
}
if let Some(ref mp4a) = &stbl.stsd.mp4a {
boxes.push(build_box(mp4a));
}
Expand Down
32 changes: 22 additions & 10 deletions examples/mp4info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,28 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
}

fn video_info(track: &Mp4Track) -> Result<String> {
Ok(format!(
"{} ({}) ({:?}), {}x{}, {} kb/s, {:.2} fps",
track.media_type()?,
track.video_profile()?,
track.box_type()?,
track.width(),
track.height(),
track.bitrate() / 1000,
track.frame_rate_f64()
))
if track.trak.mdia.minf.stbl.stsd.avc1.is_some() {
Ok(format!(
"{} ({}) ({:?}), {}x{}, {} kb/s, {:.2} fps",
track.media_type()?,
track.video_profile()?,
track.box_type()?,
track.width(),
track.height(),
track.bitrate() / 1000,
track.frame_rate_f64()
))
} else {
Ok(format!(
"{} ({:?}), {}x{}, {} kb/s, {:.2} fps",
track.media_type()?,
track.box_type()?,
track.width(),
track.height(),
track.bitrate() / 1000,
track.frame_rate_f64()
))
}
}

fn audio_info(track: &Mp4Track) -> Result<String> {
Expand Down
219 changes: 219 additions & 0 deletions src/mp4box/hev1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};

use crate::mp4box::*;

#[derive(Debug, Clone, PartialEq)]
pub struct Hev1Box {
pub data_reference_index: u16,
pub width: u16,
pub height: u16,
pub horizresolution: FixedPointU16,
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub hvcc: HvcCBox,
}

impl Default for Hev1Box {
fn default() -> Self {
Hev1Box {
data_reference_index: 0,
width: 0,
height: 0,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
frame_count: 1,
depth: 0x0018,
hvcc: HvcCBox::default(),
}
}
}

impl Hev1Box {
pub fn new(config: &HevcConfig) -> Self {
Hev1Box {
data_reference_index: 1,
width: config.width,
height: config.height,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
frame_count: 1,
depth: 0x0018,
hvcc: HvcCBox::new(),
}
}

pub fn get_type(&self) -> BoxType {
BoxType::Hev1Box
}

pub fn get_size(&self) -> u64 {
HEADER_SIZE + 8 + 70 + self.hvcc.box_size()
}
}

impl Mp4Box for Hev1Box {
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 Hev1Box {
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>()?;

reader.read_u32::<BigEndian>()?; // pre-defined, reserved
reader.read_u64::<BigEndian>()?; // pre-defined
reader.read_u32::<BigEndian>()?; // pre-defined
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
reader.read_u32::<BigEndian>()?; // reserved
let frame_count = reader.read_u16::<BigEndian>()?;
skip_bytes(reader, 32)?; // compressorname
let depth = reader.read_u16::<BigEndian>()?;
reader.read_i16::<BigEndian>()?; // pre-defined

let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if name == BoxType::HvcCBox {
let hvcc = HvcCBox::read_box(reader, s)?;

skip_bytes_to(reader, start + size)?;

Ok(Hev1Box {
data_reference_index,
width,
height,
horizresolution,
vertresolution,
frame_count,
depth,
hvcc,
})
} else {
Err(Error::InvalidData("hvcc not found"))
}
}
}

impl<W: Write> WriteBox<&mut W> for Hev1Box {
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)?;

writer.write_u32::<BigEndian>(0)?; // pre-defined, reserved
writer.write_u64::<BigEndian>(0)?; // pre-defined
writer.write_u32::<BigEndian>(0)?; // pre-defined
writer.write_u16::<BigEndian>(self.width)?;
writer.write_u16::<BigEndian>(self.height)?;
writer.write_u32::<BigEndian>(self.horizresolution.raw_value())?;
writer.write_u32::<BigEndian>(self.vertresolution.raw_value())?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.frame_count)?;
// skip compressorname
write_zeros(writer, 32)?;
writer.write_u16::<BigEndian>(self.depth)?;
writer.write_i16::<BigEndian>(-1)?; // pre-defined

self.hvcc.write_box(writer)?;

Ok(size)
}
}

#[derive(Debug, Clone, PartialEq, Default)]
pub struct HvcCBox {
pub configuration_version: u8,
}

impl HvcCBox {
pub fn new() -> Self {
Self {
configuration_version: 1,
}
}
}

impl Mp4Box for HvcCBox {
fn box_type(&self) -> BoxType {
BoxType::HvcCBox
}

fn box_size(&self) -> u64 {
let size = HEADER_SIZE + 1;
size
}
}

impl<R: Read + Seek> ReadBox<&mut R> for HvcCBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;

let configuration_version = reader.read_u8()?;

skip_bytes_to(reader, start + size)?;

Ok(HvcCBox {
configuration_version,
})
}
}

impl<W: Write> WriteBox<&mut W> for HvcCBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;

writer.write_u8(self.configuration_version)?;
Ok(size)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;

#[test]
fn test_hev1() {
let src_box = Hev1Box {
data_reference_index: 1,
width: 320,
height: 240,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
frame_count: 1,
depth: 24,
hvcc: HvcCBox {
configuration_version: 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::Hev1Box);
assert_eq!(src_box.box_size(), header.size);

let dst_box = Hev1Box::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}
3 changes: 3 additions & 0 deletions src/mp4box/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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;
Expand Down Expand Up @@ -94,6 +95,8 @@ boxtype! {
SmhdBox => 0x736d6864,
Avc1Box => 0x61766331,
AvcCBox => 0x61766343,
Hev1Box => 0x68657631,
HvcCBox => 0x68766343,
Mp4aBox => 0x6d703461,
EsdsBox => 0x65736473
}
Expand Down
8 changes: 7 additions & 1 deletion src/mp4box/stsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};

use crate::mp4box::*;
use crate::mp4box::{avc1::Avc1Box, mp4a::Mp4aBox};
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox};

#[derive(Debug, Clone, PartialEq, Default)]
pub struct StsdBox {
pub version: u8,
pub flags: u32,
pub avc1: Option<Avc1Box>,
pub hev1: Option<Hev1Box>,
pub mp4a: Option<Mp4aBox>,
}

Expand Down Expand Up @@ -47,6 +48,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
reader.read_u32::<BigEndian>()?; // XXX entry_count

let mut avc1 = None;
let mut hev1 = None;
let mut mp4a = None;

// Get box header.
Expand All @@ -57,6 +59,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
BoxType::Avc1Box => {
avc1 = Some(Avc1Box::read_box(reader, s)?);
}
BoxType::Hev1Box => {
hev1 = Some(Hev1Box::read_box(reader, s)?);
}
BoxType::Mp4aBox => {
mp4a = Some(Mp4aBox::read_box(reader, s)?);
}
Expand All @@ -69,6 +74,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
version,
flags,
avc1,
hev1,
mp4a,
})
}
Expand Down
Loading

0 comments on commit e4c7cf4

Please sign in to comment.