├── .gitignore ├── Cargo.toml ├── src ├── error.rs ├── fmp4 │ ├── mod.rs │ ├── common.rs │ ├── media.rs │ └── initialization.rs ├── io.rs ├── lib.rs ├── aac.rs ├── avc.rs └── mpeg2_ts.rs ├── README.md ├── .travis.yml ├── LICENSE └── examples └── ts_to_fmp4.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mse_fmp4" 3 | version = "0.2.0" 4 | authors = ["Takeru Ohta "] 5 | description = "A library for generating fragmented MP4 that playable via Media Source Extensions" 6 | homepage = "https://github.com/sile/mse_fmp4" 7 | repository = "https://github.com/sile/mse_fmp4" 8 | readme = "README.md" 9 | license = "MIT" 10 | edition = "2018" 11 | 12 | [badges] 13 | travis-ci = {repository = "sile/mse_fmp4"} 14 | codecov = {repository = "sile/mse_fmp4"} 15 | 16 | [dependencies] 17 | byteorder = "1" 18 | mpeg2ts= "0.1" 19 | trackable = "0.2" 20 | 21 | [dev-dependencies] 22 | clap = "2" 23 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use mpeg2ts; 2 | use trackable::error::{ErrorKind as TrackableErrorKind, ErrorKindExt, TrackableError}; 3 | 4 | /// This crate specific `Error` type. 5 | #[derive(Debug, Clone, TrackableError)] 6 | pub struct Error(TrackableError); 7 | impl From for Error { 8 | fn from(f: mpeg2ts::Error) -> Self { 9 | let kind = match *f.kind() { 10 | mpeg2ts::ErrorKind::InvalidInput => ErrorKind::InvalidInput, 11 | mpeg2ts::ErrorKind::Unsupported => ErrorKind::Unsupported, 12 | mpeg2ts::ErrorKind::Other => ErrorKind::Other, 13 | }; 14 | kind.takes_over(f).into() 15 | } 16 | } 17 | 18 | /// Possible error kinds. 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 20 | #[allow(missing_docs)] 21 | pub enum ErrorKind { 22 | InvalidInput, 23 | Unsupported, 24 | Other, 25 | } 26 | impl TrackableErrorKind for ErrorKind {} 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mse_fmp4 2 | ========= 3 | 4 | [![mse_fmp4](https://img.shields.io/crates/v/mse_fmp4.svg)](https://crates.io/crates/mse_fmp4) 5 | [![Documentation](https://docs.rs/mse_fmp4/badge.svg)](https://docs.rs/mse_fmp4) 6 | [![Build Status](https://travis-ci.org/sile/mse_fmp4.svg?branch=master)](https://travis-ci.org/sile/mse_fmp4) 7 | [![Code Coverage](https://codecov.io/gh/sile/mse_fmp4/branch/master/graph/badge.svg)](https://codecov.io/gh/sile/mse_fmp4/branch/master) 8 | [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 9 | 10 | A Rust library for generating fragmented MP4 that playable via Media Source Extensions. 11 | 12 | [Documentation](https://docs.rs/mse_fmp4) 13 | 14 | References 15 | ---------- 16 | 17 | - [ISO BMFF Byte Stream Format (Fragmented MP4)][fmp4] 18 | - [Media Source Extensions][MSE] 19 | 20 | [fmp4]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html 21 | [MSE]: http://www.w3.org/TR/media-source/ 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | matrix: 8 | allow_failures: 9 | - rust: nightly 10 | 11 | env: 12 | global: 13 | - RUSTFLAGS="-C link-dead-code" 14 | 15 | addons: 16 | apt: 17 | packages: 18 | - libcurl4-openssl-dev 19 | - libelf-dev 20 | - libdw-dev 21 | - cmake 22 | - gcc 23 | - binutils-dev 24 | - libiberty-dev 25 | 26 | after_success: | 27 | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && 28 | tar xzf master.tar.gz && 29 | cd kcov-master && 30 | mkdir build && 31 | cd build && 32 | cmake .. && 33 | make && 34 | make install DESTDIR=../../kcov-build && 35 | cd ../.. && 36 | rm -rf kcov-master && 37 | for file in target/debug/mse_fmp4-*[^\.d]; do mkdir -p "target/cov/$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done && 38 | bash <(curl -s https://codecov.io/bash) && 39 | echo "Uploaded code coverage" 40 | -------------------------------------------------------------------------------- /src/fmp4/mod.rs: -------------------------------------------------------------------------------- 1 | //! Fragmented MP4 (ISO BMFF) related constituent elements. 2 | pub use self::common::Mp4Box; 3 | pub use self::initialization::{ 4 | AacSampleEntry, AvcConfigurationBox, AvcSampleEntry, ChunkOffsetBox, DataEntryUrlBox, 5 | DataInformationBox, DataReferenceBox, EditBox, EditListBox, FileTypeBox, HandlerReferenceBox, 6 | InitializationSegment, MediaBox, MediaHeaderBox, MediaInformationBox, MovieBox, 7 | MovieExtendsBox, MovieExtendsHeaderBox, MovieHeaderBox, Mpeg4EsDescriptorBox, 8 | SampleDescriptionBox, SampleEntry, SampleSizeBox, SampleTableBox, SampleToChunkBox, 9 | SoundMediaHeaderBox, TimeToSampleBox, TrackBox, TrackExtendsBox, TrackHeaderBox, 10 | VideoMediaHeaderBox, 11 | }; 12 | pub use self::media::{ 13 | MediaDataBox, MediaSegment, MovieFragmentBox, MovieFragmentHeaderBox, Sample, SampleFlags, 14 | TrackFragmentBaseMediaDecodeTimeBox, TrackFragmentBox, TrackFragmentHeaderBox, TrackRunBox, 15 | }; 16 | 17 | const VIDEO_TRACK_ID: u32 = 1; 18 | const AUDIO_TRACK_ID: u32 = 2; 19 | 20 | mod common; 21 | mod initialization; 22 | mod media; 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Takeru Ohta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/ts_to_fmp4.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | extern crate mpeg2ts; 3 | extern crate mse_fmp4; 4 | #[macro_use] 5 | extern crate trackable; 6 | 7 | use clap::{App, Arg}; 8 | use mpeg2ts::ts::TsPacketReader; 9 | use mse_fmp4::io::WriteTo; 10 | use mse_fmp4::mpeg2_ts; 11 | use std::fs::File; 12 | use trackable::error::Failure; 13 | 14 | fn main() { 15 | let matches = App::new("ts_to_fmp4") 16 | .arg( 17 | Arg::with_name("OUTPUT_FILE_PREFIX") 18 | .long("output-file-prefix") 19 | .takes_value(true) 20 | .default_value("movie"), 21 | ) 22 | .get_matches(); 23 | let output_file_prefix = matches.value_of("OUTPUT_FILE_PREFIX").unwrap(); 24 | 25 | let (initialization_segment, media_segment) = 26 | track_try_unwrap!(mpeg2_ts::to_fmp4(TsPacketReader::new(std::io::stdin()))); 27 | 28 | let path = format!("{}-init.mp4", output_file_prefix); 29 | let out = track_try_unwrap!(File::create(&path).map_err(Failure::from_error)); 30 | track_try_unwrap!(initialization_segment.write_to(out)); 31 | println!("# Initialization Segment: {:?}", path); 32 | 33 | let path = format!("{}.m4s", output_file_prefix); 34 | let out = track_try_unwrap!(File::create(&path).map_err(Failure::from_error)); 35 | track_try_unwrap!(media_segment.write_to(out)); 36 | println!("# Media Segment: {:?}", path); 37 | } 38 | -------------------------------------------------------------------------------- /src/fmp4/common.rs: -------------------------------------------------------------------------------- 1 | use crate::Result; 2 | use std::io::Write; 3 | 4 | /// MP4 (ISO BMFF) box. 5 | pub trait Mp4Box { 6 | /// Box type. 7 | const BOX_TYPE: [u8; 4]; 8 | 9 | /// Box size. 10 | fn box_size(&self) -> Result { 11 | let mut size = 8; 12 | if self.box_version().is_some() | self.box_flags().is_some() { 13 | size += 4; 14 | } 15 | size += track!(self.box_payload_size())?; 16 | Ok(size) 17 | } 18 | 19 | /// Payload size of the box. 20 | fn box_payload_size(&self) -> Result; 21 | 22 | /// Box version. 23 | /// 24 | /// If this method returns `Some(...)`, the box will be regarded as a full box. 25 | fn box_version(&self) -> Option { 26 | None 27 | } 28 | 29 | /// Box flags (for full box). 30 | /// 31 | /// If this method returns `Some(...)`, the box will be regarded as a full box. 32 | fn box_flags(&self) -> Option { 33 | None 34 | } 35 | 36 | /// Writes the box to the given writer. 37 | fn write_box(&self, mut writer: W) -> Result<()> { 38 | write_u32!(writer, track!(self.box_size())?); 39 | write_all!(writer, &Self::BOX_TYPE); 40 | 41 | let version = self.box_version(); 42 | let flags = self.box_flags(); 43 | if version.is_some() || flags.is_some() { 44 | let full_box_header = (u32::from(version.unwrap_or(0)) << 24) | flags.unwrap_or(0); 45 | write_u32!(writer, full_box_header); 46 | } 47 | 48 | track!(self.write_box_payload(writer))?; 49 | Ok(()) 50 | } 51 | 52 | /// Writes the payload of the box to the given writer. 53 | fn write_box_payload(&self, writer: W) -> Result<()>; 54 | } 55 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | //! I/O related constituent elements. 2 | use crate::Result; 3 | use byteorder::ReadBytesExt; 4 | use std::io::{sink, Read, Result as IoResult, Sink, Write}; 5 | 6 | /// A trait for objects which can be written to byte-oriented sinks. 7 | pub trait WriteTo { 8 | /// Writes this object to the given byte-oriented sink. 9 | fn write_to(&self, writer: W) -> Result<()>; 10 | } 11 | 12 | #[derive(Debug)] 13 | pub(crate) struct ByteCounter { 14 | inner: T, 15 | count: u64, 16 | } 17 | impl ByteCounter { 18 | pub fn new(inner: T) -> Self { 19 | ByteCounter { inner, count: 0 } 20 | } 21 | 22 | pub fn count(&self) -> u64 { 23 | self.count 24 | } 25 | } 26 | impl ByteCounter { 27 | pub fn with_sink() -> Self { 28 | Self::new(sink()) 29 | } 30 | 31 | pub fn calculate(f: F) -> Result 32 | where 33 | F: FnOnce(&mut Self) -> Result<()>, 34 | { 35 | let mut writer = ByteCounter::with_sink(); 36 | track!(f(&mut writer))?; 37 | Ok(writer.count() as u64) 38 | } 39 | } 40 | impl Write for ByteCounter { 41 | fn write(&mut self, buf: &[u8]) -> IoResult { 42 | let size = self.inner.write(buf)?; 43 | self.count += size as u64; 44 | Ok(size) 45 | } 46 | 47 | fn flush(&mut self) -> IoResult<()> { 48 | self.inner.flush() 49 | } 50 | } 51 | 52 | #[derive(Debug)] 53 | pub(crate) struct AvcBitReader { 54 | stream: R, 55 | byte: u8, 56 | bit_offset: usize, 57 | } 58 | impl AvcBitReader { 59 | pub fn new(stream: R) -> Self { 60 | AvcBitReader { 61 | stream, 62 | byte: 0, 63 | bit_offset: 8, 64 | } 65 | } 66 | 67 | pub fn read_bit(&mut self) -> Result { 68 | if self.bit_offset == 8 { 69 | self.byte = track_io!(self.stream.read_u8())?; 70 | self.bit_offset = 0; 71 | } 72 | let bit = (self.byte >> (7 - self.bit_offset)) & 0b1; 73 | self.bit_offset += 1; 74 | Ok(bit) 75 | } 76 | 77 | pub fn read_ue(&mut self) -> Result { 78 | track!(self.read_exp_golomb_code()) 79 | } 80 | 81 | fn read_exp_golomb_code(&mut self) -> Result { 82 | let mut leading_zeros = 0; 83 | while 0 == track!(self.read_bit())? { 84 | leading_zeros += 1; 85 | } 86 | let mut n = 0; 87 | for _ in 0..leading_zeros { 88 | let bit = track!(self.read_bit())?; 89 | n = (n << 1) | u64::from(bit); 90 | } 91 | n += 2u64.pow(leading_zeros) - 1; 92 | Ok(n) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is a library for generating fragmented MP4 that playable via Media Source Extensions. 2 | //! 3 | //! # References 4 | //! 5 | //! - [ISO BMFF Byte Stream Format (Fragmented MP4)][fmp4] 6 | //! - [Media Source Extensions][MSE] 7 | //! 8 | //! [fmp4]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html 9 | //! [MSE]: http://www.w3.org/TR/media-source/ 10 | #![warn(missing_docs)] 11 | extern crate byteorder; 12 | extern crate mpeg2ts; 13 | #[macro_use] 14 | extern crate trackable; 15 | 16 | macro_rules! track_io { 17 | ($expr:expr) => { 18 | $expr.map_err(|e: std::io::Error| { 19 | use trackable::error::ErrorKindExt; 20 | track!(crate::Error::from(crate::ErrorKind::Other.cause(e))) 21 | }) 22 | }; 23 | } 24 | macro_rules! write_u8 { 25 | ($w:expr, $n:expr) => {{ 26 | use byteorder::WriteBytesExt; 27 | track_io!($w.write_u8($n))?; 28 | }}; 29 | } 30 | macro_rules! write_u16 { 31 | ($w:expr, $n:expr) => {{ 32 | use byteorder::{BigEndian, WriteBytesExt}; 33 | track_io!($w.write_u16::($n))?; 34 | }}; 35 | } 36 | macro_rules! write_i16 { 37 | ($w:expr, $n:expr) => {{ 38 | use byteorder::{BigEndian, WriteBytesExt}; 39 | track_io!($w.write_i16::($n))?; 40 | }}; 41 | } 42 | macro_rules! write_u24 { 43 | ($w:expr, $n:expr) => {{ 44 | use byteorder::{BigEndian, WriteBytesExt}; 45 | track_io!($w.write_uint::($n as u64, 3))?; 46 | }}; 47 | } 48 | macro_rules! write_u32 { 49 | ($w:expr, $n:expr) => {{ 50 | use byteorder::{BigEndian, WriteBytesExt}; 51 | track_io!($w.write_u32::($n))?; 52 | }}; 53 | } 54 | macro_rules! write_i32 { 55 | ($w:expr, $n:expr) => {{ 56 | use byteorder::{BigEndian, WriteBytesExt}; 57 | track_io!($w.write_i32::($n))?; 58 | }}; 59 | } 60 | macro_rules! write_u64 { 61 | ($w:expr, $n:expr) => {{ 62 | use byteorder::{BigEndian, WriteBytesExt}; 63 | track_io!($w.write_u64::($n))?; 64 | }}; 65 | } 66 | macro_rules! write_all { 67 | ($w:expr, $n:expr) => { 68 | track_io!($w.write_all($n))?; 69 | }; 70 | } 71 | macro_rules! write_zeroes { 72 | ($w:expr, $n:expr) => { 73 | track_io!($w.write_all(&[0; $n][..]))?; 74 | }; 75 | } 76 | macro_rules! write_box { 77 | ($w:expr, $b:expr) => { 78 | track!($b.write_box(&mut $w))?; 79 | }; 80 | } 81 | macro_rules! write_boxes { 82 | ($w:expr, $bs:expr) => { 83 | for b in $bs { 84 | track!(b.write_box(&mut $w))?; 85 | } 86 | }; 87 | } 88 | macro_rules! box_size { 89 | ($b:expr) => { 90 | track!($b.box_size())? 91 | }; 92 | } 93 | macro_rules! optional_box_size { 94 | ($b:expr) => { 95 | if let Some(ref b) = $b.as_ref() { 96 | track!(b.box_size())? 97 | } else { 98 | 0 99 | } 100 | }; 101 | } 102 | macro_rules! boxes_size { 103 | ($b:expr) => {{ 104 | let mut size = 0; 105 | for b in $b.iter() { 106 | size += box_size!(b); 107 | } 108 | size 109 | }}; 110 | } 111 | 112 | pub use error::{Error, ErrorKind}; 113 | 114 | pub mod aac; 115 | pub mod avc; 116 | pub mod fmp4; 117 | pub mod io; 118 | pub mod mpeg2_ts; 119 | 120 | mod error; 121 | 122 | /// This crate specific `Result` type. 123 | pub type Result = std::result::Result; 124 | -------------------------------------------------------------------------------- /src/aac.rs: -------------------------------------------------------------------------------- 1 | //! AAC related constituent elements. 2 | use crate::{ErrorKind, Result}; 3 | use byteorder::{BigEndian, ReadBytesExt}; 4 | use std::io::Read; 5 | 6 | pub(crate) const SAMPLES_IN_FRAME: usize = 1024; 7 | 8 | #[derive(Debug, Clone)] 9 | pub(crate) struct AdtsHeader { 10 | pub profile: AacProfile, 11 | pub sampling_frequency: SamplingFrequency, 12 | pub private: bool, 13 | pub channel_configuration: ChannelConfiguration, 14 | pub frame_len: u16, // u13 15 | pub buffer_fullness: u16, // u11 16 | } 17 | impl AdtsHeader { 18 | const SYNC_WORD: u16 = 0b1111_1111_1111; 19 | const MPEG_VERSION_4: u8 = 0; 20 | const HEADER_LEN_WITHOUT_CRC: u16 = 7; 21 | 22 | pub fn raw_data_blocks_len(&self) -> u16 { 23 | self.frame_len - Self::HEADER_LEN_WITHOUT_CRC 24 | } 25 | 26 | pub fn read_from(mut reader: R) -> Result { 27 | let n = track_io!(reader.read_u16::())?; 28 | track_assert_eq!(n >> 4, Self::SYNC_WORD, ErrorKind::InvalidInput); 29 | 30 | let mpeg_version = ((n >> 3) & 0b1) as u8; 31 | let layer = (n >> 1) & 0b11; 32 | let crc_protection_absent = (n & 0b1) != 0; 33 | track_assert_eq!(mpeg_version, Self::MPEG_VERSION_4, ErrorKind::Unsupported); 34 | track_assert_eq!(layer, 0, ErrorKind::InvalidInput); 35 | 36 | let n = track_io!(reader.read_u8())?; 37 | let profile = match n >> 6 { 38 | 0 => AacProfile::Main, 39 | 1 => AacProfile::Lc, 40 | 2 => AacProfile::Ssr, 41 | 3 => AacProfile::Ltp, 42 | _ => unreachable!(), 43 | }; 44 | let sampling_frequency = track!(SamplingFrequency::from_index((n >> 2) & 0b1111))?; 45 | let private = (n & 0b10) != 0; 46 | let channel_msb = n & 0b1; 47 | 48 | let n = track_io!(reader.read_u8())?; 49 | let channel_configuration = 50 | track!(ChannelConfiguration::from_u8((channel_msb << 2) | (n >> 6)))?; 51 | let originality = (n & 0b10_0000) != 0; 52 | let home = (n & 0b01_0000) != 0; 53 | let copyrighted = (n & 0b00_1000) != 0; 54 | let copyright_id_start = (n & 0b00_0100) != 0; 55 | track_assert!(!originality, ErrorKind::Unsupported); 56 | track_assert!(!home, ErrorKind::Unsupported); 57 | track_assert!(!copyrighted, ErrorKind::Unsupported); 58 | track_assert!(!copyright_id_start, ErrorKind::Unsupported); 59 | let frame_len_msb_2bits = u16::from(n & 0b11); 60 | 61 | let n = track_io!(reader.read_u16::())?; 62 | let frame_len = (frame_len_msb_2bits << 11) | (n >> 5); 63 | let buffer_fullness_msb_5bits = n & 0b1_1111; 64 | 65 | let n = track_io!(reader.read_u8())?; 66 | let buffer_fullness = (buffer_fullness_msb_5bits << 5) | u16::from(n >> 2); 67 | let raw_data_blocks_minus_1 = n & 0b11; 68 | track_assert_eq!(raw_data_blocks_minus_1, 0, ErrorKind::Unsupported); 69 | if !crc_protection_absent { 70 | // 16bits 71 | track_panic!(ErrorKind::Unsupported); 72 | } 73 | 74 | Ok(AdtsHeader { 75 | profile, 76 | sampling_frequency, 77 | private, 78 | channel_configuration, 79 | frame_len, 80 | buffer_fullness, 81 | }) 82 | } 83 | } 84 | 85 | /// Profile. 86 | #[allow(missing_docs)] 87 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 88 | pub enum AacProfile { 89 | /// AAC Main. 90 | Main = 0, 91 | 92 | /// AAC LC (Low Complexity). 93 | Lc = 1, 94 | 95 | /// AAC SSR (Scalable Sample Rate). 96 | Ssr = 2, 97 | 98 | /// AAC LTP (Long Term Prediction). 99 | Ltp = 3, 100 | } 101 | 102 | /// Sampling frequency. 103 | #[allow(missing_docs)] 104 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 105 | pub enum SamplingFrequency { 106 | Hz96000 = 0, 107 | Hz88200 = 1, 108 | Hz64000 = 2, 109 | Hz48000 = 3, 110 | Hz44100 = 4, 111 | Hz32000 = 5, 112 | Hz24000 = 6, 113 | Hz22050 = 7, 114 | Hz16000 = 8, 115 | Hz12000 = 9, 116 | Hz11025 = 10, 117 | Hz8000 = 11, 118 | Hz7350 = 12, 119 | } 120 | impl SamplingFrequency { 121 | pub(crate) fn as_u32(&self) -> u32 { 122 | match *self { 123 | SamplingFrequency::Hz96000 => 96_000, 124 | SamplingFrequency::Hz88200 => 88_200, 125 | SamplingFrequency::Hz64000 => 64_000, 126 | SamplingFrequency::Hz48000 => 48_000, 127 | SamplingFrequency::Hz44100 => 44_100, 128 | SamplingFrequency::Hz32000 => 32_000, 129 | SamplingFrequency::Hz24000 => 24_000, 130 | SamplingFrequency::Hz22050 => 22_050, 131 | SamplingFrequency::Hz16000 => 16_000, 132 | SamplingFrequency::Hz12000 => 12_000, 133 | SamplingFrequency::Hz11025 => 11_025, 134 | SamplingFrequency::Hz8000 => 8_000, 135 | SamplingFrequency::Hz7350 => 7_350, 136 | } 137 | } 138 | 139 | pub(crate) fn as_index(&self) -> u8 { 140 | *self as u8 141 | } 142 | 143 | fn from_index(n: u8) -> Result { 144 | Ok(match n { 145 | 0 => SamplingFrequency::Hz96000, 146 | 1 => SamplingFrequency::Hz88200, 147 | 2 => SamplingFrequency::Hz64000, 148 | 3 => SamplingFrequency::Hz48000, 149 | 4 => SamplingFrequency::Hz44100, 150 | 5 => SamplingFrequency::Hz32000, 151 | 6 => SamplingFrequency::Hz24000, 152 | 7 => SamplingFrequency::Hz22050, 153 | 8 => SamplingFrequency::Hz16000, 154 | 9 => SamplingFrequency::Hz12000, 155 | 10 => SamplingFrequency::Hz11025, 156 | 11 => SamplingFrequency::Hz8000, 157 | 12 => SamplingFrequency::Hz7350, 158 | 13 | 14 => track_panic!(ErrorKind::InvalidInput, "Reserved"), 159 | 15 => track_panic!(ErrorKind::InvalidInput, "Forbidden"), 160 | _ => track_panic!(ErrorKind::InvalidInput, "Unreachable"), 161 | }) 162 | } 163 | } 164 | 165 | /// Channel configuration. 166 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 167 | pub enum ChannelConfiguration { 168 | /// Channel configuration is sent via an inband PCE. 169 | SentViaInbandPce = 0, 170 | 171 | /// 1 channel: front-center. 172 | OneChannel = 1, 173 | 174 | /// 2 channels: front-left, front-right. 175 | TwoChannels = 2, 176 | 177 | /// 3 channels: front-center, front-left, front-right. 178 | ThreeChannels = 3, 179 | 180 | /// 4 channels: front-center, front-left, front-right, back-center. 181 | FourChannels = 4, 182 | 183 | /// 5 channels: front-center, front-left, front-right, back-left, back-right. 184 | FiveChannels = 5, 185 | 186 | /// 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel. 187 | SixChannels = 6, 188 | 189 | /// 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, 190 | /// back-right, LFE-channel. 191 | EightChannels = 7, 192 | } 193 | impl ChannelConfiguration { 194 | fn from_u8(n: u8) -> Result { 195 | Ok(match n { 196 | 0 => ChannelConfiguration::SentViaInbandPce, 197 | 1 => ChannelConfiguration::OneChannel, 198 | 2 => ChannelConfiguration::TwoChannels, 199 | 3 => ChannelConfiguration::ThreeChannels, 200 | 4 => ChannelConfiguration::FourChannels, 201 | 5 => ChannelConfiguration::FiveChannels, 202 | 6 => ChannelConfiguration::SixChannels, 203 | 7 => ChannelConfiguration::EightChannels, 204 | _ => track_panic!(ErrorKind::InvalidInput, "Unreachable"), 205 | }) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/avc.rs: -------------------------------------------------------------------------------- 1 | //! AVC (H.264) related constituent elements. 2 | use crate::io::AvcBitReader; 3 | use crate::{ErrorKind, Result}; 4 | use byteorder::ReadBytesExt; 5 | use std::io::{Read, Write}; 6 | 7 | /// AVC decoder configuration record. 8 | #[allow(missing_docs)] 9 | #[derive(Debug, Clone)] 10 | pub struct AvcDecoderConfigurationRecord { 11 | pub profile_idc: u8, 12 | pub constraint_set_flag: u8, 13 | pub level_idc: u8, 14 | pub sequence_parameter_set: Vec, 15 | pub picture_parameter_set: Vec, 16 | } 17 | impl AvcDecoderConfigurationRecord { 18 | pub(crate) fn write_to(&self, mut writer: W) -> Result<()> { 19 | write_u8!(writer, 1); // configuration_version 20 | 21 | match self.profile_idc { 22 | 100 | 110 | 122 | 144 => track_panic!(ErrorKind::Unsupported), 23 | _ => {} 24 | } 25 | write_u8!(writer, self.profile_idc); 26 | write_u8!(writer, self.constraint_set_flag); 27 | write_u8!(writer, self.level_idc); 28 | write_u8!(writer, 0b1111_1100 | 0b0000_0011); // reserved and length_size_minus_one 29 | 30 | write_u8!(writer, 0b1110_0000 | 0b0000_0001); // reserved and num_of_sequence_parameter_set_ext 31 | write_u16!(writer, self.sequence_parameter_set.len() as u16); 32 | write_all!(writer, &self.sequence_parameter_set); 33 | 34 | write_u8!(writer, 0b0000_0001); // num_of_picture_parameter_set_ext 35 | write_u16!(writer, self.picture_parameter_set.len() as u16); 36 | write_all!(writer, &self.picture_parameter_set); 37 | Ok(()) 38 | } 39 | } 40 | 41 | #[derive(Debug)] 42 | pub(crate) struct SpsSummary { 43 | pub profile_idc: u8, 44 | pub constraint_set_flag: u8, 45 | pub level_idc: u8, 46 | pic_width_in_mbs_minus_1: u64, 47 | pic_height_in_map_units_minus_1: u64, 48 | frame_mbs_only_flag: u8, 49 | frame_crop_left_offset: u64, 50 | frame_crop_right_offset: u64, 51 | frame_crop_top_offset: u64, 52 | frame_crop_bottom_offset: u64, 53 | } 54 | impl SpsSummary { 55 | pub fn width(&self) -> usize { 56 | (self.pic_width_in_mbs_minus_1 as usize + 1) * 16 57 | - (self.frame_crop_right_offset as usize * 2) 58 | - (self.frame_crop_left_offset as usize * 2) 59 | } 60 | 61 | pub fn height(&self) -> usize { 62 | (2 - self.frame_mbs_only_flag as usize) 63 | * ((self.pic_height_in_map_units_minus_1 as usize + 1) * 16) 64 | - (self.frame_crop_bottom_offset as usize * 2) 65 | - (self.frame_crop_top_offset as usize * 2) 66 | } 67 | 68 | pub fn read_from(mut reader: R) -> Result { 69 | let profile_idc = track_io!(reader.read_u8())?; 70 | let constraint_set_flag = track_io!(reader.read_u8())?; 71 | let level_idc = track_io!(reader.read_u8())?; 72 | 73 | let mut reader = AvcBitReader::new(reader); 74 | let _seq_parameter_set_id = track!(reader.read_ue())?; 75 | 76 | match profile_idc { 77 | 100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 => { 78 | track_panic!(ErrorKind::Unsupported, "profile_idc={}", profile_idc) 79 | } 80 | _ => {} 81 | } 82 | 83 | let _log2_max_frame_num_minus4 = track!(reader.read_ue())?; 84 | let pic_order_cnt_type = track!(reader.read_ue())?; 85 | match pic_order_cnt_type { 86 | 0 => { 87 | let _log2_max_pic_order_cnt_lsb_minus4 = track!(reader.read_ue())?; 88 | } 89 | 1 => { 90 | let _delta_pic_order_always_zero_flag = track!(reader.read_bit())?; 91 | let _offset_for_non_ref_pic = track!(reader.read_ue())?; 92 | let _ffset_for_top_to_bottom_field = track!(reader.read_ue())?; 93 | let num_ref_frames_in_pic_order_cnt_cycle = track!(reader.read_ue())?; 94 | for _ in 0..num_ref_frames_in_pic_order_cnt_cycle { 95 | let _offset_for_ref_frame = track!(reader.read_ue())?; 96 | } 97 | } 98 | 2 => {} 99 | _ => track_panic!(ErrorKind::InvalidInput), 100 | } 101 | let _num_ref_frames = track!(reader.read_ue())?; 102 | let _gaps_in_frame_num_value_allowed_flag = track!(reader.read_bit())?; 103 | let pic_width_in_mbs_minus_1 = track!(reader.read_ue())?; 104 | let pic_height_in_map_units_minus_1 = track!(reader.read_ue())?; 105 | let frame_mbs_only_flag = track!(reader.read_bit())?; 106 | if frame_mbs_only_flag == 0 { 107 | let _mb_adaptive_frame_field_flag = track!(reader.read_bit())?; 108 | } 109 | let _direct_8x8_inference_flag = track!(reader.read_bit())?; 110 | let frame_cropping_flag = track!(reader.read_bit())?; 111 | let ( 112 | frame_crop_left_offset, 113 | frame_crop_right_offset, 114 | frame_crop_top_offset, 115 | frame_crop_bottom_offset, 116 | ) = if frame_cropping_flag == 1 { 117 | ( 118 | track!(reader.read_ue())?, 119 | track!(reader.read_ue())?, 120 | track!(reader.read_ue())?, 121 | track!(reader.read_ue())?, 122 | ) 123 | } else { 124 | (0, 0, 0, 0) 125 | }; 126 | 127 | Ok(SpsSummary { 128 | profile_idc, 129 | constraint_set_flag, 130 | level_idc, 131 | pic_width_in_mbs_minus_1, 132 | pic_height_in_map_units_minus_1, 133 | frame_mbs_only_flag, 134 | frame_crop_left_offset, 135 | frame_crop_right_offset, 136 | frame_crop_top_offset, 137 | frame_crop_bottom_offset, 138 | }) 139 | } 140 | } 141 | 142 | #[derive(Debug)] 143 | pub(crate) struct NalUnit { 144 | pub nal_ref_idc: u8, 145 | pub nal_unit_type: NalUnitType, 146 | } 147 | impl NalUnit { 148 | pub fn read_from(mut reader: R) -> Result { 149 | let b = track_io!(reader.read_u8())?; 150 | 151 | let nal_ref_idc = (b >> 5) & 0b11; 152 | let nal_unit_type = track!(NalUnitType::from_u8(b & 0b1_1111))?; 153 | Ok(NalUnit { 154 | nal_ref_idc, 155 | nal_unit_type, 156 | }) 157 | } 158 | } 159 | 160 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 161 | pub(crate) enum NalUnitType { 162 | CodedSliceOfANonIdrPicture = 1, 163 | CodedSliceDataPartitionA = 2, 164 | CodedSliceDataPartitionB = 3, 165 | CodedSliceDataPartitionC = 4, 166 | CodedSliceOfAnIdrPicture = 5, 167 | SupplementalEnhancementInformation = 6, 168 | SequenceParameterSet = 7, 169 | PictureParameterSet = 8, 170 | AccessUnitDelimiter = 9, 171 | EndOfSequence = 10, 172 | EndOfStream = 11, 173 | FilterData = 12, 174 | SequenceParameterSetExtension = 13, 175 | PrefixNalUnit = 14, 176 | SubsetSequenceParameterSet = 15, 177 | CodedSliceOfAnAuxiliaryCodedPictureWithoutPartitioning = 19, 178 | CodedSliceExtension = 20, 179 | } 180 | impl NalUnitType { 181 | fn from_u8(n: u8) -> Result { 182 | Ok(match n { 183 | 1 => NalUnitType::CodedSliceOfANonIdrPicture, 184 | 2 => NalUnitType::CodedSliceDataPartitionA, 185 | 3 => NalUnitType::CodedSliceDataPartitionB, 186 | 4 => NalUnitType::CodedSliceDataPartitionC, 187 | 5 => NalUnitType::CodedSliceOfAnIdrPicture, 188 | 6 => NalUnitType::SupplementalEnhancementInformation, 189 | 7 => NalUnitType::SequenceParameterSet, 190 | 8 => NalUnitType::PictureParameterSet, 191 | 9 => NalUnitType::AccessUnitDelimiter, 192 | 10 => NalUnitType::EndOfSequence, 193 | 11 => NalUnitType::EndOfStream, 194 | 12 => NalUnitType::FilterData, 195 | 13 => NalUnitType::SequenceParameterSetExtension, 196 | 14 => NalUnitType::PrefixNalUnit, 197 | 15 => NalUnitType::SubsetSequenceParameterSet, 198 | 19 => NalUnitType::CodedSliceOfAnAuxiliaryCodedPictureWithoutPartitioning, 199 | 20 => NalUnitType::CodedSliceExtension, 200 | _ => track_panic!(ErrorKind::InvalidInput), 201 | }) 202 | } 203 | } 204 | 205 | #[derive(Debug)] 206 | pub(crate) struct ByteStreamFormatNalUnits<'a> { 207 | bytes: &'a [u8], 208 | } 209 | impl<'a> ByteStreamFormatNalUnits<'a> { 210 | pub fn new(bytes: &'a [u8]) -> Result { 211 | let bytes = if bytes.starts_with(&[0, 0, 1][..]) { 212 | &bytes[3..] 213 | } else if bytes.starts_with(&[0, 0, 0, 1][..]) { 214 | &bytes[4..] 215 | } else { 216 | track_panic!(ErrorKind::InvalidInput); 217 | }; 218 | Ok(ByteStreamFormatNalUnits { bytes }) 219 | } 220 | } 221 | impl<'a> Iterator for ByteStreamFormatNalUnits<'a> { 222 | type Item = &'a [u8]; 223 | fn next(&mut self) -> Option { 224 | if self.bytes.is_empty() { 225 | None 226 | } else { 227 | let mut nal_unit_end = self.bytes.len(); 228 | let mut next_start = self.bytes.len(); 229 | for i in 0..self.bytes.len() { 230 | if (&self.bytes[i..]).starts_with(&[0, 0, 0, 1][..]) { 231 | nal_unit_end = i; 232 | next_start = i + 4; 233 | break; 234 | } else if (&self.bytes[i..]).starts_with(&[0, 0, 1][..]) { 235 | nal_unit_end = i; 236 | next_start = i + 3; 237 | break; 238 | } 239 | } 240 | let nal_unit = &self.bytes[..nal_unit_end]; 241 | self.bytes = &self.bytes[next_start..]; 242 | Some(nal_unit) 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/fmp4/media.rs: -------------------------------------------------------------------------------- 1 | use crate::fmp4::{Mp4Box, AUDIO_TRACK_ID, VIDEO_TRACK_ID}; 2 | use crate::io::{ByteCounter, WriteTo}; 3 | use crate::{ErrorKind, Result}; 4 | use std::io::Write; 5 | 6 | /// [ISO BMFF Byte Stream Format: 4. Media Segments][media_segment] 7 | /// 8 | /// [media_segment]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html#iso-media-segments 9 | #[allow(missing_docs)] 10 | #[derive(Debug, Default)] 11 | pub struct MediaSegment { 12 | pub moof_box: MovieFragmentBox, 13 | pub mdat_boxes: Vec, 14 | } 15 | impl WriteTo for MediaSegment { 16 | fn write_to(&self, mut writer: W) -> Result<()> { 17 | track_assert!(!self.mdat_boxes.is_empty(), ErrorKind::InvalidInput); 18 | write_box!(writer, self.moof_box); 19 | write_boxes!(writer, &self.mdat_boxes); 20 | Ok(()) 21 | } 22 | } 23 | 24 | /// 8.1.1 Media Data Box (ISO/IEC 14496-12). 25 | #[allow(missing_docs)] 26 | #[derive(Debug)] 27 | pub struct MediaDataBox { 28 | pub data: Vec, 29 | } 30 | impl Mp4Box for MediaDataBox { 31 | const BOX_TYPE: [u8; 4] = *b"mdat"; 32 | 33 | fn box_payload_size(&self) -> Result { 34 | Ok(self.data.len() as u32) 35 | } 36 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 37 | write_all!(writer, &self.data); 38 | Ok(()) 39 | } 40 | } 41 | 42 | /// 8.8.4 Movie Fragment Box (ISO/IEC 14496-12). 43 | #[allow(missing_docs)] 44 | #[derive(Debug, Default)] 45 | pub struct MovieFragmentBox { 46 | pub mfhd_box: MovieFragmentHeaderBox, 47 | pub traf_boxes: Vec, 48 | } 49 | impl Mp4Box for MovieFragmentBox { 50 | const BOX_TYPE: [u8; 4] = *b"moof"; 51 | 52 | fn box_payload_size(&self) -> Result { 53 | let mut size = 0; 54 | size += box_size!(self.mfhd_box); 55 | size += boxes_size!(self.traf_boxes); 56 | Ok(size) 57 | } 58 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 59 | track_assert!(!self.traf_boxes.is_empty(), ErrorKind::InvalidInput); 60 | write_box!(writer, self.mfhd_box); 61 | write_boxes!(writer, &self.traf_boxes); 62 | Ok(()) 63 | } 64 | } 65 | 66 | /// 8.8.5 Movie Fragment Header Box (ISO/IEC 14496-12). 67 | #[derive(Debug)] 68 | pub struct MovieFragmentHeaderBox { 69 | /// The number associated with this fragment. 70 | pub sequence_number: u32, 71 | } 72 | impl Mp4Box for MovieFragmentHeaderBox { 73 | const BOX_TYPE: [u8; 4] = *b"mfhd"; 74 | 75 | fn box_version(&self) -> Option { 76 | Some(0) 77 | } 78 | fn box_payload_size(&self) -> Result { 79 | Ok(4) 80 | } 81 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 82 | write_u32!(writer, self.sequence_number); 83 | Ok(()) 84 | } 85 | } 86 | impl Default for MovieFragmentHeaderBox { 87 | /// Return the default value of `MovieFragmentHeaderBox`. 88 | /// 89 | /// This is equivalent to `MovieFragmentHeaderBox { sequence_number: 1 }`. 90 | fn default() -> Self { 91 | MovieFragmentHeaderBox { sequence_number: 1 } 92 | } 93 | } 94 | 95 | /// 8.8.6 Track Fragment Box (ISO/IEC 14496-12). 96 | #[allow(missing_docs)] 97 | #[derive(Debug)] 98 | pub struct TrackFragmentBox { 99 | pub tfhd_box: TrackFragmentHeaderBox, 100 | pub tfdt_box: TrackFragmentBaseMediaDecodeTimeBox, 101 | pub trun_box: TrackRunBox, 102 | } 103 | impl TrackFragmentBox { 104 | /// Makes a new `TrackFragmentBox` instance. 105 | pub fn new(is_video: bool) -> Self { 106 | let track_id = if is_video { 107 | VIDEO_TRACK_ID 108 | } else { 109 | AUDIO_TRACK_ID 110 | }; 111 | TrackFragmentBox { 112 | tfhd_box: TrackFragmentHeaderBox::new(track_id), 113 | tfdt_box: TrackFragmentBaseMediaDecodeTimeBox, 114 | trun_box: TrackRunBox::default(), 115 | } 116 | } 117 | } 118 | impl Mp4Box for TrackFragmentBox { 119 | const BOX_TYPE: [u8; 4] = *b"traf"; 120 | 121 | fn box_payload_size(&self) -> Result { 122 | let mut size = 0; 123 | size += box_size!(self.tfhd_box); 124 | size += box_size!(self.tfdt_box); 125 | size += box_size!(self.trun_box); 126 | Ok(size) 127 | } 128 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 129 | write_box!(writer, self.tfhd_box); 130 | write_box!(writer, self.tfdt_box); 131 | write_box!(writer, self.trun_box); 132 | Ok(()) 133 | } 134 | } 135 | 136 | /// 8.8.7 Track Fragment Header Box (ISO/IEC 14496-12). 137 | #[allow(missing_docs)] 138 | #[derive(Debug)] 139 | pub struct TrackFragmentHeaderBox { 140 | track_id: u32, 141 | pub duration_is_empty: bool, 142 | pub default_base_is_moof: bool, 143 | pub base_data_offset: Option, 144 | pub sample_description_index: Option, 145 | pub default_sample_duration: Option, 146 | pub default_sample_size: Option, 147 | pub default_sample_flags: Option, 148 | } 149 | impl TrackFragmentHeaderBox { 150 | fn new(track_id: u32) -> Self { 151 | TrackFragmentHeaderBox { 152 | track_id, 153 | duration_is_empty: false, 154 | default_base_is_moof: true, 155 | base_data_offset: None, 156 | sample_description_index: None, 157 | default_sample_duration: None, 158 | default_sample_size: None, 159 | default_sample_flags: None, 160 | } 161 | } 162 | } 163 | impl Mp4Box for TrackFragmentHeaderBox { 164 | const BOX_TYPE: [u8; 4] = *b"tfhd"; 165 | 166 | fn box_flags(&self) -> Option { 167 | let flags = self.base_data_offset.is_some() as u32 168 | | (self.sample_description_index.is_some() as u32 * 0x00_0002) 169 | | (self.default_sample_duration.is_some() as u32 * 0x00_0008) 170 | | (self.default_sample_size.is_some() as u32 * 0x00_0010) 171 | | (self.default_sample_flags.is_some() as u32 * 0x00_0020) 172 | | (self.duration_is_empty as u32 * 0x01_0000) 173 | | (self.default_base_is_moof as u32 * 0x02_0000); 174 | Some(flags) 175 | } 176 | fn box_payload_size(&self) -> Result { 177 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 178 | Ok(size as u32) 179 | } 180 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 181 | write_u32!(writer, self.track_id); 182 | if let Some(x) = self.base_data_offset { 183 | write_u64!(writer, x); 184 | } 185 | if let Some(x) = self.sample_description_index { 186 | write_u32!(writer, x); 187 | } 188 | if let Some(x) = self.default_sample_duration { 189 | write_u32!(writer, x); 190 | } 191 | if let Some(x) = self.default_sample_size { 192 | write_u32!(writer, x); 193 | } 194 | if let Some(x) = self.default_sample_flags { 195 | write_u32!(writer, x.to_u32()); 196 | } 197 | Ok(()) 198 | } 199 | } 200 | 201 | /// 8.8.12 Track fragment decode time (ISO/IEC 14496-12). 202 | #[derive(Debug)] 203 | pub struct TrackFragmentBaseMediaDecodeTimeBox; 204 | impl Mp4Box for TrackFragmentBaseMediaDecodeTimeBox { 205 | const BOX_TYPE: [u8; 4] = *b"tfdt"; 206 | 207 | fn box_version(&self) -> Option { 208 | Some(0) 209 | } 210 | fn box_payload_size(&self) -> Result { 211 | Ok(4) 212 | } 213 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 214 | write_u32!(writer, 0); // base_media_decode_time 215 | Ok(()) 216 | } 217 | } 218 | 219 | /// 8.8.8 Track Fragment Run Box (ISO/IEC 14496-12). 220 | #[allow(missing_docs)] 221 | #[derive(Debug, Default)] 222 | pub struct TrackRunBox { 223 | pub data_offset: Option, 224 | pub first_sample_flags: Option, 225 | pub samples: Vec, 226 | } 227 | impl Mp4Box for TrackRunBox { 228 | const BOX_TYPE: [u8; 4] = *b"trun"; 229 | 230 | fn box_version(&self) -> Option { 231 | Some(1) 232 | } 233 | fn box_flags(&self) -> Option { 234 | let sample = self 235 | .samples 236 | .first() 237 | .cloned() 238 | .unwrap_or_else(Sample::default); 239 | let flags = self.data_offset.is_some() as u32 240 | | (self.first_sample_flags.is_some() as u32 * 0x00_0004) 241 | | sample.to_box_flags(); 242 | Some(flags) 243 | } 244 | fn box_payload_size(&self) -> Result { 245 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 246 | Ok(size as u32) 247 | } 248 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 249 | write_u32!(writer, self.samples.len() as u32); 250 | if let Some(x) = self.data_offset { 251 | write_i32!(writer, x); 252 | } 253 | if let Some(x) = self.first_sample_flags { 254 | write_u32!(writer, x.to_u32()); 255 | } 256 | 257 | let mut sample_flags = None; 258 | for sample in &self.samples { 259 | if sample_flags.is_none() { 260 | sample_flags = Some(sample.to_box_flags()); 261 | } 262 | track_assert_eq!( 263 | Some(sample.to_box_flags()), 264 | sample_flags, 265 | ErrorKind::InvalidInput 266 | ); 267 | 268 | if let Some(x) = sample.duration { 269 | write_u32!(writer, x); 270 | } 271 | if let Some(x) = sample.size { 272 | write_u32!(writer, x); 273 | } 274 | if let Some(x) = sample.flags { 275 | write_u32!(writer, x.to_u32()); 276 | } 277 | if let Some(x) = sample.composition_time_offset { 278 | write_i32!(writer, x); 279 | } 280 | } 281 | Ok(()) 282 | } 283 | } 284 | 285 | /// 8.8.8.2 A sample (ISO/IEC 14496-12). 286 | #[allow(missing_docs)] 287 | #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] 288 | pub struct Sample { 289 | pub duration: Option, 290 | pub size: Option, 291 | pub flags: Option, 292 | pub composition_time_offset: Option, 293 | } 294 | impl Sample { 295 | fn to_box_flags(&self) -> u32 { 296 | (self.duration.is_some() as u32 * 0x00_0100) 297 | | (self.size.is_some() as u32 * 0x00_0200) 298 | | (self.flags.is_some() as u32 * 0x00_0400) 299 | | (self.composition_time_offset.is_some() as u32 * 0x00_0800) 300 | } 301 | } 302 | 303 | /// 8.8.8.1 Flags for a sample (ISO/IEC 14496-12). 304 | #[allow(missing_docs)] 305 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 306 | pub struct SampleFlags { 307 | pub is_leading: u8, // u2 308 | pub sample_depends_on: u8, // u2 309 | pub sample_is_depdended_on: u8, // u2 310 | pub sample_has_redundancy: u8, // u2 311 | pub sample_padding_value: u8, // u3 312 | pub sample_is_non_sync_sample: bool, 313 | pub sample_degradation_priority: u16, 314 | } 315 | impl SampleFlags { 316 | fn to_u32(&self) -> u32 { 317 | (u32::from(self.is_leading) << 26) 318 | | (u32::from(self.sample_depends_on) << 24) 319 | | (u32::from(self.sample_is_depdended_on) << 22) 320 | | (u32::from(self.sample_has_redundancy) << 20) 321 | | (u32::from(self.sample_padding_value) << 17) 322 | | ((self.sample_is_non_sync_sample as u32) << 16) 323 | | u32::from(self.sample_degradation_priority) 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/mpeg2_ts.rs: -------------------------------------------------------------------------------- 1 | //! MPEG-2 TS related constituent elements. 2 | use crate::aac::{self, AdtsHeader}; 3 | use crate::avc::{ 4 | AvcDecoderConfigurationRecord, ByteStreamFormatNalUnits, NalUnit, NalUnitType, SpsSummary, 5 | }; 6 | use crate::fmp4::{ 7 | AacSampleEntry, AvcConfigurationBox, AvcSampleEntry, InitializationSegment, MediaDataBox, 8 | MediaSegment, MovieExtendsHeaderBox, Mp4Box, Mpeg4EsDescriptorBox, Sample, SampleEntry, 9 | SampleFlags, TrackBox, TrackExtendsBox, TrackFragmentBox, 10 | }; 11 | use crate::io::ByteCounter; 12 | use crate::{Error, ErrorKind, Result}; 13 | use byteorder::{BigEndian, WriteBytesExt}; 14 | use mpeg2ts; 15 | use mpeg2ts::es::{StreamId, StreamType}; 16 | use mpeg2ts::pes::{PesPacketReader, ReadPesPacket}; 17 | use mpeg2ts::time::Timestamp; 18 | use mpeg2ts::ts::{Pid, ReadTsPacket, TsPacket, TsPayload}; 19 | use std::cmp; 20 | use std::collections::HashMap; 21 | use std::io::Write; 22 | 23 | /// Reads TS packets from `reader`, and converts them into fragmented MP4 segments. 24 | pub fn to_fmp4(reader: R) -> Result<(InitializationSegment, MediaSegment)> { 25 | let (avc_stream, aac_stream) = track!(read_avc_aac_stream(reader))?; 26 | 27 | let initialization_segment = track!(make_initialization_segment(&avc_stream, &aac_stream))?; 28 | let media_segment = track!(make_media_segment(avc_stream, aac_stream))?; 29 | Ok((initialization_segment, media_segment)) 30 | } 31 | 32 | fn make_initialization_segment( 33 | avc_stream: &AvcStream, 34 | aac_stream: &AacStream, 35 | ) -> Result { 36 | let video_duration = track!(avc_stream.duration())?; 37 | let audio_duration = track!(aac_stream.duration())?; 38 | 39 | let mut segment = InitializationSegment::default(); 40 | if video_duration < audio_duration { 41 | segment.moov_box.mvhd_box.timescale = aac::SAMPLES_IN_FRAME as u32; 42 | segment.moov_box.mvhd_box.duration = audio_duration; 43 | segment.moov_box.mvex_box.mehd_box = Some(MovieExtendsHeaderBox { 44 | fragment_duration: audio_duration, 45 | }); 46 | } else { 47 | segment.moov_box.mvhd_box.timescale = Timestamp::RESOLUTION as u32; 48 | segment.moov_box.mvhd_box.duration = video_duration; 49 | segment.moov_box.mvex_box.mehd_box = Some(MovieExtendsHeaderBox { 50 | fragment_duration: video_duration, 51 | }); 52 | } 53 | 54 | // video track 55 | let mut track = TrackBox::new(true); 56 | track.tkhd_box.width = (avc_stream.width as u32) << 16; 57 | track.tkhd_box.height = (avc_stream.height as u32) << 16; 58 | track.tkhd_box.duration = video_duration; 59 | track.edts_box.elst_box.media_time = avc_stream.start_time(); 60 | track.mdia_box.mdhd_box.timescale = Timestamp::RESOLUTION as u32; 61 | track.mdia_box.mdhd_box.duration = video_duration; 62 | 63 | let avc_sample_entry = AvcSampleEntry { 64 | width: avc_stream.width as u16, 65 | height: avc_stream.height as u16, 66 | avcc_box: AvcConfigurationBox { 67 | configuration: avc_stream.configuration.clone(), 68 | }, 69 | }; 70 | track 71 | .mdia_box 72 | .minf_box 73 | .stbl_box 74 | .stsd_box 75 | .sample_entries 76 | .push(SampleEntry::Avc(avc_sample_entry)); 77 | segment.moov_box.trak_boxes.push(track); 78 | segment 79 | .moov_box 80 | .mvex_box 81 | .trex_boxes 82 | .push(TrackExtendsBox::new(true)); 83 | 84 | // audio track 85 | let mut track = TrackBox::new(false); 86 | track.tkhd_box.duration = audio_duration; 87 | track.mdia_box.mdhd_box.timescale = aac_stream.adts_header.sampling_frequency.as_u32(); 88 | track.mdia_box.mdhd_box.duration = audio_duration; 89 | 90 | let aac_sample_entry = AacSampleEntry { 91 | esds_box: Mpeg4EsDescriptorBox { 92 | profile: aac_stream.adts_header.profile, 93 | frequency: aac_stream.adts_header.sampling_frequency, 94 | channel_configuration: aac_stream.adts_header.channel_configuration, 95 | }, 96 | }; 97 | track 98 | .mdia_box 99 | .minf_box 100 | .stbl_box 101 | .stsd_box 102 | .sample_entries 103 | .push(SampleEntry::Aac(aac_sample_entry)); 104 | segment.moov_box.trak_boxes.push(track); 105 | segment 106 | .moov_box 107 | .mvex_box 108 | .trex_boxes 109 | .push(TrackExtendsBox::new(false)); 110 | 111 | Ok(segment) 112 | } 113 | 114 | fn make_media_segment(avc_stream: AvcStream, aac_stream: AacStream) -> Result { 115 | let mut segment = MediaSegment::default(); 116 | 117 | // video traf 118 | let mut traf = TrackFragmentBox::new(true); 119 | traf.tfhd_box.default_sample_flags = Some(SampleFlags { 120 | is_leading: 0, 121 | sample_depends_on: 1, 122 | sample_is_depdended_on: 0, 123 | sample_has_redundancy: 0, 124 | sample_padding_value: 0, 125 | sample_is_non_sync_sample: true, 126 | sample_degradation_priority: 0, 127 | }); 128 | traf.trun_box.data_offset = Some(0); // dummy 129 | traf.trun_box.first_sample_flags = Some(SampleFlags { 130 | is_leading: 0, 131 | sample_depends_on: 2, 132 | sample_is_depdended_on: 0, 133 | sample_has_redundancy: 0, 134 | sample_padding_value: 0, 135 | sample_is_non_sync_sample: false, 136 | sample_degradation_priority: 0, 137 | }); 138 | traf.trun_box.samples = avc_stream.samples; 139 | segment.moof_box.traf_boxes.push(traf); 140 | 141 | // audio traf 142 | let mut traf = TrackFragmentBox::new(false); 143 | traf.tfhd_box.default_sample_duration = Some(aac::SAMPLES_IN_FRAME as u32); 144 | traf.trun_box.data_offset = Some(0); // dummy 145 | traf.trun_box.samples = aac_stream.samples; 146 | segment.moof_box.traf_boxes.push(traf); 147 | 148 | // mdat and offsets adjustment 149 | let mut counter = ByteCounter::with_sink(); 150 | track!(segment.moof_box.write_box(&mut counter))?; 151 | segment.moof_box.traf_boxes[0].trun_box.data_offset = Some(counter.count() as i32 + 8); 152 | 153 | segment.mdat_boxes.push(MediaDataBox { 154 | data: avc_stream.data, 155 | }); 156 | track!(segment.mdat_boxes[0].write_box(&mut counter))?; 157 | 158 | segment.moof_box.traf_boxes[1].trun_box.data_offset = Some(counter.count() as i32 + 8); 159 | segment.mdat_boxes.push(MediaDataBox { 160 | data: aac_stream.data, 161 | }); 162 | 163 | Ok(segment) 164 | } 165 | 166 | #[derive(Debug)] 167 | struct AvcStream { 168 | configuration: AvcDecoderConfigurationRecord, 169 | width: usize, 170 | height: usize, 171 | samples: Vec, 172 | data: Vec, 173 | } 174 | impl AvcStream { 175 | fn duration(&self) -> Result { 176 | let mut duration: u32 = 0; 177 | for sample in &self.samples { 178 | let sample_duration = track_assert_some!(sample.duration, ErrorKind::InvalidInput); 179 | duration = track_assert_some!( 180 | duration.checked_add(sample_duration), 181 | ErrorKind::InvalidInput 182 | ); 183 | } 184 | Ok(duration) 185 | } 186 | fn start_time(&self) -> i32 { 187 | self.samples 188 | .first() 189 | .and_then(|s| s.composition_time_offset) 190 | .unwrap_or(0) 191 | } 192 | } 193 | 194 | #[derive(Debug)] 195 | struct AacStream { 196 | adts_header: AdtsHeader, 197 | samples: Vec, 198 | data: Vec, 199 | } 200 | impl AacStream { 201 | fn duration(&self) -> Result { 202 | let duration = track_assert_some!( 203 | (aac::SAMPLES_IN_FRAME as u32).checked_mul(self.samples.len() as u32), 204 | ErrorKind::InvalidInput 205 | ); 206 | Ok(duration) 207 | } 208 | } 209 | 210 | fn read_avc_aac_stream(ts_reader: R) -> Result<(AvcStream, AacStream)> { 211 | let mut avc_stream: Option = None; 212 | let mut aac_stream: Option = None; 213 | let mut avc_timestamps = Vec::new(); 214 | let mut avc_timestamp_offset = 0; 215 | 216 | let mut reader = PesPacketReader::new(TsPacketReader::new(ts_reader)); 217 | while let Some(pes) = track!(reader.read_pes_packet().map_err(Error::from))? { 218 | let stream_type = track_assert_some!( 219 | reader 220 | .ts_packet_reader() 221 | .get_stream_type(pes.header.stream_id), 222 | ErrorKind::InvalidInput 223 | ); 224 | if pes.header.stream_id.is_video() { 225 | track_assert_eq!(stream_type, StreamType::H264, ErrorKind::Unsupported); 226 | 227 | let pts = track_assert_some!(pes.header.pts, ErrorKind::InvalidInput); 228 | let dts = pes.header.dts.unwrap_or(pts); 229 | 230 | let i = avc_timestamps.len(); 231 | let mut timestamp = pts.as_u64(); 232 | if i == 0 { 233 | avc_timestamp_offset = timestamp; 234 | } 235 | if timestamp < avc_timestamp_offset { 236 | timestamp += Timestamp::MAX; 237 | } 238 | avc_timestamps.push((timestamp - avc_timestamp_offset, i)); 239 | 240 | if avc_stream.is_none() { 241 | let mut sps = None; 242 | let mut pps = None; 243 | let mut sps_summary = None; 244 | for nal_unit in track!(ByteStreamFormatNalUnits::new(&pes.data))? { 245 | let nal_unit_type = track!(NalUnit::read_from(nal_unit))?.nal_unit_type; 246 | match nal_unit_type { 247 | NalUnitType::SequenceParameterSet => { 248 | sps_summary = Some(track!(SpsSummary::read_from(&nal_unit[1..]))?); 249 | sps = Some(nal_unit.to_owned()); 250 | } 251 | NalUnitType::PictureParameterSet => { 252 | pps = Some(nal_unit.to_owned()); 253 | } 254 | _ => {} 255 | } 256 | } 257 | 258 | let sps_summary = track_assert_some!(sps_summary, ErrorKind::InvalidInput); 259 | let sps = track_assert_some!(sps, ErrorKind::InvalidInput); 260 | let pps = track_assert_some!(pps, ErrorKind::InvalidInput); 261 | avc_stream = Some(AvcStream { 262 | configuration: AvcDecoderConfigurationRecord { 263 | profile_idc: sps_summary.profile_idc, 264 | constraint_set_flag: sps_summary.constraint_set_flag, 265 | level_idc: sps_summary.level_idc, 266 | sequence_parameter_set: sps, 267 | picture_parameter_set: pps, 268 | }, 269 | width: sps_summary.width(), 270 | height: sps_summary.height(), 271 | samples: Vec::new(), 272 | data: Vec::new(), 273 | }); 274 | } 275 | 276 | let avc_stream = avc_stream.as_mut().expect("Never fails"); 277 | let prev_data_len = avc_stream.data.len(); 278 | for nal_unit in track!(ByteStreamFormatNalUnits::new(&pes.data))? { 279 | avc_stream 280 | .data 281 | .write_u32::(nal_unit.len() as u32) 282 | .unwrap(); 283 | avc_stream.data.write_all(nal_unit).unwrap(); 284 | } 285 | 286 | let sample_size = (avc_stream.data.len() - prev_data_len) as u32; 287 | let sample_composition_time_offset = (pts.as_u64() as i64 - dts.as_u64() as i64) as i32; 288 | avc_stream.samples.push(Sample { 289 | duration: None, // dummy 290 | size: Some(sample_size), 291 | flags: None, 292 | composition_time_offset: Some(sample_composition_time_offset), 293 | }); 294 | } else { 295 | track_assert!(pes.header.stream_id.is_audio(), ErrorKind::InvalidInput); 296 | track_assert_eq!(stream_type, StreamType::AdtsAac, ErrorKind::Unsupported); 297 | if aac_stream.is_none() { 298 | let adts_header = track!(AdtsHeader::read_from(&pes.data[..]))?; 299 | aac_stream = Some(AacStream { 300 | adts_header, 301 | samples: Vec::new(), 302 | data: Vec::new(), 303 | }); 304 | } 305 | 306 | let aac_stream = aac_stream.as_mut().expect("Never fails"); 307 | let mut bytes = &pes.data[..]; 308 | while !bytes.is_empty() { 309 | let header = track!(AdtsHeader::read_from(&mut bytes))?; 310 | 311 | let sample_size = header.raw_data_blocks_len(); 312 | aac_stream.samples.push(Sample { 313 | duration: None, 314 | size: Some(u32::from(sample_size)), 315 | flags: None, 316 | composition_time_offset: None, 317 | }); 318 | aac_stream 319 | .data 320 | .extend_from_slice(&bytes[..sample_size as usize]); 321 | bytes = &bytes[sample_size as usize..]; 322 | } 323 | } 324 | } 325 | 326 | let mut avc_stream = track_assert_some!(avc_stream, ErrorKind::InvalidInput); 327 | let aac_stream = track_assert_some!(aac_stream, ErrorKind::InvalidInput); 328 | 329 | avc_timestamps.sort(); 330 | for (&(curr, _), &(next, i)) in avc_timestamps.iter().zip(avc_timestamps.iter().skip(1)) { 331 | let duration = next - curr; 332 | avc_stream.samples[i].duration = Some(duration as u32); 333 | } 334 | if !avc_stream.samples.is_empty() { 335 | avc_stream.samples[0].duration = Some(cmp::max(0, avc_stream.start_time()) as u32); 336 | } 337 | 338 | Ok((avc_stream, aac_stream)) 339 | } 340 | 341 | #[derive(Debug)] 342 | struct TsPacketReader { 343 | inner: R, 344 | pid_to_stream_type: HashMap, 345 | stream_id_to_pid: HashMap, 346 | } 347 | impl TsPacketReader { 348 | fn new(inner: R) -> Self { 349 | TsPacketReader { 350 | inner, 351 | pid_to_stream_type: HashMap::new(), 352 | stream_id_to_pid: HashMap::new(), 353 | } 354 | } 355 | fn get_stream_type(&self, stream_id: StreamId) -> Option { 356 | self.stream_id_to_pid 357 | .get(&stream_id) 358 | .and_then(|pid| self.pid_to_stream_type.get(pid)) 359 | .cloned() 360 | } 361 | } 362 | impl ReadTsPacket for TsPacketReader { 363 | fn read_ts_packet(&mut self) -> mpeg2ts::Result> { 364 | if let Some(packet) = track!(self.inner.read_ts_packet())? { 365 | match packet.payload { 366 | Some(TsPayload::Pmt(ref pmt)) => { 367 | for es_info in &pmt.table { 368 | self.pid_to_stream_type 369 | .insert(es_info.elementary_pid, es_info.stream_type); 370 | } 371 | } 372 | Some(TsPayload::Pes(ref pes)) => { 373 | if self.pid_to_stream_type.contains_key(&packet.header.pid) { 374 | self.stream_id_to_pid 375 | .insert(pes.header.stream_id, packet.header.pid); 376 | } 377 | } 378 | _ => {} 379 | } 380 | Ok(Some(packet)) 381 | } else { 382 | Ok(None) 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/fmp4/initialization.rs: -------------------------------------------------------------------------------- 1 | use crate::aac::{AacProfile, ChannelConfiguration, SamplingFrequency}; 2 | use crate::avc::AvcDecoderConfigurationRecord; 3 | use crate::fmp4::{Mp4Box, AUDIO_TRACK_ID, VIDEO_TRACK_ID}; 4 | use crate::io::{ByteCounter, WriteTo}; 5 | use crate::{ErrorKind, Result}; 6 | use std::ffi::CString; 7 | use std::io::Write; 8 | 9 | /// [3. Initialization Segments][init_segment] (ISO BMFF Byte Stream Format) 10 | /// 11 | /// [init_segment]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html#iso-init-segments 12 | #[allow(missing_docs)] 13 | #[derive(Debug, Default)] 14 | pub struct InitializationSegment { 15 | pub ftyp_box: FileTypeBox, 16 | pub moov_box: MovieBox, 17 | } 18 | impl InitializationSegment { 19 | /// Returns MIME type. 20 | pub fn mime_type(&self) -> String { 21 | // FIXME 22 | r#"video/mp4; "avc1.640029, mp4a.40.2""#.to_string() 23 | } 24 | } 25 | impl WriteTo for InitializationSegment { 26 | fn write_to(&self, mut writer: W) -> Result<()> { 27 | write_box!(writer, self.ftyp_box); 28 | write_box!(writer, self.moov_box); 29 | Ok(()) 30 | } 31 | } 32 | 33 | /// 4.3 File Type Box (ISO/IEC 14496-12). 34 | #[allow(missing_docs)] 35 | #[derive(Debug, Default)] 36 | pub struct FileTypeBox; 37 | impl Mp4Box for FileTypeBox { 38 | const BOX_TYPE: [u8; 4] = *b"ftyp"; 39 | 40 | fn box_payload_size(&self) -> Result { 41 | Ok(8) 42 | } 43 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 44 | write_all!(writer, b"isom"); // major_brand 45 | write_u32!(writer, 512); // minor_version 46 | Ok(()) 47 | } 48 | } 49 | 50 | /// 8.2.1 Movie Box (ISO/IEC 14496-12). 51 | #[allow(missing_docs)] 52 | #[derive(Debug, Default)] 53 | pub struct MovieBox { 54 | pub mvhd_box: MovieHeaderBox, 55 | pub trak_boxes: Vec, 56 | pub mvex_box: MovieExtendsBox, 57 | } 58 | impl Mp4Box for MovieBox { 59 | const BOX_TYPE: [u8; 4] = *b"moov"; 60 | 61 | fn box_payload_size(&self) -> Result { 62 | let mut size = 0; 63 | size += box_size!(self.mvhd_box); 64 | size += boxes_size!(self.trak_boxes); 65 | size += box_size!(self.mvex_box); 66 | Ok(size) 67 | } 68 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 69 | track_assert!(!self.trak_boxes.is_empty(), ErrorKind::InvalidInput); 70 | write_box!(writer, self.mvhd_box); 71 | write_boxes!(writer, &self.trak_boxes); 72 | write_box!(writer, &self.mvex_box); 73 | Ok(()) 74 | } 75 | } 76 | 77 | /// 8.8.1 Movie Extends Box (ISO/IEC 14496-12). 78 | #[allow(missing_docs)] 79 | #[derive(Debug, Default)] 80 | pub struct MovieExtendsBox { 81 | pub mehd_box: Option, 82 | pub trex_boxes: Vec, 83 | } 84 | impl Mp4Box for MovieExtendsBox { 85 | const BOX_TYPE: [u8; 4] = *b"mvex"; 86 | 87 | fn box_payload_size(&self) -> Result { 88 | let mut size = 0; 89 | size += optional_box_size!(self.mehd_box); 90 | size += boxes_size!(self.trex_boxes); 91 | Ok(size) 92 | } 93 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 94 | track_assert!(!self.trex_boxes.is_empty(), ErrorKind::InvalidInput); 95 | if let Some(mehd_box) = &self.mehd_box { 96 | write_box!(writer, mehd_box); 97 | } 98 | write_boxes!(writer, &self.trex_boxes); 99 | Ok(()) 100 | } 101 | } 102 | 103 | /// 8.8.2 Movie Extends Header Box (ISO/IEC 14496-12). 104 | #[allow(missing_docs)] 105 | #[derive(Debug, Default)] 106 | pub struct MovieExtendsHeaderBox { 107 | pub fragment_duration: u32, 108 | } 109 | impl Mp4Box for MovieExtendsHeaderBox { 110 | const BOX_TYPE: [u8; 4] = *b"mehd"; 111 | 112 | fn box_version(&self) -> Option { 113 | Some(0) 114 | } 115 | fn box_payload_size(&self) -> Result { 116 | Ok(4) 117 | } 118 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 119 | write_u32!(writer, self.fragment_duration); 120 | Ok(()) 121 | } 122 | } 123 | 124 | /// 8.8.3 Track Extends Box (ISO/IEC 14496-12). 125 | #[allow(missing_docs)] 126 | #[derive(Debug)] 127 | pub struct TrackExtendsBox { 128 | track_id: u32, 129 | default_sample_description_index: u32, 130 | pub default_sample_duration: u32, 131 | pub default_sample_size: u32, 132 | pub default_sample_flags: u32, 133 | } 134 | impl TrackExtendsBox { 135 | /// Makes a new `TrackExtendsBox` instance. 136 | pub fn new(is_video: bool) -> Self { 137 | TrackExtendsBox { 138 | track_id: if is_video { 139 | VIDEO_TRACK_ID 140 | } else { 141 | AUDIO_TRACK_ID 142 | }, 143 | default_sample_description_index: 1, 144 | default_sample_duration: 0, 145 | default_sample_size: 0, 146 | default_sample_flags: 0, 147 | } 148 | } 149 | } 150 | impl Mp4Box for TrackExtendsBox { 151 | const BOX_TYPE: [u8; 4] = *b"trex"; 152 | 153 | fn box_version(&self) -> Option { 154 | Some(0) 155 | } 156 | fn box_payload_size(&self) -> Result { 157 | Ok(4 * 5) 158 | } 159 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 160 | write_u32!(writer, self.track_id); 161 | write_u32!(writer, self.default_sample_description_index); 162 | write_u32!(writer, self.default_sample_duration); 163 | write_u32!(writer, self.default_sample_size); 164 | write_u32!(writer, self.default_sample_flags); 165 | Ok(()) 166 | } 167 | } 168 | 169 | /// 8.2.2 Movie Header Box (ISO/IEC 14496-12). 170 | #[allow(missing_docs)] 171 | #[derive(Debug)] 172 | pub struct MovieHeaderBox { 173 | pub timescale: u32, 174 | pub duration: u32, 175 | } 176 | impl Default for MovieHeaderBox { 177 | fn default() -> Self { 178 | MovieHeaderBox { 179 | timescale: 1, 180 | duration: 1, 181 | } 182 | } 183 | } 184 | impl Mp4Box for MovieHeaderBox { 185 | const BOX_TYPE: [u8; 4] = *b"mvhd"; 186 | 187 | fn box_version(&self) -> Option { 188 | Some(0) 189 | } 190 | fn box_payload_size(&self) -> Result { 191 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 192 | Ok(size as u32) 193 | } 194 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 195 | write_u32!(writer, 0); // creation_time 196 | write_u32!(writer, 0); // modification_time 197 | write_u32!(writer, self.timescale); 198 | write_u32!(writer, self.duration); 199 | write_i32!(writer, 0x1_0000); // rate 200 | write_i16!(writer, 256); // volume 201 | write_zeroes!(writer, 2); 202 | write_zeroes!(writer, 4 * 2); 203 | for &x in &[0x1_0000, 0, 0, 0, 0x1_0000, 0, 0, 0, 0x4000_0000] { 204 | write_i32!(writer, x); // matrix 205 | } 206 | write_zeroes!(writer, 4 * 6); 207 | write_u32!(writer, 0xFFFF_FFFF); // next_track_id 208 | Ok(()) 209 | } 210 | } 211 | 212 | /// 8.3.1 Track Box (ISO/IEC 14496-12). 213 | #[allow(missing_docs)] 214 | #[derive(Debug)] 215 | pub struct TrackBox { 216 | pub tkhd_box: TrackHeaderBox, 217 | pub edts_box: EditBox, 218 | pub mdia_box: MediaBox, 219 | } 220 | impl TrackBox { 221 | /// Makes a new `TrackBox` instance. 222 | pub fn new(is_video: bool) -> Self { 223 | TrackBox { 224 | tkhd_box: TrackHeaderBox::new(is_video), 225 | edts_box: EditBox::default(), 226 | mdia_box: MediaBox::new(is_video), 227 | } 228 | } 229 | } 230 | impl Mp4Box for TrackBox { 231 | const BOX_TYPE: [u8; 4] = *b"trak"; 232 | 233 | fn box_payload_size(&self) -> Result { 234 | let mut size = 0; 235 | size += box_size!(self.tkhd_box); 236 | size += box_size!(self.edts_box); 237 | size += box_size!(self.mdia_box); 238 | Ok(size) 239 | } 240 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 241 | write_box!(writer, self.tkhd_box); 242 | write_box!(writer, self.edts_box); 243 | write_box!(writer, self.mdia_box); 244 | Ok(()) 245 | } 246 | } 247 | 248 | /// 8.3.2 Track Header Box (ISO/IEC 14496-12). 249 | #[allow(missing_docs)] 250 | #[derive(Debug)] 251 | pub struct TrackHeaderBox { 252 | track_id: u32, 253 | pub duration: u32, 254 | volume: i16, // fixed point 8.8 255 | pub width: u32, // fixed point 16.16 256 | pub height: u32, // fixed point 16.16 257 | } 258 | impl TrackHeaderBox { 259 | fn new(is_video: bool) -> Self { 260 | TrackHeaderBox { 261 | track_id: if is_video { 262 | VIDEO_TRACK_ID 263 | } else { 264 | AUDIO_TRACK_ID 265 | }, 266 | duration: 1, 267 | volume: if is_video { 0 } else { 256 }, 268 | width: 0, 269 | height: 0, 270 | } 271 | } 272 | } 273 | impl Mp4Box for TrackHeaderBox { 274 | const BOX_TYPE: [u8; 4] = *b"tkhd"; 275 | 276 | fn box_version(&self) -> Option { 277 | Some(0) 278 | } 279 | fn box_flags(&self) -> Option { 280 | // track_enabled | track_in_movie | track_in_preview 281 | let flags = 0x00_0001 | 0x00_0002 | 0x00_0004; 282 | Some(flags) 283 | } 284 | fn box_payload_size(&self) -> Result { 285 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 286 | Ok(size as u32) 287 | } 288 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 289 | write_u32!(writer, 0); // creation_time 290 | write_u32!(writer, 0); // modification_time 291 | write_u32!(writer, self.track_id); 292 | write_zeroes!(writer, 4); 293 | write_u32!(writer, self.duration); 294 | write_zeroes!(writer, 4 * 2); 295 | write_i16!(writer, 0); // layer 296 | write_i16!(writer, 0); // alternate_group 297 | write_i16!(writer, self.volume); 298 | write_zeroes!(writer, 2); 299 | for &x in &[0x1_0000, 0, 0, 0, 0x1_0000, 0, 0, 0, 0x4000_0000] { 300 | write_i32!(writer, x); // matrix 301 | } 302 | write_u32!(writer, self.width); 303 | write_u32!(writer, self.height); 304 | Ok(()) 305 | } 306 | } 307 | 308 | /// 8.6.5 Edit Box (ISO/IEC 14496-12). 309 | #[allow(missing_docs)] 310 | #[derive(Debug, Default)] 311 | pub struct EditBox { 312 | pub elst_box: EditListBox, 313 | } 314 | impl Mp4Box for EditBox { 315 | const BOX_TYPE: [u8; 4] = *b"edts"; 316 | 317 | fn box_payload_size(&self) -> Result { 318 | Ok(box_size!(self.elst_box)) 319 | } 320 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 321 | write_box!(writer, self.elst_box); 322 | Ok(()) 323 | } 324 | } 325 | 326 | /// 8.6.6 Edit List Box (ISO/IEC 14496-12). 327 | #[allow(missing_docs)] 328 | #[derive(Debug, Default)] 329 | pub struct EditListBox { 330 | pub media_time: i32, 331 | } 332 | impl Mp4Box for EditListBox { 333 | const BOX_TYPE: [u8; 4] = *b"elst"; 334 | 335 | fn box_version(&self) -> Option { 336 | Some(0) 337 | } 338 | fn box_payload_size(&self) -> Result { 339 | Ok(4 + 4 + 4 + 2 + 2) 340 | } 341 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 342 | write_u32!(writer, 1); // entry_count 343 | write_u32!(writer, 0); // segment_duration ("0" indicating that it spans all subsequent media) 344 | write_i32!(writer, self.media_time); 345 | write_i16!(writer, 1); // media_rate_integer 346 | write_i16!(writer, 0); // media_rate_fraction 347 | Ok(()) 348 | } 349 | } 350 | 351 | /// 8.4.1 Media Box (ISO/IEC 14496-12). 352 | #[allow(missing_docs)] 353 | #[derive(Debug)] 354 | pub struct MediaBox { 355 | pub mdhd_box: MediaHeaderBox, 356 | pub hdlr_box: HandlerReferenceBox, 357 | pub minf_box: MediaInformationBox, 358 | } 359 | impl MediaBox { 360 | fn new(is_video: bool) -> Self { 361 | MediaBox { 362 | mdhd_box: MediaHeaderBox::default(), 363 | hdlr_box: HandlerReferenceBox::new(is_video), 364 | minf_box: MediaInformationBox::new(is_video), 365 | } 366 | } 367 | } 368 | impl Mp4Box for MediaBox { 369 | const BOX_TYPE: [u8; 4] = *b"mdia"; 370 | 371 | fn box_payload_size(&self) -> Result { 372 | let mut size = 0; 373 | size += box_size!(self.mdhd_box); 374 | size += box_size!(self.hdlr_box); 375 | size += box_size!(self.minf_box); 376 | Ok(size) 377 | } 378 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 379 | write_box!(writer, self.mdhd_box); 380 | write_box!(writer, self.hdlr_box); 381 | write_box!(writer, self.minf_box); 382 | Ok(()) 383 | } 384 | } 385 | 386 | /// 8.4.2 Media Header Box (ISO/IEC 14496-12). 387 | #[allow(missing_docs)] 388 | #[derive(Debug)] 389 | pub struct MediaHeaderBox { 390 | pub timescale: u32, 391 | pub duration: u32, 392 | } 393 | impl Default for MediaHeaderBox { 394 | fn default() -> Self { 395 | MediaHeaderBox { 396 | timescale: 0, 397 | duration: 1, 398 | } 399 | } 400 | } 401 | impl Mp4Box for MediaHeaderBox { 402 | const BOX_TYPE: [u8; 4] = *b"mdhd"; 403 | 404 | fn box_version(&self) -> Option { 405 | Some(0) 406 | } 407 | fn box_payload_size(&self) -> Result { 408 | Ok(4 + 4 + 4 + 4 + 2 + 2) 409 | } 410 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 411 | write_u32!(writer, 0); // creation_time 412 | write_u32!(writer, 0); // modification_time 413 | write_u32!(writer, self.timescale); 414 | write_u32!(writer, self.duration); 415 | write_u16!(writer, 0x55c4); // language 416 | write_zeroes!(writer, 2); 417 | Ok(()) 418 | } 419 | } 420 | 421 | /// 8.4.3 Handler Reference Box (ISO/IEC 14496-12). 422 | #[derive(Debug)] 423 | pub struct HandlerReferenceBox { 424 | handler_type: [u8; 4], 425 | name: CString, 426 | } 427 | impl HandlerReferenceBox { 428 | fn new(is_video: bool) -> Self { 429 | let name = if is_video { 430 | "Video Handler" 431 | } else { 432 | "Sound Handler" 433 | }; 434 | HandlerReferenceBox { 435 | handler_type: if is_video { *b"vide" } else { *b"soun" }, 436 | name: CString::new(name).expect("Never fails"), 437 | } 438 | } 439 | } 440 | impl Mp4Box for HandlerReferenceBox { 441 | const BOX_TYPE: [u8; 4] = *b"hdlr"; 442 | 443 | fn box_version(&self) -> Option { 444 | Some(0) 445 | } 446 | fn box_payload_size(&self) -> Result { 447 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 448 | Ok(size as u32) 449 | } 450 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 451 | write_zeroes!(writer, 4); 452 | write_all!(writer, &self.handler_type); 453 | write_zeroes!(writer, 4 * 3); 454 | write_all!(writer, self.name.as_bytes_with_nul()); 455 | Ok(()) 456 | } 457 | } 458 | 459 | /// 8.4.4 Media Information Box (ISO/IEC 14496-12). 460 | #[allow(missing_docs)] 461 | #[derive(Debug)] 462 | pub struct MediaInformationBox { 463 | pub vmhd_box: Option, 464 | pub smhd_box: Option, 465 | pub dinf_box: DataInformationBox, 466 | pub stbl_box: SampleTableBox, 467 | } 468 | impl MediaInformationBox { 469 | fn new(is_video: bool) -> Self { 470 | MediaInformationBox { 471 | vmhd_box: if is_video { 472 | Some(VideoMediaHeaderBox) 473 | } else { 474 | None 475 | }, 476 | smhd_box: if !is_video { 477 | Some(SoundMediaHeaderBox) 478 | } else { 479 | None 480 | }, 481 | dinf_box: DataInformationBox::default(), 482 | stbl_box: SampleTableBox::default(), 483 | } 484 | } 485 | } 486 | impl Mp4Box for MediaInformationBox { 487 | const BOX_TYPE: [u8; 4] = *b"minf"; 488 | 489 | fn box_payload_size(&self) -> Result { 490 | let mut size = 0; 491 | size += optional_box_size!(self.vmhd_box); 492 | size += optional_box_size!(self.smhd_box); 493 | size += box_size!(self.dinf_box); 494 | size += box_size!(self.stbl_box); 495 | Ok(size) 496 | } 497 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 498 | if let Some(ref x) = self.vmhd_box { 499 | write_box!(writer, x); 500 | } 501 | if let Some(ref x) = self.smhd_box { 502 | write_box!(writer, x); 503 | } 504 | write_box!(writer, self.dinf_box); 505 | write_box!(writer, self.stbl_box); 506 | Ok(()) 507 | } 508 | } 509 | 510 | /// 12.1.2 Video media header (ISO/IEC 14496-12). 511 | #[derive(Debug)] 512 | pub struct VideoMediaHeaderBox; 513 | impl Mp4Box for VideoMediaHeaderBox { 514 | const BOX_TYPE: [u8; 4] = *b"vmhd"; 515 | 516 | fn box_flags(&self) -> Option { 517 | Some(1) 518 | } 519 | fn box_payload_size(&self) -> Result { 520 | Ok(2 + 2 * 3) 521 | } 522 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 523 | write_u16!(writer, 0); // graphicsmode 524 | write_zeroes!(writer, 2 * 3); // opcolor 525 | Ok(()) 526 | } 527 | } 528 | 529 | /// 12.2.2 Sound media header (ISO/IEC 14496-12). 530 | #[derive(Debug)] 531 | pub struct SoundMediaHeaderBox; 532 | impl Mp4Box for SoundMediaHeaderBox { 533 | const BOX_TYPE: [u8; 4] = *b"smhd"; 534 | 535 | fn box_version(&self) -> Option { 536 | Some(0) 537 | } 538 | fn box_payload_size(&self) -> Result { 539 | Ok(2 + 2) 540 | } 541 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 542 | write_i16!(writer, 0); // balance 543 | write_zeroes!(writer, 2); 544 | Ok(()) 545 | } 546 | } 547 | 548 | /// 8.7.1 Data Information Box (ISO/IEC 14496-12). 549 | #[allow(missing_docs)] 550 | #[derive(Debug, Default)] 551 | pub struct DataInformationBox { 552 | pub dref_box: DataReferenceBox, 553 | } 554 | impl Mp4Box for DataInformationBox { 555 | const BOX_TYPE: [u8; 4] = *b"dinf"; 556 | 557 | fn box_payload_size(&self) -> Result { 558 | Ok(box_size!(self.dref_box)) 559 | } 560 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 561 | write_box!(writer, self.dref_box); 562 | Ok(()) 563 | } 564 | } 565 | 566 | /// 8.7.2 Data Reference Box (ISO/IEC 14496-12). 567 | #[allow(missing_docs)] 568 | #[derive(Debug, Default)] 569 | pub struct DataReferenceBox { 570 | pub url_box: DataEntryUrlBox, 571 | } 572 | impl Mp4Box for DataReferenceBox { 573 | const BOX_TYPE: [u8; 4] = *b"dref"; 574 | 575 | fn box_version(&self) -> Option { 576 | Some(0) 577 | } 578 | fn box_payload_size(&self) -> Result { 579 | let mut size = 4; 580 | size += box_size!(self.url_box); 581 | Ok(size) 582 | } 583 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 584 | write_u32!(writer, 1); // entry_count 585 | write_box!(writer, self.url_box); 586 | Ok(()) 587 | } 588 | } 589 | 590 | /// 8.7.2.2 Data Entry Url Box (ISO/IEC 14496-12). 591 | #[derive(Debug, Default)] 592 | pub struct DataEntryUrlBox; 593 | impl Mp4Box for DataEntryUrlBox { 594 | const BOX_TYPE: [u8; 4] = *b"url "; 595 | 596 | fn box_flags(&self) -> Option { 597 | Some(0x00_0001) 598 | } 599 | fn box_payload_size(&self) -> Result { 600 | Ok(0) 601 | } 602 | fn write_box_payload(&self, _writer: W) -> Result<()> { 603 | // NOTE: null location 604 | Ok(()) 605 | } 606 | } 607 | 608 | /// 8.5.1 Sample Table Box (ISO/IEC 14496-12). 609 | #[allow(missing_docs)] 610 | #[derive(Debug, Default)] 611 | pub struct SampleTableBox { 612 | pub stsd_box: SampleDescriptionBox, 613 | pub stts_box: TimeToSampleBox, 614 | pub stsc_box: SampleToChunkBox, 615 | pub stsz_box: SampleSizeBox, 616 | pub stco_box: ChunkOffsetBox, 617 | } 618 | impl Mp4Box for SampleTableBox { 619 | const BOX_TYPE: [u8; 4] = *b"stbl"; 620 | 621 | fn box_payload_size(&self) -> Result { 622 | let mut size = 0; 623 | size += box_size!(self.stsd_box); 624 | size += box_size!(self.stts_box); 625 | size += box_size!(self.stsc_box); 626 | size += box_size!(self.stsz_box); 627 | size += box_size!(self.stco_box); 628 | Ok(size) 629 | } 630 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 631 | write_box!(writer, self.stsd_box); 632 | write_box!(writer, self.stts_box); 633 | write_box!(writer, self.stsc_box); 634 | write_box!(writer, self.stsz_box); 635 | write_box!(writer, self.stco_box); 636 | Ok(()) 637 | } 638 | } 639 | 640 | /// 8.5.2 Sample Description Box (ISO/IEC 14496-12). 641 | #[allow(missing_docs)] 642 | #[derive(Debug, Default)] 643 | pub struct SampleDescriptionBox { 644 | pub sample_entries: Vec, 645 | } 646 | impl Mp4Box for SampleDescriptionBox { 647 | const BOX_TYPE: [u8; 4] = *b"stsd"; 648 | 649 | fn box_version(&self) -> Option { 650 | Some(0) 651 | } 652 | fn box_payload_size(&self) -> Result { 653 | let mut size = 4; 654 | size += boxes_size!(self.sample_entries); 655 | Ok(size) 656 | } 657 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 658 | track_assert!(!self.sample_entries.is_empty(), ErrorKind::InvalidInput); 659 | write_u32!(writer, self.sample_entries.len() as u32); 660 | write_boxes!(writer, &self.sample_entries); 661 | Ok(()) 662 | } 663 | } 664 | 665 | /// 8.5.3 Sample Size Boxes (ISO/IEC 14496-12). 666 | #[derive(Debug, Default)] 667 | pub struct SampleSizeBox; 668 | impl Mp4Box for SampleSizeBox { 669 | const BOX_TYPE: [u8; 4] = *b"stsz"; 670 | 671 | fn box_version(&self) -> Option { 672 | Some(0) 673 | } 674 | fn box_payload_size(&self) -> Result { 675 | Ok(4 + 4) 676 | } 677 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 678 | write_u32!(writer, 0); 679 | write_u32!(writer, 0); 680 | Ok(()) 681 | } 682 | } 683 | 684 | /// 8.6.1.2 Decoding Time To Sample Box (ISO/IEC 14496-12). 685 | #[derive(Debug, Default)] 686 | pub struct TimeToSampleBox; 687 | impl Mp4Box for TimeToSampleBox { 688 | const BOX_TYPE: [u8; 4] = *b"stts"; 689 | 690 | fn box_version(&self) -> Option { 691 | Some(0) 692 | } 693 | fn box_payload_size(&self) -> Result { 694 | Ok(4) 695 | } 696 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 697 | write_u32!(writer, 0); 698 | Ok(()) 699 | } 700 | } 701 | 702 | /// 8.7.5 Chunk Offset Box (ISO/IEC 14496-12). 703 | #[derive(Debug, Default)] 704 | pub struct ChunkOffsetBox; 705 | impl Mp4Box for ChunkOffsetBox { 706 | const BOX_TYPE: [u8; 4] = *b"stco"; 707 | 708 | fn box_version(&self) -> Option { 709 | Some(0) 710 | } 711 | fn box_payload_size(&self) -> Result { 712 | Ok(4) 713 | } 714 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 715 | write_u32!(writer, 0); 716 | Ok(()) 717 | } 718 | } 719 | 720 | /// 8.7.4 Sample To Chunk Box (ISO/IEC 14496-12). 721 | #[derive(Debug, Default)] 722 | pub struct SampleToChunkBox; 723 | impl Mp4Box for SampleToChunkBox { 724 | const BOX_TYPE: [u8; 4] = *b"stsc"; 725 | 726 | fn box_version(&self) -> Option { 727 | Some(0) 728 | } 729 | fn box_payload_size(&self) -> Result { 730 | Ok(4) 731 | } 732 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 733 | write_u32!(writer, 0); 734 | Ok(()) 735 | } 736 | } 737 | 738 | /// 8.5.2.2 Sample Entry (ISO/IEC 14496-12). 739 | #[allow(missing_docs)] 740 | #[derive(Debug)] 741 | pub enum SampleEntry { 742 | Avc(AvcSampleEntry), 743 | Aac(AacSampleEntry), 744 | } 745 | impl SampleEntry { 746 | fn box_size(&self) -> Result { 747 | match *self { 748 | SampleEntry::Avc(ref x) => track!(x.box_size()), 749 | SampleEntry::Aac(ref x) => track!(x.box_size()), 750 | } 751 | } 752 | fn write_box(&self, writer: W) -> Result<()> { 753 | match *self { 754 | SampleEntry::Avc(ref x) => track!(x.write_box(writer)), 755 | SampleEntry::Aac(ref x) => track!(x.write_box(writer)), 756 | } 757 | } 758 | } 759 | 760 | /// Sample Entry for AVC. 761 | #[allow(missing_docs)] 762 | #[derive(Debug)] 763 | pub struct AvcSampleEntry { 764 | pub width: u16, 765 | pub height: u16, 766 | pub avcc_box: AvcConfigurationBox, 767 | } 768 | impl AvcSampleEntry { 769 | fn write_box_payload_without_avcc(&self, mut writer: W) -> Result<()> { 770 | write_zeroes!(writer, 6); 771 | write_u16!(writer, 1); // data_reference_index 772 | 773 | write_zeroes!(writer, 16); 774 | write_u16!(writer, self.width); 775 | write_u16!(writer, self.height); 776 | write_u32!(writer, 0x0048_0000); 777 | write_u32!(writer, 0x0048_0000); 778 | write_zeroes!(writer, 4); 779 | write_u16!(writer, 1); 780 | write_zeroes!(writer, 32); 781 | write_u16!(writer, 0x0018); 782 | write_i16!(writer, -1); 783 | Ok(()) 784 | } 785 | } 786 | impl Mp4Box for AvcSampleEntry { 787 | const BOX_TYPE: [u8; 4] = *b"avc1"; 788 | 789 | fn box_payload_size(&self) -> Result { 790 | let mut size = 0; 791 | size += track!(ByteCounter::calculate( 792 | |w| self.write_box_payload_without_avcc(w) 793 | ))? as u32; 794 | size += box_size!(self.avcc_box); 795 | Ok(size) 796 | } 797 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 798 | track!(self.write_box_payload_without_avcc(&mut writer))?; 799 | write_box!(writer, self.avcc_box); 800 | Ok(()) 801 | } 802 | } 803 | 804 | /// Box that contains AVC Decoder Configuration Record. 805 | #[allow(missing_docs)] 806 | #[derive(Debug)] 807 | pub struct AvcConfigurationBox { 808 | pub configuration: AvcDecoderConfigurationRecord, 809 | } 810 | impl Mp4Box for AvcConfigurationBox { 811 | const BOX_TYPE: [u8; 4] = *b"avcC"; 812 | 813 | fn box_payload_size(&self) -> Result { 814 | let size = track!(ByteCounter::calculate(|w| self.configuration.write_to(w)))?; 815 | Ok(size as u32) 816 | } 817 | fn write_box_payload(&self, writer: W) -> Result<()> { 818 | track!(self.configuration.write_to(writer)) 819 | } 820 | } 821 | 822 | /// Sample Entry for AAC. 823 | #[allow(missing_docs)] 824 | #[derive(Debug)] 825 | pub struct AacSampleEntry { 826 | pub esds_box: Mpeg4EsDescriptorBox, 827 | } 828 | impl AacSampleEntry { 829 | fn write_box_payload_without_esds(&self, mut writer: W) -> Result<()> { 830 | write_zeroes!(writer, 6); 831 | write_u16!(writer, 1); // data_reference_index 832 | 833 | let channels = self.esds_box.channel_configuration as u16; 834 | let sample_rate = self.esds_box.frequency.as_u32(); 835 | write_zeroes!(writer, 8); 836 | track_assert!(channels == 1 || channels == 2, ErrorKind::Unsupported); 837 | track_assert!(sample_rate <= 0xFFFF, ErrorKind::InvalidInput); 838 | 839 | write_u16!(writer, channels); 840 | write_u16!(writer, 16); 841 | write_zeroes!(writer, 4); 842 | write_u16!(writer, sample_rate as u16); 843 | write_zeroes!(writer, 2); 844 | Ok(()) 845 | } 846 | } 847 | impl Mp4Box for AacSampleEntry { 848 | const BOX_TYPE: [u8; 4] = *b"mp4a"; 849 | 850 | fn box_payload_size(&self) -> Result { 851 | let mut size = 0; 852 | size += track!(ByteCounter::calculate( 853 | |w| self.write_box_payload_without_esds(w) 854 | ))? as u32; 855 | size += box_size!(self.esds_box); 856 | Ok(size) 857 | } 858 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 859 | track!(self.write_box_payload_without_esds(&mut writer))?; 860 | write_box!(writer, self.esds_box); 861 | Ok(()) 862 | } 863 | } 864 | 865 | /// MPEG-4 ES Description Box (ISO/IEC 14496-1). 866 | #[allow(missing_docs)] 867 | #[derive(Debug)] 868 | pub struct Mpeg4EsDescriptorBox { 869 | pub profile: AacProfile, 870 | pub frequency: SamplingFrequency, 871 | pub channel_configuration: ChannelConfiguration, 872 | } 873 | impl Mp4Box for Mpeg4EsDescriptorBox { 874 | const BOX_TYPE: [u8; 4] = *b"esds"; 875 | 876 | fn box_version(&self) -> Option { 877 | Some(0) 878 | } 879 | fn box_payload_size(&self) -> Result { 880 | let size = track!(ByteCounter::calculate(|w| self.write_box_payload(w)))?; 881 | Ok(size as u32) 882 | } 883 | fn write_box_payload(&self, mut writer: W) -> Result<()> { 884 | // es descriptor 885 | write_u8!(writer, 0x03); // descriptor_tag=es 886 | write_u8!(writer, 25); // descriptor_len 887 | write_u16!(writer, 0); // es_id 888 | write_u8!(writer, 0); // stream_priority and flags 889 | 890 | // decoder configuration descriptor 891 | write_u8!(writer, 0x04); // descriptor_tag=decoder_configuration 892 | write_u8!(writer, 17); // descriptor_len 893 | 894 | write_u8!(writer, 0x40); // object_type 895 | write_u8!(writer, (5 << 2) | 1); // stream_type=audio=5, upstream=0, reserved=1 896 | write_u24!(writer, 0); // buffer_size 897 | write_u32!(writer, 0); // max_bitrate 898 | write_u32!(writer, 0); // avg_bitrate 899 | 900 | // decoder specific info 901 | write_u8!(writer, 0x05); // descriptor_tag=decoder_specific_info 902 | write_u8!(writer, 2); // descriptor_len 903 | write_u16!( 904 | writer, 905 | ((self.profile as u16 + 1) << 11) 906 | | (u16::from(self.frequency.as_index()) << 7) 907 | | ((self.channel_configuration as u16) << 3) 908 | ); 909 | 910 | // sl configuration descriptor 911 | write_u8!(writer, 0x06); // descriptor_tag=es_configuration_descriptor 912 | write_u8!(writer, 1); // descriptor_len 913 | write_u8!(writer, 2); // MP4 914 | 915 | Ok(()) 916 | } 917 | } 918 | --------------------------------------------------------------------------------