Skip to content
Open
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ pub use types::*;

mod mp4box;
pub use mp4box::*;
pub use mp4box::{
gps::{GpsBox, GpsDataBlockInfo},
Mp4Box,
};

mod track;
pub use track::{Mp4Track, TrackConfig};
Expand Down
142 changes: 142 additions & 0 deletions src/mp4box/gps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};

use crate::mp4box::*;

const GPS_DATA_BLOCK_HEADER_SIZE: u64 = 8;
const GPS_DATA_BLOCK_INFO_SIZE: u64 = 8;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub struct GpsDataBlockInfo {
/// File offset of GPS data block in bytes
pub offset: u32,
/// Size of GPS data block in bytes
pub size: u32,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct GpsBox {
pub version_and_date: u64,
pub data_blocks: Vec<GpsDataBlockInfo>,
}

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

pub fn get_size(&self) -> u64 {
HEADER_SIZE
+ GPS_DATA_BLOCK_HEADER_SIZE
+ (self.data_blocks.len() as u64 * GPS_DATA_BLOCK_INFO_SIZE)
}
}

impl Mp4Box for GpsBox {
fn box_type(&self) -> BoxType {
self.get_type()
}

fn box_size(&self) -> u64 {
self.get_size()
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

fn summary(&self) -> Result<String> {
let s = format!(
"version_and_date=0x{:X}, num_blocks={}",
self.version_and_date,
self.data_blocks.len()
);
Ok(s)
}
}

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

let version_and_date = reader.read_u64::<BigEndian>()?;

// TODO - size checks/etc
let mut data_blocks = Vec::new();
let count = (size - HEADER_SIZE - GPS_DATA_BLOCK_HEADER_SIZE) / GPS_DATA_BLOCK_INFO_SIZE;
for _ in 0..count {
let offset = reader.read_u32::<BigEndian>()?;
let size = reader.read_u32::<BigEndian>()?;
if offset == 0 || size == 0 {
// log::warn!("Ignoring block offset={}, size={}", offset, size);
} else {
data_blocks.push(GpsDataBlockInfo { offset, size });
}
}

skip_bytes_to(reader, start + size)?;

Ok(GpsBox {
version_and_date,
data_blocks,
})
}
}

impl<W: Write> WriteBox<&mut W> for GpsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
writer.write_u64::<BigEndian>(self.version_and_date)?;
for info in self.data_blocks.iter() {
writer.write_u32::<BigEndian>(info.offset)?;
writer.write_u32::<BigEndian>(info.size)?;
}
Ok(size)
}
}

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

#[test]
fn test_gps() {
let src_box = GpsBox {
version_and_date: 12345678,
data_blocks: vec![GpsDataBlockInfo {
offset: 100,
size: 256,
}],
};
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::GpsBox);
assert_eq!(src_box.box_size(), header.size);

let dst_box = GpsBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}

#[test]
fn test_gps_empty() {
let src_box = GpsBox::default();
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::GpsBox);
assert_eq!(src_box.box_size(), header.size);

let dst_box = GpsBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}
2 changes: 1 addition & 1 deletion src/mp4box/ilst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for IlstItemBox {
}
}

impl<'a> Metadata<'a> for IlstBox {
impl Metadata<'_> for IlstBox {
fn title(&self) -> Option<Cow<str>> {
self.items.get(&MetadataKey::Title).map(item_to_str)
}
Expand Down
2 changes: 2 additions & 0 deletions src/mp4box/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub(crate) mod edts;
pub(crate) mod elst;
pub(crate) mod emsg;
pub(crate) mod ftyp;
pub(crate) mod gps;
pub(crate) mod hdlr;
pub(crate) mod hev1;
pub(crate) mod ilst;
Expand Down Expand Up @@ -237,6 +238,7 @@ boxtype! {
DayBox => 0xa9646179,
CovrBox => 0x636f7672,
DescBox => 0x64657363,
GpsBox => 0x67707320,
WideBox => 0x77696465,
WaveBox => 0x77617665
}
Expand Down
17 changes: 16 additions & 1 deletion src/mp4box/moov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::io::{Read, Seek, Write};

use crate::meta::MetaBox;
use crate::mp4box::*;
use crate::mp4box::{mvex::MvexBox, mvhd::MvhdBox, trak::TrakBox, udta::UdtaBox};
use crate::mp4box::{gps::GpsBox, mvex::MvexBox, mvhd::MvhdBox, trak::TrakBox, udta::UdtaBox};

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct MoovBox {
Expand All @@ -20,6 +20,9 @@ pub struct MoovBox {

#[serde(skip_serializing_if = "Option::is_none")]
pub udta: Option<UdtaBox>,

#[serde(skip_serializing_if = "Option::is_none")]
pub gps: Option<GpsBox>,
}

impl MoovBox {
Expand All @@ -38,6 +41,9 @@ impl MoovBox {
if let Some(udta) = &self.udta {
size += udta.box_size();
}
if let Some(gps) = &self.gps {
size += gps.box_size();
}
size
}
}
Expand Down Expand Up @@ -69,6 +75,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
let mut meta = None;
let mut udta = None;
let mut mvex = None;
let mut gps = None;
let mut traks = Vec::new();

let mut current = reader.stream_position()?;
Expand Down Expand Up @@ -100,6 +107,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
BoxType::UdtaBox => {
udta = Some(UdtaBox::read_box(reader, s)?);
}
BoxType::GpsBox => {
gps = Some(GpsBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
Expand All @@ -121,6 +131,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
udta,
mvex,
traks,
gps,
})
}
}
Expand All @@ -140,6 +151,9 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
if let Some(udta) = &self.udta {
udta.write_box(writer)?;
}
if let Some(gps) = &self.gps {
gps.write_box(writer)?;
}
Ok(0)
}
}
Expand All @@ -158,6 +172,7 @@ mod tests {
traks: vec![],
meta: Some(MetaBox::default()),
udta: Some(UdtaBox::default()),
gps: Some(GpsBox::default()),
};

let mut buf = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion src/mp4box/mp4a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ fn size_of_length(size: u32) -> u32 {
fn write_desc<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
writer.write_u8(tag)?;

if size as u64 > std::u32::MAX as u64 {
if size as u64 > u32::MAX as u64 {
return Err(Error::InvalidData("invalid descriptor length range"));
}

Expand Down
4 changes: 2 additions & 2 deletions src/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ impl Mp4Track {

pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
match avc1.avcc.sequence_parameter_sets.get(0) {
match avc1.avcc.sequence_parameter_sets.first() {
Some(nal) => Ok(nal.bytes.as_ref()),
None => Err(Error::EntryInStblNotFound(
self.track_id(),
Expand All @@ -276,7 +276,7 @@ impl Mp4Track {

pub fn picture_parameter_set(&self) -> Result<&[u8]> {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
match avc1.avcc.picture_parameter_sets.get(0) {
match avc1.avcc.picture_parameter_sets.first() {
Some(nal) => Ok(nal.bytes.as_ref()),
None => Err(Error::EntryInStblNotFound(
self.track_id(),
Expand Down
2 changes: 1 addition & 1 deletion src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl<W: Write + Seek> Mp4Writer<W> {
fn update_mdat_size(&mut self) -> Result<()> {
let mdat_end = self.writer.stream_position()?;
let mdat_size = mdat_end - self.mdat_pos;
if mdat_size > std::u32::MAX as u64 {
if mdat_size > u32::MAX as u64 {
self.writer.seek(SeekFrom::Start(self.mdat_pos))?;
self.writer.write_u32::<BigEndian>(1)?;
self.writer.seek(SeekFrom::Start(self.mdat_pos + 8))?;
Expand Down
Loading