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 {
|
||||
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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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>>,
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue