stsd creation for av1 and opus

This commit is contained in:
Muaz Ahmad 2023-10-19 12:55:28 +05:00
parent 9680ffd46b
commit d57ccb2c9d
4 changed files with 131 additions and 11 deletions

View file

@ -1,3 +1,9 @@
trait MP4Atom {
fn size(&self) -> u32;
fn marshall(&self) -> Vec<u8>;
}
struct WindowMatrix {
a: u32,
b: u32,
@ -16,7 +22,7 @@ struct Edits {
play_rate: u32,
}
struct MOOV {
pub struct MOOV {
mvhd: MVHD,
traks: [TRAK; 2],
mvex: MVEX,
@ -150,7 +156,7 @@ struct URN {
}
struct STBL {
stsd: Vec<u8>,
stsd: STSD,
stts: STTS,
stsc: STSC,
stsz: STSZ,
@ -158,11 +164,47 @@ struct STBL {
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 {
version: u8,
flags: u32,
num_entires: u32,
entires: Vec<STTSEntry>,
num_entries: u32,
entries: Vec<STTSEntry>,
}
struct STTSEntry {
@ -211,3 +253,4 @@ struct TREX {
default_size: u32,
default_flags: u32,
}

View file

@ -8,9 +8,9 @@ use crate::util;
pub struct MP4Muxer {
v: mpsc::Receiver<util::NALUPacket>,
v_samples: Vec<util::NALUPacket>,
v_samples: Vec<Vec<u8>>,
a: mpsc::Receiver<util::NALUPacket>,
a_samples: Vec<util::NALUPacket>,
a_samples: Vec<Vec<u8>>,
metadata: Arc<util::Metadata>,
err_in: mpsc::Sender<Box<dyn Error + Send + Sync>>,
}

View file

@ -1,20 +1,95 @@
use std::error::Error;
use std::sync::Arc;
use crate::util;
use crate::muxer::hls::mp4;
impl mp4::MP4Muxer {
pub fn gen_init(&mut self) -> Result<(), Box<dyn Error>> {
let a_CC = self.handle_a_CC()?;
let v_CC = self.handle_v_CC()?;
let stsd_v = self.handle_a_cc()?;
let stsd_a = self.handle_v_cc()?;
let init_tree = self.construct_moov(stsd_v, stsd_a);
Ok(())
}
fn handle_a_CC(&mut self) -> Result<util::NALUPacket, Box<dyn Error>> {
todo!();
fn handle_v_cc(&mut self) -> Result<mp4::atoms::STSD, Box<dyn Error>> {
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!();
}
}
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),
}
}

View file

@ -197,6 +197,7 @@ impl fmt::Display for EncoderError {
pub enum MuxerError {
InvalidCodec,
CCParseError,
}
impl Error for MuxerError {}
@ -206,6 +207,7 @@ impl fmt::Debug for MuxerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
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")
}
}