1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-05-19 16:58:04 +00:00

add support for hev1 box

This commit is contained in:
Alf 2020-09-01 20:41:34 -07:00
parent 65b3408625
commit b4b4dbd49f
5 changed files with 278 additions and 3 deletions

219
src/mp4box/hev1.rs Normal file
View file

@ -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 + 7;
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);
}
}

View file

@ -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;
@ -94,6 +95,8 @@ boxtype! {
SmhdBox => 0x736d6864,
Avc1Box => 0x61766331,
AvcCBox => 0x61766343,
Hev1Box => 0x68657631,
HvcCBox => 0x68766343,
Mp4aBox => 0x6d703461,
EsdsBox => 0x65736473
}

View file

@ -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>,
}
@ -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.
@ -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)?);
}
@ -69,6 +74,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
version,
flags,
avc1,
hev1,
mp4a,
})
}

View file

@ -7,8 +7,17 @@ use std::time::Duration;
use crate::mp4box::trak::TrakBox;
use crate::mp4box::*;
use crate::mp4box::{
avc1::Avc1Box, ctts::CttsBox, ctts::CttsEntry, mp4a::Mp4aBox, smhd::SmhdBox, stco::StcoBox,
stsc::StscEntry, stss::StssBox, stts::SttsEntry, vmhd::VmhdBox,
avc1::Avc1Box,
hev1::Hev1Box,
ctts::CttsBox,
ctts::CttsEntry,
mp4a::Mp4aBox,
smhd::SmhdBox,
stco::StcoBox,
stsc::StscEntry,
stss::StssBox,
stts::SttsEntry,
vmhd::VmhdBox,
};
use crate::*;
@ -24,6 +33,7 @@ impl From<MediaConfig> for TrackConfig {
fn from(media_conf: MediaConfig) -> Self {
match media_conf {
MediaConfig::AvcConfig(avc_conf) => Self::from(avc_conf),
MediaConfig::HevcConfig(hevc_conf) => Self::from(hevc_conf),
MediaConfig::AacConfig(aac_conf) => Self::from(aac_conf),
}
}
@ -40,6 +50,17 @@ impl From<AvcConfig> for TrackConfig {
}
}
impl From<HevcConfig> for TrackConfig {
fn from(hevc_conf: HevcConfig) -> Self {
Self {
track_type: TrackType::Video,
timescale: 1000, // XXX
language: String::from("und"), // XXX
media_conf: MediaConfig::HevcConfig(hevc_conf),
}
}
}
impl From<AacConfig> for TrackConfig {
fn from(aac_conf: AacConfig) -> Self {
Self {
@ -73,6 +94,8 @@ impl Mp4Track {
pub fn media_type(&self) -> Result<MediaType> {
if self.trak.mdia.minf.stbl.stsd.avc1.is_some() {
Ok(MediaType::H264)
} else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() {
Ok(MediaType::H265)
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(MediaType::AAC)
} else {
@ -83,6 +106,8 @@ impl Mp4Track {
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.hev1.is_some() {
Ok(FourCC::from(BoxType::Hev1Box))
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(FourCC::from(BoxType::Mp4aBox))
} else {
@ -460,6 +485,16 @@ impl Mp4TrackWriter {
let avc1 = Avc1Box::new(avc_config);
trak.mdia.minf.stbl.stsd.avc1 = Some(avc1);
}
MediaConfig::HevcConfig(ref hevc_config) => {
trak.tkhd.set_width(hevc_config.width);
trak.tkhd.set_height(hevc_config.height);
let vmhd = VmhdBox::default();
trak.mdia.minf.vmhd = Some(vmhd);
let hev1 = Hev1Box::new(hevc_config);
trak.mdia.minf.stbl.stsd.hev1 = Some(hev1);
}
MediaConfig::AacConfig(ref aac_config) => {
let smhd = SmhdBox::default();
trak.mdia.minf.smhd = Some(smhd);

View file

@ -229,11 +229,13 @@ impl Into<FourCC> for TrackType {
}
const MEDIA_TYPE_H264: &str = "h264";
const MEDIA_TYPE_H265: &str = "h265";
const MEDIA_TYPE_AAC: &str = "aac";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MediaType {
H264,
H265,
AAC,
}
@ -249,6 +251,7 @@ impl TryFrom<&str> for MediaType {
fn try_from(media: &str) -> Result<MediaType> {
match media {
MEDIA_TYPE_H264 => Ok(MediaType::H264),
MEDIA_TYPE_H265 => Ok(MediaType::H265),
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
_ => Err(Error::InvalidData("unsupported media type")),
}
@ -259,6 +262,7 @@ impl Into<&str> for MediaType {
fn into(self) -> &'static str {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::AAC => MEDIA_TYPE_AAC,
}
}
@ -268,6 +272,7 @@ impl Into<&str> for &MediaType {
fn into(self) -> &'static str {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::AAC => MEDIA_TYPE_AAC,
}
}
@ -451,6 +456,12 @@ pub struct AvcConfig {
pub pic_param_set: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone, Default)]
pub struct HevcConfig {
pub width: u16,
pub height: u16,
}
#[derive(Debug, PartialEq, Clone)]
pub struct AacConfig {
pub bitrate: u32,
@ -473,6 +484,7 @@ impl Default for AacConfig {
#[derive(Debug, PartialEq, Clone)]
pub enum MediaConfig {
AvcConfig(AvcConfig),
HevcConfig(HevcConfig),
AacConfig(AacConfig),
}