stsd creation for av1 and opus
This commit is contained in:
parent
9680ffd46b
commit
d57ccb2c9d
4 changed files with 131 additions and 11 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
trait MP4Atom {
|
||||||
|
fn size(&self) -> u32;
|
||||||
|
|
||||||
|
fn marshall(&self) -> Vec<u8>;
|
||||||
|
}
|
||||||
|
|
||||||
struct WindowMatrix {
|
struct WindowMatrix {
|
||||||
a: u32,
|
a: u32,
|
||||||
b: u32,
|
b: u32,
|
||||||
|
@ -16,7 +22,7 @@ struct Edits {
|
||||||
play_rate: u32,
|
play_rate: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MOOV {
|
pub struct MOOV {
|
||||||
mvhd: MVHD,
|
mvhd: MVHD,
|
||||||
traks: [TRAK; 2],
|
traks: [TRAK; 2],
|
||||||
mvex: MVEX,
|
mvex: MVEX,
|
||||||
|
@ -150,7 +156,7 @@ struct URN {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct STBL {
|
struct STBL {
|
||||||
stsd: Vec<u8>,
|
stsd: STSD,
|
||||||
stts: STTS,
|
stts: STTS,
|
||||||
stsc: STSC,
|
stsc: STSC,
|
||||||
stsz: STSZ,
|
stsz: STSZ,
|
||||||
|
@ -158,11 +164,47 @@ struct STBL {
|
||||||
opts: Vec<Vec<u8>>,
|
opts: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct STSD {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub num_entries: u32,
|
||||||
|
pub entry: SampleEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SampleEntry {
|
||||||
|
Visual(VisualSampleEntry),
|
||||||
|
Sound(SoundSampleEntry),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VisualSampleEntry {
|
||||||
|
pub dref_idx: u16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
pub resolution: u64,
|
||||||
|
pub sample_per_frame: u16,
|
||||||
|
pub encoder_name: String,
|
||||||
|
pub pixel_depth: u16,
|
||||||
|
pub codec_config: CodecConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SoundSampleEntry {
|
||||||
|
pub dref_idx: u16,
|
||||||
|
pub channels: u16,
|
||||||
|
pub sample_size: u16,
|
||||||
|
pub sample_rate: u32,
|
||||||
|
pub codec_config: CodecConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CodecConfig {
|
||||||
|
pub atom_name: [u8; 4],
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
struct STTS {
|
struct STTS {
|
||||||
version: u8,
|
version: u8,
|
||||||
flags: u32,
|
flags: u32,
|
||||||
num_entires: u32,
|
num_entries: u32,
|
||||||
entires: Vec<STTSEntry>,
|
entries: Vec<STTSEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct STTSEntry {
|
struct STTSEntry {
|
||||||
|
@ -211,3 +253,4 @@ struct TREX {
|
||||||
default_size: u32,
|
default_size: u32,
|
||||||
default_flags: u32,
|
default_flags: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,9 @@ use crate::util;
|
||||||
|
|
||||||
pub struct MP4Muxer {
|
pub struct MP4Muxer {
|
||||||
v: mpsc::Receiver<util::NALUPacket>,
|
v: mpsc::Receiver<util::NALUPacket>,
|
||||||
v_samples: Vec<util::NALUPacket>,
|
v_samples: Vec<Vec<u8>>,
|
||||||
a: mpsc::Receiver<util::NALUPacket>,
|
a: mpsc::Receiver<util::NALUPacket>,
|
||||||
a_samples: Vec<util::NALUPacket>,
|
a_samples: Vec<Vec<u8>>,
|
||||||
metadata: Arc<util::Metadata>,
|
metadata: Arc<util::Metadata>,
|
||||||
err_in: mpsc::Sender<Box<dyn Error + Send + Sync>>,
|
err_in: mpsc::Sender<Box<dyn Error + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,95 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::muxer::hls::mp4;
|
use crate::muxer::hls::mp4;
|
||||||
|
|
||||||
impl mp4::MP4Muxer {
|
impl mp4::MP4Muxer {
|
||||||
pub fn gen_init(&mut self) -> Result<(), Box<dyn Error>> {
|
pub fn gen_init(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
let a_CC = self.handle_a_CC()?;
|
let stsd_v = self.handle_a_cc()?;
|
||||||
let v_CC = self.handle_v_CC()?;
|
let stsd_a = self.handle_v_cc()?;
|
||||||
|
let init_tree = self.construct_moov(stsd_v, stsd_a);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_a_CC(&mut self) -> Result<util::NALUPacket, Box<dyn Error>> {
|
fn handle_v_cc(&mut self) -> Result<mp4::atoms::STSD, Box<dyn Error>> {
|
||||||
todo!();
|
let v_cc = self.v.recv()?;
|
||||||
|
return match v_cc.packet_type {
|
||||||
|
util::NALUPacketType::Video(util::VideoCodec::AV1) => {
|
||||||
|
self.v_samples.push(v_cc.packet_data.clone());
|
||||||
|
Ok(get_av1_stsd(v_cc.packet_data, &self.metadata))
|
||||||
|
},
|
||||||
|
_ => Err(Box::new(util::MuxerError::InvalidCodec))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_v_CC(&mut self) -> Result<util::NALUPacket, Box<dyn Error>> {
|
fn handle_a_cc(&mut self) -> Result<mp4::atoms::STSD, Box<dyn Error>> {
|
||||||
|
let a_cc = self.a.recv()?;
|
||||||
|
return match a_cc.packet_type {
|
||||||
|
util::NALUPacketType::Audio(util::AudioCodec::OPUS) => {
|
||||||
|
self.a.recv()?;
|
||||||
|
Ok(get_opus_stsd(a_cc.packet_data, &self.metadata))
|
||||||
|
},
|
||||||
|
_ => Err(Box::new(util::MuxerError::InvalidCodec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_moov(&self, stsd_v: mp4::atoms::STSD, stsd_a: mp4::atoms::STSD) -> mp4::atoms::MOOV {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_av1_stsd(mut sample: Vec<u8>, metadata: &Arc<util::Metadata>) -> mp4::atoms::STSD {
|
||||||
|
let cc_len = sample[1] as usize;
|
||||||
|
let mut av1c: Vec<u8> = sample.drain(..2 + cc_len).collect();
|
||||||
|
// should match encoder config somehow, check iso binding specs for details
|
||||||
|
// default for svt defaults + 720p@30fps.
|
||||||
|
// More complicated to parse than is worth doing.
|
||||||
|
let mut dummy_mp4_cc_binding = vec![0x81u8, 0x05, 0x0d, 0x00];
|
||||||
|
dummy_mp4_cc_binding.append(&mut av1c);
|
||||||
|
let cc = mp4::atoms::CodecConfig {atom_name: "av1C".as_bytes().try_into().unwrap(), data: dummy_mp4_cc_binding};
|
||||||
|
let v_sample_entry = mp4::atoms::VisualSampleEntry {
|
||||||
|
dref_idx: 1,
|
||||||
|
width: metadata.video.width as u16,
|
||||||
|
height: metadata.video.height as u16,
|
||||||
|
resolution: (72u64 << 48) + (72u64 << 16),
|
||||||
|
sample_per_frame: 1,
|
||||||
|
encoder_name: String::from("libsvtav1"),
|
||||||
|
pixel_depth: 24,
|
||||||
|
codec_config: cc,
|
||||||
|
};
|
||||||
|
return mp4::atoms::STSD {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
num_entries: 1,
|
||||||
|
entry: mp4::atoms::SampleEntry::Visual(v_sample_entry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_opus_stsd(sample: Vec<u8>, metadata: &Arc<util::Metadata>) -> mp4::atoms::STSD {
|
||||||
|
let mut opus_binding = vec![0u8; 11];
|
||||||
|
opus_binding[1] = metadata.audio.channels;
|
||||||
|
let pre_skip = u16::from_le_bytes(sample[10..12].try_into().unwrap());
|
||||||
|
opus_binding.splice(2..4, pre_skip.to_be_bytes());
|
||||||
|
opus_binding.splice(4..8, metadata.audio.samplerate.to_be_bytes());
|
||||||
|
let output_gain = u16::from_le_bytes(sample[16..18].try_into().unwrap());
|
||||||
|
opus_binding.splice(8..10, output_gain.to_be_bytes());
|
||||||
|
opus_binding[10] = sample[18];
|
||||||
|
if opus_binding[18] == 1 {
|
||||||
|
opus_binding.append(&mut sample[19..].to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
let a_sample_entry = mp4::atoms::SoundSampleEntry {
|
||||||
|
dref_idx: 1,
|
||||||
|
channels: metadata.audio.channels as u16,
|
||||||
|
sample_size: 16,
|
||||||
|
sample_rate: metadata.audio.samplerate,
|
||||||
|
codec_config: mp4::atoms::CodecConfig { atom_name: "dOps".as_bytes().try_into().unwrap(), data: opus_binding}
|
||||||
|
};
|
||||||
|
return mp4::atoms::STSD {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
num_entries: 1,
|
||||||
|
entry: mp4::atoms::SampleEntry::Sound(a_sample_entry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -197,6 +197,7 @@ impl fmt::Display for EncoderError {
|
||||||
|
|
||||||
pub enum MuxerError {
|
pub enum MuxerError {
|
||||||
InvalidCodec,
|
InvalidCodec,
|
||||||
|
CCParseError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for MuxerError {}
|
impl Error for MuxerError {}
|
||||||
|
@ -206,6 +207,7 @@ impl fmt::Debug for MuxerError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
MuxerError::InvalidCodec => write!(f, "Codec not valid, unhandled"),
|
MuxerError::InvalidCodec => write!(f, "Codec not valid, unhandled"),
|
||||||
|
MuxerError::CCParseError => write!(f, "Generic error while parsing codec config box"),
|
||||||
_ => write!(f, "Error not described yet")
|
_ => write!(f, "Error not described yet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue