pub const EBML_ID: u32 = 0x1A45DFA3;
pub const SEGMENT_ID: u32 = 0x18538067;
pub const INFO_ID: u32 = 0x1549A966;
pub const TRACKS_ID: u32 = 0x1654AE6B;
pub const TRACK_ENTRY_ID: u32 = 0xAE;
pub const CLUSTER_ID: u32 = 0x1F43B675;
pub const SIMPLE_BLOCK_ID: u32 = 0xA3;
pub const TIMECODE_SCALE_ID: u32 = 0x2AD7B1;
pub const MUXING_APP_ID: u32 = 0x4D80;
pub const WRITING_APP_ID: u32 = 0x5741;
pub const TRACK_NUMBER_ID: u32 = 0xD7;
pub const TRACK_UID_ID: u32 = 0x73C5;
pub const TRACK_TYPE_ID: u32 = 0x83;
pub const CODEC_ID_ID: u32 = 0x86;
pub const CODEC_PRIVATE_ID: u32 = 0x63A2;
pub const CODEC_DELAY_ID: u32 = 0x56AA;
pub const SEEK_PRE_ROLL_ID: u32 = 0x56BB;
pub const AUDIO_ID: u32 = 0xE1;
pub const SAMPLING_FREQUENCY_ID: u32 = 0xB5;
pub const CHANNELS_ID: u32 = 0x94;
pub const TIMECODE_ID: u32 = 0xE7;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WebmError {
VintTooLarge(u64),
}
impl std::fmt::Display for WebmError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WebmError::VintTooLarge(value) => write!(f, "value {value} is too large for VINT"),
}
}
}
impl std::error::Error for WebmError {}
pub fn encode_vint(value: u64) -> Result<Vec<u8>, WebmError> {
let mut bytes = Vec::new();
let mut len = 1;
let mut mask = 0x80;
loop {
if value < mask - 1 {
break;
}
mask <<= 7;
len += 1;
if len > 8 {
return Err(WebmError::VintTooLarge(value));
}
}
let mut marker = 0x80u8;
for _ in 1..len {
marker >>= 1;
}
let val_with_marker = value | ((marker as u64) << ((len - 1) * 8));
for i in (0..len).rev() {
bytes.push(((val_with_marker >> (i * 8)) & 0xFF) as u8);
}
Ok(bytes)
}
pub fn encode_id(id: u32) -> Vec<u8> {
let mut bytes = Vec::new();
if id >= 0x10000000 {
bytes.push(((id >> 24) & 0xFF) as u8);
}
if id >= 0x0010000 {
bytes.push(((id >> 16) & 0xFF) as u8);
}
if id >= 0x0000100 {
bytes.push(((id >> 8) & 0xFF) as u8);
}
bytes.push((id & 0xFF) as u8);
bytes
}
pub fn make_element(id: u32, data: &[u8]) -> Vec<u8> {
let mut out = encode_id(id);
let size = encode_vint(data.len() as u64)
.expect("element size should always fit within an 8-byte VINT");
out.extend_from_slice(&size);
out.extend_from_slice(data);
out
}
pub fn make_uint_element(id: u32, value: u64) -> Vec<u8> {
let mut data = Vec::new();
let bytes = value.to_be_bytes();
let skip = bytes.iter().position(|&x| x != 0).unwrap_or(bytes.len());
if skip == bytes.len() {
data.push(0);
} else {
data.extend_from_slice(&bytes[skip..]);
}
make_element(id, &data)
}
pub fn make_float_element(id: u32, value: f32) -> Vec<u8> {
make_element(id, &value.to_be_bytes())
}
pub fn make_string_element(id: u32, value: &str) -> Vec<u8> {
make_element(id, value.as_bytes())
}
pub fn make_simple_block(track_number: u64, timecode: i16, data: &[u8]) -> Vec<u8> {
let mut block = encode_vint(track_number).expect("track number must fit in VINT");
block.extend_from_slice(&timecode.to_be_bytes());
block.push(0x80);
block.extend_from_slice(data);
make_element(SIMPLE_BLOCK_ID, &block)
}