Created
March 10, 2023 10:52
-
-
Save Zaggy1024/a030cf23069ed62d452161f579ed272f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#pragma debug | |
#pragma endian big | |
#include <std/core.pat> | |
#include <std/fxpt.pat> | |
#include <std/io.pat> | |
#include <std/math.pat> | |
#include <std/mem.pat> | |
#include <std/string.pat> | |
#include <std/time.pat> | |
#include <type/byte.pat> | |
#include <type/guid.pat> | |
#include <type/size.pat> | |
#define MIN_BOX_SIZE 8 | |
str current_handler; | |
fn contents_size(u128 base_offset, u64 size) { | |
s128 end = size == 0 ? std::mem::size() : base_offset + size; | |
end = std::math::min(end, std::mem::size()); | |
s128 offset_signed = $; | |
return end - offset_signed; | |
}; | |
fn get_box_size() { | |
u64 size = be u32(std::mem::read_unsigned($, sizeof(u32))); | |
if (size == 1) { | |
size = be u64(std::mem::read_unsigned($ + (sizeof(u32) * 2), sizeof(u64))); | |
} | |
return size; | |
}; | |
fn get_box_type() { | |
u128 start = $ + sizeof(u32); | |
char a = std::mem::read_unsigned(start, 1); | |
char b = std::mem::read_unsigned(start + 1, 1); | |
char c = std::mem::read_unsigned(start + 2, 1); | |
char d = std::mem::read_unsigned(start + 3, 1); | |
return std::format("{:c}{:c}{:c}{:c}", a, b, c, d); | |
}; | |
struct Box { | |
type::Size32 box_size; | |
char box_type[4]; | |
if (box_size == 1) { | |
type::Size64 box_size; | |
} | |
if (box_type == "uuid") { | |
type::GUID user_type; | |
} | |
}; | |
using UnknownBox; | |
fn appears_to_contain_boxes(ref u128 contents_offset, u128 contents_size) { | |
u128 current_offset = contents_offset; | |
while (true) { | |
u32 current_size_raw @ current_offset; | |
u32 current_size = be u32(current_size_raw); | |
current_offset += current_size; | |
if (current_offset == contents_offset || current_offset >= (contents_offset + contents_size)) { | |
break; | |
} | |
} | |
return current_offset == (contents_offset + contents_size); | |
}; | |
struct UnknownBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} [[inline]]; | |
struct UnknownBox : Box { | |
if (appears_to_contain_boxes($, contents_size(addressof(this), box_size))) { | |
UnknownBoxSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE && std::mem::size() - $ > MIN_BOX_SIZE)]; | |
} else { | |
$ += sizeof(u32); | |
if (appears_to_contain_boxes($, contents_size(addressof(this), box_size))) { | |
$ -= sizeof(u32); | |
u8 version; | |
u24 flags; | |
UnknownBoxSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE && std::mem::size() - $ > MIN_BOX_SIZE)]; | |
} else { | |
$ -= sizeof(u32); | |
} | |
} | |
u128 next_offset = std::math::min($ + contents_size(addressof(this), box_size), std::mem::size()); | |
if (next_offset > $) { | |
u8 unknown_data[next_offset - $] [[sealed]]; | |
} else { | |
$ = next_offset; | |
} | |
} [[format("format_box_type")]]; | |
fn format_box_type(ref auto box) { | |
return box.box_type; | |
}; | |
// ftyp | |
struct FileTypeBox : Box { | |
char major_brand[4]; | |
u32 minor_version; | |
char minor_brands[contents_size(addressof(this), box_size)] [[format("format_minor_brands")]]; | |
} [[format("format_ftyp")]]; | |
fn string_trim_end(ref str string, u128 amount) { | |
u128 length = std::string::length(string); | |
return std::string::substr(string, 0, length - amount); | |
}; | |
fn format_minor_brands(ref auto minor_brands) { | |
str result = ""; | |
u128 i; | |
for (i = 0, i < sizeof(minor_brands), i += 4) { | |
result = result + minor_brands[i] + minor_brands[i + 1] + minor_brands[i + 2] + minor_brands[i + 3] + ", "; | |
} | |
return string_trim_end(result, 2); | |
}; | |
fn format_ftyp(ref auto ftyp) { | |
return std::format("ftyp (major={})", ftyp.major_brand); | |
}; | |
// meta | |
using HandlerBox; | |
using PrimaryItemBox; | |
using ItemLocationBox; | |
using ItemInfoBox; | |
using ItemReferenceBox; | |
using ItemPropertiesBox; | |
using DataInformationBox; | |
using ItemKeysAtom; | |
using ItemListAtom; | |
struct FullBox : Box { | |
u8 version; | |
u24 flags; | |
}; | |
struct MetaChildBoxSelector { | |
// It'd be nice to inherit the skipping behavior and just have the if statement here | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "hdlr") { | |
HandlerBox hdlr; | |
} else if (box_header.box_type == "pitm") { | |
PrimaryItemBox pitm; | |
} else if (box_header.box_type == "iloc") { | |
ItemLocationBox iloc; | |
} else if (box_header.box_type == "iinf") { | |
ItemInfoBox iinf; | |
} else if (box_header.box_type == "iref") { | |
ItemReferenceBox iref; | |
} else if (box_header.box_type == "iprp") { | |
ItemPropertiesBox iprp; | |
} else if (box_header.box_type == "dinf") { | |
DataInformationBox dinf; | |
} else if (box_header.box_type == "keys") { | |
ItemKeysAtom keys; | |
} else if (box_header.box_type == "ilst") { | |
ItemListAtom ilst; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} else { | |
padding[std::mem::size() - $]; | |
} | |
} [[inline]]; | |
fn is_hdlr(u128 offset) { | |
u32 fourcc = be u32(std::mem::read_unsigned(offset, 4)); | |
const u32 hdlr_fourcc = ('h' << 24) | ('d' << 16) | ('l' << 8) | 'r'; | |
return fourcc == hdlr_fourcc; | |
}; | |
struct MetaBox : Box { | |
// QuickTime didn't include the version and flags, but must have a handler. | |
if (!is_hdlr($ + sizeof(u32))) { | |
u8 version; | |
u24 flags; | |
} | |
MetaChildBoxSelector contents[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)] [[format("format_box_array")]]; | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_meta")]]; | |
fn format_box_array(ref auto box_array) { | |
str result = "[ "; | |
u64 i; | |
for (i = 0, i < std::core::member_count(box_array), i += 1) { | |
result = result + box_array[i].box_header.box_type + ", "; | |
} | |
return string_trim_end(result, 2) + " ]"; | |
}; | |
fn format_meta(ref auto meta) { | |
return std::format("meta {}", format_box_array(meta.contents)); | |
}; | |
// (mdia|meta) -> hdlr | |
struct HandlerBox : FullBox { | |
if (std::mem::read_unsigned($, 4) != 0x0) { | |
char component_type[4]; | |
char component_subtype[4]; | |
current_handler = "none"; | |
} else { | |
padding[sizeof(u32)]; | |
char handler_type[4]; | |
current_handler = handler_type; | |
} | |
if (std::mem::read_unsigned($, 4) != 0x0) { | |
char component_manufacturer[4]; | |
u32 component_flags; | |
u32 component_flags_mask; | |
} else { | |
padding[sizeof(u32) * 3]; | |
} | |
if (contents_size(addressof(this), box_size) > 0) { | |
char name[]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_hdlr")]]; | |
fn format_hdlr(ref auto hdlr) { | |
if (std::core::has_member(hdlr, "name")) { | |
return std::format("hdlr (type={}, name={})", hdlr.handler_type, string_trim_end(hdlr.name, 1)); | |
} | |
return std::format("hdlr (type={})", hdlr.handler_type); | |
}; | |
// meta -> pitm | |
struct PrimaryItemBox : FullBox { | |
if (version == 0) { | |
u16 item_id; | |
} else { | |
u32 item_id; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_pitm")]]; | |
fn format_pitm(ref auto pitm) { | |
return std::format("pitm (id={})", pitm.item_id); | |
}; | |
// meta -> iloc | |
bitfield ConstructionMethod { | |
padding : 12; | |
value : 4; | |
} [[inline]]; | |
struct Extent { | |
if (parent.parent.version == 1 || parent.parent.version == 2) { | |
if (parent.parent.sizes.index_size == 4) { | |
u32 index; | |
} else if (parent.parent.sizes.index_size == 8) { | |
u64 index; | |
} | |
} | |
if (parent.parent.sizes.offset_size == 4) { | |
type::Size32 offset; | |
} else if (parent.parent.sizes.offset_size == 8) { | |
type::Size64 offset; | |
} | |
if (parent.parent.sizes.length_size == 4) { | |
type::Size32 length; | |
} else if (parent.parent.sizes.length_size == 8) { | |
type::Size64 length; | |
} | |
} [[format("format_extent")]]; | |
fn format_extent(ref auto extent) { | |
u64 offset = 0; | |
if (std::core::has_member(extent, "offset")) { | |
offset = extent.offset; | |
} | |
u64 length = 0; | |
if (std::core::has_member(extent, "length")) { | |
length = extent.length; | |
} | |
return std::format("[{}..{}] ({})", offset, offset + length, type::impl::size_formatter(length)); | |
}; | |
struct ItemLocation { | |
if (parent.version < 2) { | |
u16 item_id; | |
} else { | |
u32 item_id; | |
} | |
if (parent.version == 1 || parent.version == 2) { | |
le ConstructionMethod construction_method; | |
} | |
u16 data_reference_index; | |
if (parent.sizes.base_offset_size == 4) { | |
u8* base_offset : u32; | |
} else if (parent.sizes.base_offset_size == 8) { | |
u8* base_offset : u64; | |
} | |
u16 extent_count; | |
Extent extents[extent_count] [[format("format_array")]]; | |
}; | |
fn format_array(ref auto array) { | |
str result = "[ "; | |
u64 i; | |
for (i = 0, i < std::core::member_count(array), i += 1) { | |
result = result + std::format("{}, ", array[i], ", "); | |
} | |
return string_trim_end(result, 2) + " ]"; | |
}; | |
bitfield ItemLocationSizes { | |
offset_size : 4; | |
length_size : 4; | |
base_offset_size : 4; | |
index_size : 4; | |
} [[inline,left_to_right]]; | |
struct ItemLocationBox : FullBox { | |
ItemLocationSizes sizes; | |
if (version < 2) { | |
u16 item_count; | |
} else { | |
u32 item_count; | |
} | |
ItemLocation locations[item_count]; | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_iloc")]]; | |
fn format_iloc(ref auto iloc) { | |
str result = "iloc { "; | |
u32 i; | |
for (i = 0, i < iloc.item_count, i += 1) { | |
result = result + std::format("{} = {}, ", iloc.locations[i].item_id, iloc.locations[i].extents); | |
} | |
return string_trim_end(result, 2) + " }"; | |
}; | |
// meta -> iinf | |
using ItemInfoEntry; | |
struct ItemInfoEntrySelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "infe") { | |
ItemInfoEntry infe; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct ItemInfoBox : FullBox { | |
if (version == 0) { | |
u16 entry_count; | |
} else { | |
u32 entry_count; | |
} | |
ItemInfoEntrySelector entries[entry_count]; | |
}; | |
// meta -> iinf -> infe | |
struct ItemInfoEntry : FullBox { | |
if (version <= 1) { | |
u16 item_id; | |
u16 item_protection_index; | |
char item_name[]; | |
char content_type[]; | |
char content_encoding[]; | |
if (version == 1) { | |
char extension_type[4]; | |
if (extension_type == "fdel") { | |
std::print("TODO: Implement FileDeliveryItemInfoExtension ('fdel')"); | |
} | |
} | |
} else { | |
if (version == 2) { | |
u16 item_id; | |
} else { | |
u32 item_id; | |
} | |
u16 item_protection_index; | |
char item_type[4]; | |
char item_name[]; | |
padding[1]; | |
if (item_type == "mime") { | |
char content_type[]; | |
char content_encoding[]; | |
} else if (item_type == "uri ") { | |
char item_uri_type[]; | |
} | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_infe")]]; | |
fn format_infe(ref auto infe) { | |
str type; | |
if (std::core::has_member(infe, "item_type")) { | |
type = infe.item_type; | |
} else { | |
type = infe.content_type; | |
} | |
return std::format("infe {{ id={}, type=\"{}\", name=\"{}\" }}", infe.item_id, type, string_trim_end(infe.item_name, 1)); | |
}; | |
// meta -> iref | |
struct SingleItemTypeReferenceBox<SizedType> { | |
u32 box_size; | |
char reference_type[4]; | |
SizedType from_item_id; | |
u16 reference_count; | |
u16 to_item_ids[reference_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct SingleItemTypeReferenceBoxWrapper<SizedType> { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
SingleItemTypeReferenceBox<SizedType> box [[name(box_header.box_type)]]; | |
} | |
} [[inline]]; | |
struct ItemReferenceBox : FullBox { | |
if (version == 0) { | |
SingleItemTypeReferenceBoxWrapper<u16> references[while (contents_size(addressof(this), box_size) > 0)]; | |
} else if (version == 1) { | |
SingleItemTypeReferenceBoxWrapper<u32> references[while (contents_size(addressof(this), box_size) > 0)]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// meta -> iprp | |
using ItemPropertyContainerBox; | |
using ItemPropertyAssociationBox; | |
struct ItemPropertiesChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "ipco") { | |
ItemPropertyContainerBox ipco; | |
} else if (box_header.box_type == "ipma") { | |
ItemPropertyAssociationBox ipma; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct ItemPropertiesBox : Box { | |
ItemPropertiesChildBoxSelector contents[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)] [[format("format_box_array")]]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// meta -> iprp -> ipco | |
using ImageSpatialExtentsProperty; | |
using PixelInformationBox; | |
using AuxiliaryTypeProperty; | |
using ColourInformationBox; | |
using AV1CodecConfigurationBox; | |
struct ItemPropertyContainerSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "ispe") { | |
ImageSpatialExtentsProperty ispe; | |
} else if (box_header.box_type == "pixi") { | |
PixelInformationBox pixi; | |
} else if (box_header.box_type == "av1C") { | |
AV1CodecConfigurationBox av1C; | |
} else if (box_header.box_type == "colr") { | |
ColourInformationBox colr; | |
} else if (box_header.box_type == "auxC") { | |
AuxiliaryTypeProperty auxC; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct ItemPropertyContainerBox : Box { | |
ItemPropertyContainerSelector contents[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)] [[format("format_box_array")]]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// meta -> iprp -> ipma | |
bitfield PropertyIndex8 { | |
essential : 1; | |
property_index : 7; | |
}; | |
bitfield PropertyIndex16 { | |
essential : 1; | |
property_index : 15; | |
}; | |
struct ItemPropertyAssociations<auto ipma_version, auto ipma_flags> { | |
if (ipma_version == 0) { | |
u16 item_id; | |
} else { | |
u32 item_id; | |
} | |
u8 association_count; | |
if ((ipma_flags & 1) == 1) { | |
PropertyIndex16 properties[association_count]; | |
} else { | |
PropertyIndex8 properties[association_count]; | |
} | |
}; | |
struct ItemPropertyAssociationBox : FullBox { | |
u32 entry_count; | |
ItemPropertyAssociations<version, flags> associations[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// meta -> keys | |
struct ItemKey { | |
u32 size; | |
char namespace_[4] [[name("namespace")]]; | |
char name[contents_size(addressof(this), size)]; | |
} [[format("format_item_key")]]; | |
fn format_item_key(ref auto item_key) { | |
return std::format("{}:{}", item_key.namespace_, item_key.name); | |
}; | |
struct ItemKeysAtom : FullBox { | |
u32 entry_count; | |
ItemKey entries[entry_count]; | |
}; | |
// meta -> ilst | |
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html | |
enum WellKnownType : u32 { | |
Reserved = 0, | |
UTF8 = 1, | |
UTF16 = 2, | |
SJIS = 3, | |
UTF8Sort = 4, | |
UTF16Sort = 5, | |
JPEG = 13, | |
PNG = 14, | |
BESignedInt = 21, | |
BE_UnsignedInt = 22, | |
BEFloat32 = 23, | |
BEFloat64 = 24, | |
BMP = 27, | |
QTMetadataAtom = 28, | |
EightBitSignedInt = 65, | |
BE16BitSignedInt = 66, | |
BE32BitSignedInt = 67, | |
BEPointF32 = 70, | |
BEDimensionsF32 = 71, | |
BERectF32 = 72, | |
BE64BitSignedInt = 74, | |
EightBitUnsignedInt = 75, | |
BE16BitUnsignedInt = 76, | |
BE32BitUnsignedInt = 77, | |
BE64BitUnsignedInt = 78, | |
AffineTransformF64 = 79, | |
}; | |
struct DataAtom : Box { | |
WellKnownType type_indicator; | |
u32 locale_indicator; | |
if (type_indicator == WellKnownType::UTF8) { | |
char value[contents_size(addressof(this), box_size)]; | |
} else if (type_indicator == WellKnownType::UTF16) { | |
char16 value[contents_size(addressof(this), box_size) / sizeof(char16)]; | |
} else { | |
u8 value[contents_size(addressof(this), box_size)]; | |
} | |
} [[format("format_data_atom")]]; | |
fn format_data_atom(ref auto data_atom) { | |
if (data_atom.type_indicator == WellKnownType::UTF8 | |
|| data_atom.type_indicator == WellKnownType::UTF16) { | |
return std::format("\"{}\"", data_atom.value); | |
} | |
return data_atom.value; | |
}; | |
struct NameAtom : FullBox { | |
char value[contents_size(addressof(this), box_size)]; | |
} [[format("format_unwrap_value")]]; | |
struct ItemAtomChildSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "data") { | |
DataAtom data; | |
} else if (box_header.box_type == "data") { | |
NameAtom name; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct ItemAtom { | |
u32 box_size; | |
if (current_handler == "mdir") { | |
char key[4]; | |
} else { | |
u32 key_index; | |
} | |
ItemAtomChildSelector contents[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct ItemListAtomChildSelector { | |
u64 box_size = get_box_size(); | |
if (current_handler == "mdir") { | |
str box_type = get_box_type(); | |
} else { | |
u32 box_type = be u32(std::mem::read_unsigned($ + sizeof(u32), 4)); | |
} | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_size) <= std::mem::size()) { | |
ItemAtom data [[name(box_type)]]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[inline]]; | |
struct ItemListAtom : Box { | |
ItemListAtomChildSelector contents[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov | |
using MovieHeaderBox; | |
using MovieExtendsBox; | |
using ObjectDescriptorBox; | |
using TrackBox; | |
using UserDataBox; | |
struct MovieChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "mvhd") { | |
MovieHeaderBox mvhd; | |
} else if (box_header.box_type == "mvex") { | |
MovieExtendsBox mvex; | |
} else if (box_header.box_type == "meta") { | |
MetaBox meta; | |
} else if (box_header.box_type == "iods") { | |
ObjectDescriptorBox iods; | |
} else if (box_header.box_type == "trak") { | |
TrackBox trak; | |
} else if (box_header.box_type == "udta") { | |
UserDataBox udta; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct MovieBox : Box { | |
MovieChildBoxSelector children[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moov -> mvhd | |
fn to_float(auto value) { | |
return std::string::parse_float(std::string::to_string(value)); | |
}; | |
fn format_fixed(u32 value, u32 precision) { | |
// double cast is broken it seems | |
return to_float(value) / to_float(1 << precision); | |
}; | |
using fixed_s32 = s32 [[format("format_fixed_32")]]; | |
using fixed_u32 = u32 [[format("format_fixed_32")]]; | |
fn format_fixed_32(u32 value) { | |
return format_fixed(value, 16); | |
}; | |
using fixed_s16 = s16 [[format("format_fixed_16")]]; | |
fn format_fixed_16(u16 value) { | |
return format_fixed(value, 8); | |
}; | |
using normal_s32 = s32 [[format("format_normal_32")]]; | |
fn format_normal_32(ref auto value) { | |
return format_fixed(value, 30); | |
}; | |
struct Matrix { | |
fixed_s32 matrix_00; | |
fixed_s32 matrix_01; | |
fixed_s32 matrix_02; | |
fixed_s32 matrix_10; | |
fixed_s32 matrix_11; | |
fixed_s32 matrix_12; | |
normal_s32 matrix_20; | |
normal_s32 matrix_21; | |
normal_s32 matrix_22; | |
} [[format("format_matrix")]]; | |
fn format_matrix(ref auto matrix) { | |
return std::format("[ [ {}, {}, {} ], [ {}, {}, {} ], [ {}, {}, {} ] ]", | |
format_fixed_32(matrix.matrix_00), format_fixed_32(matrix.matrix_01), format_fixed_32(matrix.matrix_02), | |
format_fixed_32(matrix.matrix_10), format_fixed_32(matrix.matrix_11), format_fixed_32(matrix.matrix_12), | |
format_normal_32(matrix.matrix_20), format_normal_32(matrix.matrix_21), format_normal_32(matrix.matrix_22)); | |
}; | |
using datetime<type> = type [[format("format_datetime")]]; | |
fn format_datetime(ref auto value) { | |
return std::time::format(std::time::to_utc(value - 2082844800)); | |
}; | |
struct MovieHeaderBox : FullBox { | |
if (version < 2) { | |
if (version == 1) { | |
datetime<u64> creation_time; | |
datetime<u64> modification_time; | |
u32 timescale; | |
u64 duration; | |
} else if (version == 0) { | |
datetime<u32> creation_time; | |
datetime<u32> modification_time; | |
u32 timescale; | |
u32 duration; | |
} | |
fixed_s32 rate; | |
fixed_s16 volume; | |
padding[sizeof(u16) + (sizeof(u32) * 2)]; | |
Matrix matrix; | |
padding[sizeof(u32) * 6]; | |
u32 next_track_id; | |
} else { | |
print("Unsupported movie header box version {}", version); | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_mvhd")]]; | |
fn format_duration(ref auto duration, ref auto timescale) { | |
float duration_f = duration; | |
float timescale_f = timescale; | |
float total_seconds = duration_f / timescale_f; | |
u128 minutes = std::math::floor(total_seconds / 60); | |
float seconds = total_seconds - (minutes * 60); | |
if (minutes > 0) { | |
return std::format("{:02d}:{:06.3f}", minutes, seconds); | |
} else { | |
return std::format("{:.3f}s", seconds); | |
} | |
}; | |
fn format_mvhd(ref auto mvhd) { | |
return "mvhd { duration = " + format_duration(mvhd.duration, mvhd.timescale) + " }"; | |
}; | |
// moov -> mvex | |
using MovieExtendsHeaderBox; | |
using TrackExtendsBox; | |
struct MovieExtendsChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "mehd") { | |
MovieExtendsHeaderBox mehd; | |
} else if (box_header.box_type == "trex") { | |
TrackExtendsBox trex; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct MovieExtendsBox : Box { | |
MovieExtendsChildBoxSelector children[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moov -> mvex -> mehd | |
struct MovieExtendsHeaderBox : FullBox { | |
if (version < 2) { | |
if (version == 1) { | |
u64 fragment_duration; | |
} else { | |
u32 fragment_duration; | |
} | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> mvex -> trex | |
bitfield FragmentSampleFlags { | |
reserved : 4; | |
is_leading : 2; | |
sample_depends_on : 2; | |
sample_is_depended_on : 2; | |
sample_has_redundancy : 2; | |
sample_padding_value : 3; | |
sample_is_non_sync : 1; | |
sample_degradation_priority : 16; | |
} [[left_to_right]]; | |
struct TrackExtendsBox : FullBox { | |
u32 track_id; | |
u32 default_sample_description_index; | |
u32 default_sample_duration; | |
u32 default_sample_size; | |
FragmentSampleFlags default_sample_flags; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> iods | |
using InitialObjectDescriptor; | |
struct ObjectDescriptorBox : FullBox { | |
InitialObjectDescriptor initial; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> iods -> * | |
// ISO IEC 14496-1 MPEG-4 System Standard | |
struct VLQ128 { | |
u8 array[while ($ < std::mem::size() && ($ == addressof(this) || std::mem::read_unsigned($ - 1, 1) & 0x80 != 0))] [[hidden]]; | |
} [[sealed,format("transform_vlq128"),transform("transform_vlq128")]]; | |
fn transform_vlq128(ref auto vlq128) { | |
u128 result = 0; | |
for (u8 i = 0, i < std::core::member_count(vlq128.array), i += 1) { | |
result = (result << 7) | (vlq128.array[i] & 0x7f); | |
} | |
return result; | |
}; | |
struct Descriptor { | |
u8 tag; | |
VLQ128 instance_size; | |
}; | |
struct UnknownDescriptor : Descriptor { | |
$ += contents_size(addressof(this), instance_size) + sizeof(Descriptor); | |
}; | |
bitfield InitialObjectDescriptorInitialBits { | |
object_descriptor_id : 10; | |
url_flag : 1; | |
include_inline_profile_level_flag : 1; | |
padding : 4; | |
// These should appear in the order they are declared in, left_to_right reverses them. | |
} [[inline,left_to_right]]; | |
// moov -> iods -> MP4_IOD | |
using ElementalStreamIDDescriptor; | |
struct ObjectDescriptorSelector { | |
u8 tag [[hidden]]; | |
$ = addressof(this); | |
if (tag == 0x0E) { | |
ElementalStreamIDDescriptor es_id; | |
} else { | |
UnknownDescriptor unknown; | |
} | |
} [[inline]]; | |
struct InitialObjectDescriptor : Descriptor { | |
if (tag != 0x10) { | |
std::print("esds child was not an ES_Desc."); | |
} | |
InitialObjectDescriptorInitialBits initial_data; | |
if (initial_data.url_flag) { | |
u8 url_length; | |
char url[url_length]; | |
} else { | |
u8 od_profile_level_indication; | |
u8 scene_profile_level_indication; | |
u8 audio_profile_level_indication; | |
u8 visual_profile_level_indication; | |
u8 graphics_profile_level_indication; | |
} | |
ObjectDescriptorSelector children[while (contents_size(addressof(initial_data), instance_size) > 2)]; | |
$ += contents_size(addressof(initial_data), instance_size); | |
}; | |
// moov -> iods -> ES_ID | |
struct ElementalStreamIDDescriptor : Descriptor { | |
u32 track_id; | |
$ += contents_size(addressof(track_id), instance_size); | |
}; | |
// moov -> trak | |
using TrackHeaderBox; | |
using TrackReferenceBox; | |
using EditBox; | |
using MediaBox; | |
struct TrackChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "tkhd") { | |
TrackHeaderBox tkhd; | |
} else if (box_header.box_type == "meta") { | |
MetaBox meta; | |
} else if (box_header.box_type == "tref") { | |
TrackReferenceBox tref; | |
} else if (box_header.box_type == "edts") { | |
EditBox edts; | |
} else if (box_header.box_type == "mdia") { | |
MediaBox mdia; | |
} else if (box_header.box_type == "udta") { | |
UserDataBox udta; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct TrackBox : Box { | |
TrackChildBoxSelector children[while(contents_size(addressof(this), box_size) > 0)]; | |
}; | |
// moov -> trak -> tref | |
struct TrackReferenceTypeBox : Box { | |
u32 track_ids[contents_size(addressof(this), box_size) / sizeof(u32)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct TrackReferenceTypeBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
TrackReferenceTypeBox child [[name(box_header.box_type)]]; | |
} | |
} [[inline]]; | |
struct TrackReferenceBox : Box { | |
TrackReferenceTypeBoxSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moov -> trak -> tkhd | |
struct TrackHeaderBox : FullBox { | |
if (version < 2) { | |
if (version == 1) { | |
datetime<u64> creation_time; | |
datetime<u64> modification_time; | |
u32 track_id; | |
padding[sizeof(u32)]; | |
u64 duration; | |
} else { | |
datetime<u32> creation_time; | |
datetime<u32> modification_time; | |
u32 track_id; | |
padding[sizeof(u32)]; | |
u32 duration; | |
} | |
padding[sizeof(u32) * 2]; | |
s16 layer; | |
u16 alternate_group; | |
fixed_s16 volume; | |
padding[sizeof(u16)]; | |
Matrix matrix; | |
fixed_u32 width; | |
fixed_u32 height; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> edts | |
using EditListBox; | |
struct EditBox : Box { | |
EditListBox edits[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> edts -> elst | |
struct EditListEntry<DurationT, TimeT> { | |
DurationT segment_duration; | |
TimeT media_time; | |
s16 media_rate_integer; | |
s16 media_rate_fraction; | |
}; | |
struct EditListBox : FullBox { | |
if (version < 2) { | |
u32 entry_count; | |
if (version == 1) { | |
EditListEntry<u64, s64> entries[entry_count]; | |
} else { | |
EditListEntry<u32, s32> entries[entry_count]; | |
} | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia | |
using MediaHeaderBox; | |
using MediaInformationBox; | |
struct MediaChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "mdhd") { | |
MediaHeaderBox mdhd; | |
} else if (box_header.box_type == "hdlr") { | |
HandlerBox hdlr; | |
} else if (box_header.box_type == "minf") { | |
MediaInformationBox minf; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct MediaBox : Box { | |
MediaChildBoxSelector children[while (contents_size(addressof(this), box_size) > 0)]; | |
}; | |
// moov -> trak -> mdia -> mdhd | |
bitfield ISO639LanguageCode { | |
// Reversed | |
pad : 1; | |
a : 5; | |
b : 5; | |
c : 5; | |
} [[sealed,format("format_iso639"),left_to_right]]; | |
fn format_iso639(ref auto iso639) { | |
if (iso639.pad != 0) { | |
return "invalid"; | |
} | |
const u8 base = 0x60; | |
return std::format("\"{:c}{:c}{:c}\"", base + iso639.a, base + iso639.b, base + iso639.c); | |
}; | |
struct MediaHeaderBox : FullBox { | |
if (version < 2) { | |
if (version == 1) { | |
datetime<u64> creation_time; | |
datetime<u64> modification_time; | |
u32 timescale; | |
u64 duration; | |
} else { | |
datetime<u32> creation_time; | |
datetime<u32> modification_time; | |
u32 timescale; | |
u32 duration; | |
} | |
ISO639LanguageCode language; | |
padding[sizeof(u16)]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_mdhd")]]; | |
fn format_mdhd(ref auto mdhd) { | |
return std::format("mdhd {{ duration = {}, language = {} }}", | |
format_duration(mdhd.duration, mdhd.timescale), | |
format_iso639(mdhd.language)); | |
}; | |
// moov -> trak -> mdia -> minf | |
using NullMediaHeaderBox; | |
using VideoMediaHeaderBox; | |
using SoundMediaHeaderBox; | |
using DataInformationBox; | |
using SampleTableBox; | |
struct MediaInformationChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "nmhd") { | |
NullMediaHeaderBox nmhd; | |
} else if (box_header.box_type == "vmhd") { | |
VideoMediaHeaderBox vmhd; | |
} else if (box_header.box_type == "smhd") { | |
SoundMediaHeaderBox smhd; | |
} else if (box_header.box_type == "hdlr") { | |
// This is non-standard. | |
HandlerBox hdlr; | |
} else if (box_header.box_type == "dinf") { | |
DataInformationBox dinf; | |
} else if (box_header.box_type == "stbl") { | |
SampleTableBox stbl; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct MediaInformationBox : Box { | |
MediaInformationChildBoxSelector children[while(contents_size(addressof(this), box_size) > 0)]; | |
}; | |
// moov -> trak -> mdia -> minf -> nmhd | |
struct NullMediaHeaderBox : FullBox { | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> vmhd | |
struct VideoMediaHeaderBox : FullBox { | |
if (version == 0) { | |
u16 graphicsmode; | |
u16 opcolor[3]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> dinf | |
using DataReferenceBox; | |
struct DataInformationChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "dref") { | |
DataReferenceBox dref; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct DataInformationBox : Box { | |
DataInformationChildBoxSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moov -> trak -> mdia -> minf -> smhd | |
struct SoundMediaHeaderBox : FullBox { | |
if (version == 0) { | |
fixed_s16 balance; | |
padding[sizeof(u16)]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> dinf -> dref | |
using DataEntryUrlBox; | |
using DataEntryUrnBox; | |
struct DataEntryBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "url ") { | |
DataEntryUrlBox url; | |
} else if (box_header.box_type == "urn ") { | |
DataEntryUrnBox urn; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct DataReferenceBox : FullBox { | |
if (version == 0) { | |
u32 entry_count; | |
DataEntryBoxSelector children[entry_count]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> dinf -> dref -> url/urn | |
struct DataEntryUrlBox : FullBox { | |
if (version == 0 && flags == 0) { | |
char location[]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct DataEntryUrnBox : FullBox { | |
if (version == 0 && flags == 0) { | |
char name[]; | |
char location[]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl | |
using SampleDescriptionBox; | |
using TimeToSampleBox; | |
using CompositionOffsetBox; | |
using SyncSampleBox; | |
using SampleToChunkBox; | |
using SampleSizeBox; | |
using CompactSampleSizeBox; | |
using ChunkOffsetBox; | |
using ChunkLargeOffsetBox; | |
using SampleGroupDescriptionBox; | |
struct SampleTableChildBox { | |
type::Size32 size [[hidden]]; | |
char type[4] [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), size) <= std::mem::size()) { | |
if (type == "stsd") { | |
SampleDescriptionBox stsd; | |
} else if (type == "stts") { | |
TimeToSampleBox stts; | |
} else if (type == "ctts") { | |
CompositionOffsetBox ctts; | |
} else if (type == "stss") { | |
SyncSampleBox stss; | |
} else if (type == "stsc") { | |
SampleToChunkBox stsc; | |
} else if (type == "stsz") { | |
SampleSizeBox stsz; | |
} else if (type == "stz2") { | |
CompactSampleSizeBox stz2; | |
} else if (type == "stco") { | |
ChunkOffsetBox stco; | |
} else if (type == "co64") { | |
ChunkLargeOffsetBox co64; | |
} else if (type == "sgpd") { | |
SampleGroupDescriptionBox sgpd; | |
} else { | |
UnknownBox unknown [[name(type)]]; | |
} | |
} | |
} [[inline]]; | |
struct SampleTableBox : Box { | |
SampleTableChildBox children[while(contents_size(addressof(this), box_size) > 0)]; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd | |
using VisualSampleEntry; | |
using AudioSampleEntry; | |
using UnknownSampleEntry; | |
struct SampleEntrySelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (current_handler == "vide") { | |
VisualSampleEntry visual [[name(box_header.box_type)]]; | |
} else if (current_handler == "soun") { | |
AudioSampleEntry audio [[name(box_header.box_type)]]; | |
} else { | |
UnknownSampleEntry unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct SampleDescriptionBox : FullBox { | |
u32 entry_count; | |
SampleEntrySelector children[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * | |
struct SampleEntry : Box { | |
padding[sizeof(u8) * 6]; | |
u16 data_reference_index; | |
}; | |
using ElementaryStreamDescriptionBox; | |
struct UnknownSampleEntryChildSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "esds") { | |
ElementaryStreamDescriptionBox esds; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct UnknownSampleEntry : SampleEntry { | |
if (appears_to_contain_boxes($, contents_size(addressof(this), box_size))) { | |
UnknownSampleEntryChildSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) | |
enum VisualSampleDepth : u16 { | |
ColourNoAlpha = 0x0018, | |
}; | |
using VisualSampleEntryChildSelector; | |
struct VisualSampleEntry : SampleEntry { | |
padding[sizeof(u16) + sizeof(u16) + (sizeof(u32) * 3)]; | |
u16 width; | |
u16 height; | |
fixed_u32 horizresolution; | |
fixed_u32 vertresolution; | |
padding[sizeof(u32)]; | |
u16 frame_count_per_sample; | |
u8 compressorname_size [[hidden]]; | |
char compressorname[compressorname_size]; | |
$ = addressof(compressorname_size) + 32; | |
VisualSampleDepth depth; | |
padding[sizeof(s16)]; | |
VisualSampleEntryChildSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
bitfield CodingConstraintsBitField { | |
all_ref_pics_intra : 1; | |
intra_pred_used : 1; | |
max_ref_per_pic : 4; | |
padding : 2; | |
} [[inline,left_to_right]]; | |
struct CodingConstraintsBox : FullBox { | |
CodingConstraintsBitField fields; | |
padding[sizeof(u24)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
using AVCConfigurationBox; | |
using PixelAspectRatioBox; | |
using BitRateBox; | |
using MasteringDisplayColourVolumeBox; | |
using FieldHandlingBox; | |
struct VisualSampleEntryChildSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "avcC") { | |
AVCConfigurationBox avcC; | |
} else if (box_header.box_type == "av1C") { | |
AV1CodecConfigurationBox av1C; | |
} else if (box_header.box_type == "colr") { | |
ColourInformationBox colr; | |
} else if (box_header.box_type == "pasp") { | |
PixelAspectRatioBox pasp; | |
} else if (box_header.box_type == "btrt") { | |
BitRateBox btrt; | |
} else if (box_header.box_type == "mdcv") { | |
MasteringDisplayColourVolumeBox mdcv; | |
} else if (box_header.box_type == "ccst") { | |
CodingConstraintsBox ccst; | |
} else if (box_header.box_type == "auxi") { | |
AuxiliaryTypeProperty auxi; | |
} else if (box_header.box_type == "esds") { | |
ElementaryStreamDescriptionBox esds; | |
} else if (box_header.box_type == "fiel") { | |
FieldHandlingBox fiel; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> avc1 -> avcC | |
bitfield LengthSize { | |
padding : 6; | |
value_minus_1 : 2; | |
} [[sealed,left_to_right,format("transform_length_size"),transform("transform_length_size")]]; | |
fn transform_length_size(ref auto length_size) { | |
return length_size.value_minus_1 + 1; | |
}; | |
bitfield PaddedBitDepth { | |
padding : 5; | |
value_minus_8 : 3; | |
} [[sealed,left_to_right,format("transform_padded_bit_depth"),transform("transform_padded_bit_depth")]]; | |
fn transform_padded_bit_depth(ref auto padded_bit_depth) { | |
return padded_bit_depth.value_minus_8 + 8; | |
}; | |
bitfield Padded2Bits { | |
padding : 6; | |
value : 2; | |
} [[sealed,left_to_right,format("transform_padded_value"),transform("transform_padded_value")]]; | |
bitfield Padded5Bits { | |
padding : 3; | |
value : 5; | |
} [[sealed,left_to_right,format("transform_padded_value"),transform("transform_padded_value")]]; | |
fn transform_padded_value(ref auto padded_value) { | |
return padded_value.value; | |
}; | |
struct ParameterSet { | |
u16 length; | |
u8 data[length] [[sealed]]; | |
}; | |
struct AVCConfigurationBox : Box { | |
u8 configuration_version; | |
u8 profile; | |
u8 profile_compatibility; | |
u8 level_indication; | |
LengthSize length_size; | |
Padded5Bits sequence_parameter_sets_count; | |
ParameterSet sequence_parameter_sets[sequence_parameter_sets_count]; | |
u8 picture_parameter_sets_count; | |
ParameterSet picture_parameter_sets[picture_parameter_sets_count]; | |
if ((profile == 100 || profile == 110 || profile == 144) && contents_size(addressof(this), box_size) > 0) { | |
Padded2Bits chroma_format; | |
PaddedBitDepth bit_depth_luma; | |
PaddedBitDepth bit_depth_chroma; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> av01 -> av1C | |
bitfield AV1CodecMarkerAndVersion { | |
marker : 1; | |
version : 7; | |
} [[inline,left_to_right]]; | |
bitfield AV1CodecProfileAndLevel { | |
seq_profile : 3; | |
seq_level_idx_0 : 5; | |
} [[inline,left_to_right]]; | |
bitfield AV1CodecTierAndColorInfo { | |
seq_tier_0 : 1; | |
high_bitdepth : 1; | |
twelve_bit : 1; | |
monochrome : 1; | |
chroma_subsampling_x : 1; | |
chroma_subsampling_y : 1; | |
chroma_sample_position: 2; | |
} [[inline,left_to_right]]; | |
bitfield AV1CodecPresentationDelay { | |
padding : 3; | |
initial_presentation_delay_present : 1; | |
initial_presentation_delay_minus_1 : 4; | |
} [[inline,left_to_right]]; | |
struct AV1CodecConfigurationBox : Box { | |
// FIXME: Each of these has its fields' orders reversed. | |
// I can't get it to put marker before version while | |
// keeping them both equal to 1. | |
AV1CodecMarkerAndVersion marker_and_version; | |
AV1CodecProfileAndLevel profile_and_level; | |
AV1CodecTierAndColorInfo tier_and_color_info; | |
AV1CodecPresentationDelay presentation_delay; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (audio) | |
struct AudioSampleEntryChildSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "esds") { | |
ElementaryStreamDescriptionBox esds; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct AudioSampleEntry : SampleEntry { | |
u16 entry_version; // reserved = 0 in V0 | |
padding[sizeof(u16) * 3]; | |
u16 channel_count; | |
u16 sample_size; | |
u16 pre_defined; | |
padding[sizeof(u16)]; | |
fixed_u32 sample_rate; | |
AudioSampleEntryChildSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> ispe | |
struct ImageSpatialExtentsProperty : FullBox { | |
if (version == 0) { | |
u32 image_width; | |
u32 image_height; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_ispe")]]; | |
fn format_ispe(ref auto ispe) { | |
if (ispe.version == 0) { | |
return std::format("ispe ({}x{})", ispe.image_width, ispe.image_height); | |
} | |
return "ispe (unknown version)"; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> pixi | |
struct PixelInformationBox : FullBox { | |
if (version == 0) { | |
u8 channel_count; | |
u8 bits_per_channel[channel_count]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_pixi")]]; | |
fn format_pixi(ref auto pixi) { | |
if (pixi.version == 0) { | |
str result = "pixi ("; | |
for (u8 i = 0, i < pixi.channel_count, i += 1) { | |
result += std::format("{}bit, ", pixi.bits_per_channel[i]); | |
} | |
return string_trim_end(result, 2) + ")"; | |
} | |
return "pixi (unknown version)"; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> colr | |
bitfield ColourFullRangeFlag { | |
full_range_flag : 1; | |
} [[inline]]; | |
struct ColourInformationBox : Box { | |
char colour_type[4]; | |
if (colour_type == "nclx") { | |
u16 colour_primaries; | |
u16 transfer_characteristics; | |
u16 matrix_coefficients; | |
ColourFullRangeFlag full_range_flag; | |
} else if (colour_type == "rICC" || colour_type == "prof") { | |
u8 icc_profile_data[contents_size(addressof(this), box_size)]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_colr")]]; | |
fn format_colr(ref auto colr) { | |
return std::format("colr ({})", colr.colour_type); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> mdcv | |
using ChromaCoordinate = u16 [[format("format_hack"),transform("transform_chroma_coordinate")]]; | |
fn transform_chroma_coordinate(u16 coordinate) { | |
return coordinate / 50000.0f; | |
}; | |
fn format_hack(ref auto coordinate) { | |
return std::format("{}", coordinate); | |
}; | |
using LumaValue = u32 [[format("format_hack"),transform("transform_luma_value")]]; | |
fn transform_luma_value(u32 coordinate) { | |
return coordinate / 10000.0f; | |
}; | |
struct ColorCoordinates { | |
ChromaCoordinate x; | |
ChromaCoordinate y; | |
} [[format("format_color_coordinates")]]; | |
fn format_color_coordinates(ref auto coordinates) { | |
return std::format("{{ x = {}, y = {} }}", coordinates.x, coordinates.y); | |
}; | |
struct MasteringDisplayColourVolumeBox : Box { | |
ColorCoordinates green; | |
ColorCoordinates blue; | |
ColorCoordinates red; | |
ColorCoordinates white_point; | |
LumaValue max_luminance; | |
LumaValue min_luminance; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> pasp | |
struct PixelAspectRatioBox : Box { | |
u32 horizontal_spacing; | |
u32 vertical_spacing; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> btrt | |
struct BitRateBox : Box { | |
u32 decoding_buffer_size; | |
u32 max_bitrate; | |
u32 avg_bitrate; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> auxi | |
struct AuxiliaryTypeProperty : FullBox { | |
char type[]; | |
if (contents_size(addressof(this), box_size) > 0) { | |
char subtype[]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
} [[format("format_auxi")]]; | |
fn format_auxi(ref auto auxi) { | |
return std::format("{} ({})", auxi.box_type, string_trim_end(auxi.type, 1)); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds | |
using ElementaryStreamDescriptor; | |
struct ElementaryStreamDescriptionBox : FullBox { | |
ElementaryStreamDescriptor es_desc; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Descriptor | |
// ISO IEC 14496-1 MPEG-4 System Standard | |
bitfield ElementaryStreamDescriptorFlags { | |
depends_on_es : 1; | |
has_url : 1; | |
has_ocr_es : 1; | |
stream_priority : 5; | |
} [[inline,left_to_right]]; | |
using DecoderConfigDescriptor; | |
using SyncLayerConfigDescriptor; | |
struct ElementaryStreamDescriptorChildSelector { | |
u8 tag [[hidden]]; | |
$ = addressof(this); | |
if (tag == 0x04) { | |
DecoderConfigDescriptor dc_desc; | |
} else if (tag == 0x06) { | |
SyncLayerConfigDescriptor sl_config; | |
} else { | |
UnknownDescriptor unknown; | |
} | |
} [[inline]]; | |
struct ElementaryStreamDescriptor : Descriptor { | |
if (tag != 0x03) { | |
std::print("esds child was not an ES_Desc."); | |
} | |
u16 es_id; | |
ElementaryStreamDescriptorFlags flags; | |
if (flags.depends_on_es) { | |
u16 depends_on_es_id; | |
} | |
if (flags.has_url) { | |
u8 url_length; | |
char url[url_length]; | |
} | |
if (flags.has_ocr_es) { | |
u16 ocr_es_id; | |
} | |
ElementaryStreamDescriptorChildSelector children[while (contents_size(addressof(es_id), instance_size) > 2)]; | |
$ += contents_size(addressof(es_id), instance_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Descriptor -> DecoderConfigDescriptor | |
bitfield DecoderConfigDescriptorFlags { | |
stream_type : 6; | |
up_stream : 1; | |
padding : 1; | |
} [[inline,left_to_right]]; | |
// https://mp4ra.org/#/object_types | |
enum ObjectTypeIndication : u8 { | |
Forbidden = 0x00, | |
Systems = 0x01, | |
MPEG_4_Visual = 0x20, | |
AVC = 0x21, | |
MP4A_LATM = 0x40, | |
AAC_Main = 0x66, | |
AAC_LC = 0x67, | |
MPEG_2_Audio = 0x69, // MP3 | |
MPEG_1_Audio = 0x6B, // MP3 | |
}; | |
using DecoderSpecificInfo; | |
using AudioSpecificConfig; | |
using ProfileLevelIndicationIndexDescriptor; | |
struct DecoderConfigDescriptorChildSelector<auto type_indication> { | |
u8 tag [[hidden]]; | |
$ = addressof(this); | |
if (tag == 0x05) { | |
if (type_indication == ObjectTypeIndication::MP4A_LATM) { | |
AudioSpecificConfig dec_specific; | |
} else { | |
DecoderSpecificInfo dec_specific; | |
} | |
} else if (tag == 0x14) { | |
ProfileLevelIndicationIndexDescriptor profile_level_desc; | |
} else { | |
UnknownDescriptor unknown; | |
} | |
} [[inline]]; | |
struct DecoderConfigDescriptor : Descriptor { | |
ObjectTypeIndication object_type_indication; | |
DecoderConfigDescriptorFlags flags; | |
u24 buffer_size_db; | |
u32 max_bit_rate; | |
u32 average_bit_rate; | |
DecoderConfigDescriptorChildSelector<object_type_indication> children[while (contents_size(addressof(object_type_indication), instance_size) > 2)]; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Descriptor -> DecoderConfigDescriptor -> DecoderSpecificInfo | |
struct DecoderSpecificInfo : Descriptor { | |
u8 data[contents_size($, instance_size)] [[sealed]]; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Descriptor -> DecoderConfigDescriptor -> AudioSpecificConfig | |
bitfield AudioSpecificConfigBitfield { | |
// This needs to read from the big end first, [[left_to_right]] doesn't work. | |
audio_object_type : 5; | |
if (audio_object_type == 31) { | |
audio_object_type_ext : 6; | |
} | |
sampling_frequency_index : 4; | |
if (sampling_frequency_index == 0xf) { | |
sampling_frequency : 24; | |
} | |
// TODO: Match based on audio_object_type, spec has plenty of options. | |
}; | |
struct AudioSpecificConfig : Descriptor { | |
AudioSpecificConfigBitfield object_type; | |
$ += contents_size(addressof(object_type), instance_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Descriptor -> DecoderConfigDescriptor -> ProfileLevelIndicationIndexDescriptor | |
struct ProfileLevelIndicationIndexDescriptor : Descriptor { | |
u8 index; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> * -> esds -> ES_Desc -> SLConfigDescriptor | |
bitfield SyncLayerConfigDescriptorFlags { | |
use_access_unit_start : 1; | |
use_access_unit_end : 1; | |
use_random_access_point : 1; | |
has_random_access_units_only : 1; | |
use_padding : 1; | |
use_timestamps : 1; | |
use_idle : 1; | |
duration_present : 1; | |
} [[inline,left_to_right]]; | |
bitfield SyncLayerConfigDescriptorLengths { | |
degradation_priority_length : 4; | |
au_seq_num_length : 5; | |
packet_seq_num_length : 5; | |
padding : 2; | |
} [[inline,left_to_right]]; | |
struct SyncLayerConfigDescriptor : Descriptor { | |
u8 predefined; | |
if (predefined == 0) { | |
SyncLayerConfigDescriptorFlags flags; | |
u32 timestamp_resolution; | |
u32 ocr_resolution; | |
u8 timestamp_length; | |
u8 ocr_length; | |
u8 au_length; | |
u8 instant_bitrate_length; | |
SyncLayerConfigDescriptorLengths other_lengths; | |
if (flags.duration_present) { | |
u32 time_scale; | |
u16 access_unit_duration; | |
u16 composition_unit_duration; | |
} | |
if (use_timestamps) { | |
// start_decoding_time_stamp : timestamp_length; | |
// start_composition_time_stamp : timestamp_length; | |
padding[((timestamp_length * 2) + 7) / 8]; | |
} | |
} | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsd -> (visual) -> fiel | |
struct FieldHandlingBox : Box { | |
u8 scan_count; | |
u8 scan_order; | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stts | |
struct TimeToSampleEntry { | |
u32 sample_count; | |
u32 sample_delta; | |
} [[format("format_stts_entry")]]; | |
fn format_stts_entry(ref auto stts_entry) { | |
return std::format("count = {}, delta = {}", stts_entry.sample_count, stts_entry.sample_delta); | |
}; | |
struct TimeToSampleBox : FullBox { | |
u32 entry_count; | |
TimeToSampleEntry entries[entry_count] [[format("count_samples")]]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
fn count_samples(ref auto entries) { | |
u128 count = 0; | |
for (u32 i = 0, i < std::core::member_count(entries), i += 1) { | |
count += entries[i].sample_count; | |
} | |
return std::format("{} samples total", count); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> ctts | |
struct CompositionOffsetEntry { | |
u32 sample_count; | |
u32 sample_offset; | |
} [[format("format_ctts_entry")]]; | |
fn format_ctts_entry(ref auto stts_entry) { | |
return std::format("count = {}, offset = {}", stts_entry.sample_count, stts_entry.sample_offset); | |
}; | |
struct CompositionOffsetBox : FullBox { | |
u32 entry_count; | |
CompositionOffsetEntry entries[entry_count] [[format("count_samples")]]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stss | |
struct SyncSampleBox : FullBox { | |
u32 entry_count; | |
u32 sample_numbers[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsc | |
struct SampleToChunkBoxEntry { | |
u32 first_chunk; | |
u32 samples_per_chunk; | |
u32 sample_description_index; | |
} [[format("format_stsc_entry")]]; | |
fn format_stsc_entry(ref auto stsc_entry) { | |
return std::format("first_chunk = {}, samples_per_chunk = {}, sample_description_index = {}", | |
stsc_entry.first_chunk, stsc_entry.samples_per_chunk, stsc_entry.sample_description_index); | |
}; | |
struct SampleToChunkBox : FullBox { | |
u32 entry_count; | |
SampleToChunkBoxEntry entries[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stsz/stz2 | |
struct SampleSizeBox : FullBox { | |
u32 sample_size; | |
u32 sample_count; | |
if (sample_size == 0) { | |
u32 entry_size[sample_count]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct CompactSampleSizeBox : FullBox { | |
padding[sizeof(u24)]; | |
u8 field_size; | |
u32 sample_count; | |
if (field_size == 4) { | |
type::Nibbles entries[(sample_count + 1) / 2]; | |
} else if (field_size == 8) { | |
u8 entries[sample_count]; | |
} else if (field_size == 16) { | |
u16 entries[sample_count]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> stco/co64 | |
struct DataPointer32 { | |
u8* pointer : u32; | |
} [[inline]]; | |
struct DataPointer64 { | |
u8* pointer : u64; | |
} [[inline]]; | |
struct ChunkOffsetBox : FullBox { | |
u32 entry_count; | |
DataPointer32 chunk_offset[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
struct ChunkLargeOffsetBox : FullBox { | |
u32 entry_count; | |
DataPointer64 chunk_offset[entry_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> sgpd -> * | |
const u32 SGPD_NO_LENGTH = 0xFFFFFFFF; | |
struct SampleGroupDescriptionEntry<auto sgpd_default_length> { | |
u32 length = sgpd_default_length; | |
if (length == 0 && length != SGPD_NO_LENGTH) { | |
u32 description_length; | |
length = description_length; | |
} | |
} [[inline]]; | |
struct UnknownSampleGroupDescriptionEntry<auto sgpd_default_length> { | |
SampleGroupDescriptionEntry<sgpd_default_length> inherited; | |
if (inherited.length != SGPD_NO_LENGTH) { | |
$ += contents_size($, inherited.length); | |
} | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> sgpd -> (roll|prol) | |
struct RollEntry<auto sgpd_default_length> { | |
SampleGroupDescriptionEntry<sgpd_default_length> inherited; | |
u16 roll_distance; | |
} [[format("format_roll")]]; | |
fn format_roll(ref auto roll) { | |
return std::format("roll_distance = {}", roll.roll_distance); | |
}; | |
// moov -> trak -> mdia -> minf -> stbl -> sgpd | |
struct SampleGroupDescriptionBox : FullBox { | |
char grouping_type[4]; | |
if (version == 1) { | |
u32 default_length; | |
} | |
if (version >= 2) { | |
u32 default_sample_description_index; | |
} | |
u32 entry_count; | |
u32 the_default_length = version == 1 ? default_length : (contents_size(addressof(this), box_size) / entry_count); | |
if (grouping_type == "roll" || grouping_type == "prol") { | |
RollEntry<the_default_length> entries[entry_count]; | |
} else { | |
UnknownSampleGroupDescriptionEntry<the_default_length> entries[entry_count]; | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moov -> udta | |
struct UserDataChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "meta") { | |
MetaBox meta; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct UserDataBox : Box { | |
UserDataChildBoxSelector children[while (contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// sidx | |
bitfield SegmentReferenceTypeAndSize { | |
reference_type : 1; | |
referenced_size : 31; | |
} [[inline]]; | |
bitfield SegmentReferenceSAP { | |
starts_with_sap : 1; | |
sap_type : 3; | |
sap_delta_time : 28; | |
} [[inline]]; | |
struct SegmentReference { | |
SegmentReferenceTypeAndSize reference_type_and_size; | |
u32 subsegment_duration; | |
SegmentReferenceSAP sap; | |
}; | |
struct SegmentIndexBox : FullBox { | |
u32 reference_id; | |
u32 timescale; | |
if (version == 0) { | |
u32 earliest_presentation_time; | |
u32 first_offset; | |
} else { | |
u64 earliest_presentation_time; | |
u64 first_offset; | |
} | |
padding[sizeof(u16)]; | |
u16 reference_count; | |
SegmentReference references[reference_count]; | |
}; | |
// moof | |
using MovieFragmentHeaderBox; | |
using TrackFragmentBox; | |
struct MovieFragmentChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "mfhd") { | |
MovieFragmentHeaderBox mfhd; | |
} else if (box_header.box_type == "traf") { | |
TrackFragmentBox traf; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct MovieFragmentBox : Box { | |
MovieFragmentChildBoxSelector children[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moof -> mfhd | |
struct MovieFragmentHeaderBox : FullBox { | |
u32 sequence_number; | |
}; | |
// moof -> traf | |
using TrackFragmentHeaderBox; | |
using TrackRunBox; | |
using TrackFragmentBaseMediaDecodeTimeBox; | |
struct TrackFragmentChildBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "tfhd") { | |
TrackFragmentHeaderBox tfhd; | |
} else if (box_header.box_type == "trun") { | |
TrackRunBox trun; | |
} else if (box_header.box_type == "tfdt") { | |
TrackFragmentBaseMediaDecodeTimeBox tfdt; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} | |
} [[inline]]; | |
struct TrackFragmentBox : Box { | |
TrackFragmentChildBoxSelector children[while(contents_size(addressof(this), box_size) > MIN_BOX_SIZE)]; | |
}; | |
// moof -> traf -> tfhd | |
bitfield TrackFragmentHeaderFlags { | |
base_data_offset_present : 1; | |
sample_description_index_present : 1; | |
padding : 1; | |
default_sample_duration_present : 1; | |
default_sample_size_present : 1; | |
default_sample_flags_present : 1; | |
padding : 10; | |
duration_is_empty : 1; | |
default_base_is_moof : 1; | |
padding : 6; | |
} [[right_to_left]]; | |
struct TrackFragmentHeaderBox : Box { | |
// FullBox fields | |
u8 version; | |
TrackFragmentHeaderFlags flags; | |
// traf fields | |
if (version == 0) { | |
u32 track_id; | |
if (flags.base_data_offset_present) { | |
u64 base_data_offset; | |
} | |
if (flags.sample_description_index_present) { | |
u32 sample_description_index; | |
} | |
if (flags.default_sample_duration_present) { | |
u32 default_sample_duration; | |
} | |
if (flags.default_sample_size_present) { | |
u32 default_sample_size; | |
} | |
if (flags.default_sample_flags_present) { | |
u32 default_sample_flags; | |
} | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moof -> traf -> trun | |
bitfield TrackRunFlags { | |
data_offset_present : 1; | |
padding : 2; | |
first_sample_flags_present : 1; | |
padding : 4; | |
sample_duration_present : 1; | |
sample_size_present : 1; | |
sample_flags_present : 1; | |
sample_composition_time_offset_present : 1; | |
padding : 12; | |
} [[right_to_left]]; | |
struct TrackRunSample<auto trun_version, auto trun_flags> | |
{ | |
if (trun_flags.sample_duration_present) { | |
u32 sample_duration; | |
} | |
if (trun_flags.sample_size_present) { | |
u32 sample_size; | |
} | |
if (trun_flags.sample_flags_present) { | |
FragmentSampleFlags sample_flags; | |
} | |
if (trun_flags.sample_composition_time_offset_present) { | |
if (trun_version == 0) { | |
u32 sample_composition_time_offset; | |
} else { | |
s32 sample_composition_time_offset; | |
} | |
} | |
}; | |
struct TrackRunBox : Box { | |
// FullBox | |
u8 version; | |
TrackRunFlags flags; | |
// trun | |
u32 sample_count; | |
if (flags.data_offset_present) { | |
s32 data_offset; | |
} | |
if (flags.first_sample_flags_present) { | |
FragmentSampleFlags first_sample_flags; | |
} | |
TrackRunSample<version, flags> samples[sample_count]; | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// moof -> traf -> tfdt | |
struct TrackFragmentBaseMediaDecodeTimeBox : FullBox { | |
if (version < 2) { | |
if (version == 1) { | |
u64 base_media_decode_time; | |
} else { | |
u32 base_media_decode_time; | |
} | |
} | |
$ += contents_size(addressof(this), box_size); | |
}; | |
// mdat | |
struct MediaDataBox : Box { | |
u8 data[contents_size(addressof(this), box_size)] [[sealed]]; | |
}; | |
// top level | |
struct TopLevelBoxSelector { | |
Box box_header [[hidden]]; | |
$ = addressof(this); | |
if ($ + contents_size(addressof(this), box_header.box_size) <= std::mem::size()) { | |
if (box_header.box_type == "ftyp") { | |
FileTypeBox ftyp; | |
} else if (box_header.box_type == "meta") { | |
MetaBox meta; | |
} else if (box_header.box_type == "moov") { | |
MovieBox moov; | |
} else if (box_header.box_type == "sidx") { | |
SegmentIndexBox sidx; | |
} else if (box_header.box_type == "moof") { | |
MovieFragmentBox moof; | |
} else if (box_header.box_type == "mdat") { | |
MediaDataBox mdat; | |
} else { | |
UnknownBox unknown [[name(box_header.box_type)]]; | |
} | |
} else { | |
$ = std::mem::size(); | |
} | |
} [[inline]]; | |
// top level | |
TopLevelBoxSelector boxes[while(std::mem::size() - $ > MIN_BOX_SIZE)] @ 0x00 [[format("format_box_array")]]; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment