737 lines
23 KiB
Python
Raw Normal View History

2025-03-18 00:17:27 +05:30
#!/usr/bin/env python
"""
Copyright 2016 beardypig
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import logging
from uuid import UUID
from construct import *
from construct.lib import *
2025-03-18 00:23:51 +05:30
from .adapters import ISO6392TLanguageCode, MaskedInteger, UUIDBytes
from .subconstructs import TellPlusSizeOf, TellMinusSizeOf
2025-03-18 00:17:27 +05:30
log = logging.getLogger(__name__)
UNITY_MATRIX = [0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000]
# Header box
FileTypeBox = Struct(
2025-03-18 00:23:51 +05:30
"major_brand" / PaddedString(4, "ascii"),
2025-03-18 00:17:27 +05:30
"minor_version" / Int32ub,
2025-03-18 00:23:51 +05:30
"compatible_brands" / GreedyRange(PaddedString(4, "ascii")),
2025-03-18 00:17:27 +05:30
)
SegmentTypeBox = Struct(
2025-03-18 00:23:51 +05:30
"major_brand" / PaddedString(4, "ascii"),
2025-03-18 00:17:27 +05:30
"minor_version" / Int32ub,
2025-03-18 00:23:51 +05:30
"compatible_brands" / GreedyRange(PaddedString(4, "ascii")),
2025-03-18 00:17:27 +05:30
)
# Catch find boxes
RawBox = Struct(
"data" / Default(GreedyBytes, b"")
)
FreeBox = Struct(
"data" / GreedyBytes
)
SkipBox = Struct(
"data" / GreedyBytes
)
# Movie boxes, contained in a moov Box
MovieHeaderBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Default(Int24ub, 0),
2025-03-18 00:23:51 +05:30
"creation_time" / Default(Switch(this.version, {0: Int32ub, 1: Int64ub}), 0),
"modification_time" / Default(Switch(this.version, {0: Int32ub, 1: Int64ub}), 0),
"timescale" / Default(Int32ub, 10000000),
"duration" / Switch(this.version, {0: Int32ub, 1: Int64ub}),
2025-03-18 00:17:27 +05:30
"rate" / Default(Int32sb, 65536),
"volume" / Default(Int16sb, 256),
# below could be just Padding(10) but why not
2025-03-18 00:23:51 +05:30
Const(0, Int16ub),
Const(0, Int32ub),
Const(0, Int32ub),
2025-03-18 00:17:27 +05:30
"matrix" / Default(Int32sb[9], UNITY_MATRIX),
"pre_defined" / Default(Int32ub[6], [0] * 6),
"next_track_ID" / Default(Int32ub, 0xffffffff)
)
# Track boxes, contained in trak box
TrackHeaderBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Default(Int24ub, 1),
2025-03-18 00:23:51 +05:30
"creation_time" / Default(Switch(this.version, {0: Int32ub, 1: Int64ub}), 0),
"modification_time" / Default(Switch(this.version, {0: Int32ub, 1: Int64ub}), 0),
"track_ID" / Default(Int32ub, 1),
Padding(4),
"duration" / Default(Switch(this.version, {0: Int32ub, 1: Int64ub}), 0),
2025-03-18 00:17:27 +05:30
Padding(8),
"layer" / Default(Int16sb, 0),
"alternate_group" / Default(Int16sb, 0),
"volume" / Default(Int16sb, 0),
Padding(2),
"matrix" / Default(Array(9, Int32sb), UNITY_MATRIX),
"width" / Default(Int32ub, 0),
"height" / Default(Int32ub, 0),
)
HDSSegmentBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Default(Int24ub, 0),
"info_version" / Int32ub,
2025-03-18 00:23:51 +05:30
"flags" / BitStruct(
2025-03-18 00:17:27 +05:30
Padding(1),
"profile" / Flag,
"live" / Flag,
"update" / Flag,
Padding(4)
),
"time_scale" / Int32ub,
"current_media_time" / Int64ub,
"smpte_time_code_offset" / Int64ub,
2025-03-18 00:23:51 +05:30
"movie_identifier" / CString("ascii"),
"server_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
"quality_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
"drm_data" / CString("ascii"),
"metadata" / CString("ascii"),
2025-03-18 00:17:27 +05:30
"segment_run_table" / PrefixedArray(Int8ub, LazyBound(lambda x: Box)),
"fragment_run_table" / PrefixedArray(Int8ub, LazyBound(lambda x: Box))
)
HDSSegmentRunBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Default(Int24ub, 0),
2025-03-18 00:23:51 +05:30
"quality_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
2025-03-18 00:17:27 +05:30
"segment_run_enteries" / PrefixedArray(Int32ub, Struct(
"first_segment" / Int32ub,
"fragments_per_segment" / Int32ub
))
)
HDSFragmentRunBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / BitStruct(
Padding(23),
"update" / Flag
),
"time_scale" / Int32ub,
2025-03-18 00:23:51 +05:30
"quality_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
2025-03-18 00:17:27 +05:30
"fragment_run_enteries" / PrefixedArray(Int32ub, Struct(
"first_fragment" / Int32ub,
"first_fragment_timestamp" / Int64ub,
"fragment_duration" / Int32ub,
"discontinuity" / If(this.fragment_duration == 0, Int8ub)
))
)
# Boxes contained by Media Box
MediaHeaderBox = Struct(
"version" / Default(Int8ub, 0),
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"creation_time" / IfThenElse(this.version == 1, Int64ub, Int32ub),
"modification_time" / IfThenElse(this.version == 1, Int64ub, Int32ub),
"timescale" / Int32ub,
"duration" / IfThenElse(this.version == 1, Int64ub, Int32ub),
2025-03-18 00:23:51 +05:30
"language" / ISO6392TLanguageCode(Int16ub),
Padding(2, pattern=b"\x00")
2025-03-18 00:17:27 +05:30
)
HandlerReferenceBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
Padding(4, pattern=b"\x00"),
2025-03-18 00:23:51 +05:30
"handler_type" / PaddedString(4, "ascii"),
2025-03-18 00:17:27 +05:30
Padding(12, pattern=b"\x00"), # Int32ub[3]
2025-03-18 00:23:51 +05:30
"name" / CString("utf8")
2025-03-18 00:17:27 +05:30
)
# Boxes contained by Media Info Box
VideoMediaHeaderBox = Struct(
"version" / Default(Int8ub, 0),
2025-03-18 00:23:51 +05:30
"flags" / Const(1, Int24ub),
2025-03-18 00:17:27 +05:30
"graphics_mode" / Default(Int16ub, 0),
"opcolor" / Struct(
"red" / Default(Int16ub, 0),
"green" / Default(Int16ub, 0),
"blue" / Default(Int16ub, 0),
),
)
2025-03-18 00:23:51 +05:30
DataEntryUrlBox = Struct(
"version" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"flags" / BitStruct(
Padding(23), "self_contained" / Rebuild(Flag, ~this._.location)
),
2025-03-18 00:23:51 +05:30
"location" / If(~this.flags.self_contained, CString("utf8")),
)
2025-03-18 00:17:27 +05:30
2025-03-18 00:23:51 +05:30
DataEntryUrnBox = Struct(
"version" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"flags" / BitStruct(
Padding(23), "self_contained" / Rebuild(Flag, ~(this._.name & this._.location))
),
2025-03-18 00:23:51 +05:30
"name" / If(this.flags == 0, CString("utf8")),
"location" / If(this.flags == 0, CString("utf8")),
)
2025-03-18 00:17:27 +05:30
DataReferenceBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"flags" / Default(Int24ub, 0),
2025-03-18 00:23:51 +05:30
"data_entries" / PrefixedArray(Int32ub, LazyBound(lambda: Box)),
2025-03-18 00:17:27 +05:30
)
# Sample Table boxes (stbl)
MP4ASampleEntryBox = Struct(
"version" / Default(Int16ub, 0),
2025-03-18 00:23:51 +05:30
"revision" / Const(0, Int16ub),
"vendor" / Const(0, Int32ub),
2025-03-18 00:17:27 +05:30
"channels" / Default(Int16ub, 2),
"bits_per_sample" / Default(Int16ub, 16),
"compression_id" / Default(Int16sb, 0),
2025-03-18 00:23:51 +05:30
"packet_size" / Const(0, Int16ub),
2025-03-18 00:17:27 +05:30
"sampling_rate" / Int16ub,
Padding(2)
)
AAVC = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(1, Int8ub),
2025-03-18 00:17:27 +05:30
"profile" / Int8ub,
"compatibility" / Int8ub,
"level" / Int8ub,
2025-03-18 00:23:51 +05:30
"flags" / BitStruct(
2025-03-18 00:17:27 +05:30
Padding(6, pattern=b'\x01'),
"nal_unit_length_field" / Default(BitsInteger(2), 3),
),
2025-03-18 00:23:51 +05:30
"sps" / Default(PrefixedArray(MaskedInteger(Int8ub), PascalString(Int16ub, "ascii")), []),
"pps" / Default(PrefixedArray(Int8ub, PascalString(Int16ub, "ascii")), [])
2025-03-18 00:17:27 +05:30
)
HVCC = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(1, Int8ub),
"flags" / BitStruct(
2025-03-18 00:17:27 +05:30
"profile_space" / BitsInteger(2),
"general_tier_flag" / BitsInteger(1),
"general_profile" / BitsInteger(5),
"general_profile_compatibility_flags" / BitsInteger(32),
"general_constraint_indicator_flags" / BitsInteger(48),
"general_level" / BitsInteger(8),
Padding(4, pattern=b'\xff'),
"min_spatial_segmentation" / BitsInteger(12),
Padding(6, pattern=b'\xff'),
"parallelism_type" / BitsInteger(2),
Padding(6, pattern=b'\xff'),
"chroma_format" / BitsInteger(2),
Padding(5, pattern=b'\xff'),
"luma_bit_depth" / BitsInteger(3),
Padding(5, pattern=b'\xff'),
"chroma_bit_depth" / BitsInteger(3),
"average_frame_rate" / BitsInteger(16),
"constant_frame_rate" / BitsInteger(2),
"num_temporal_layers" / BitsInteger(3),
"temporal_id_nested" / BitsInteger(1),
"nalu_length_size" / BitsInteger(2),
),
# TODO: parse NALUs
"raw_bytes" / GreedyBytes
)
AVC1SampleEntryBox = Struct(
"version" / Default(Int16ub, 0),
2025-03-18 00:23:51 +05:30
"revision" / Const(0, Int16ub),
"vendor" / Default(PaddedString(4, "ascii"), "brdy"),
2025-03-18 00:17:27 +05:30
"temporal_quality" / Default(Int32ub, 0),
"spatial_quality" / Default(Int32ub, 0),
"width" / Int16ub,
"height" / Int16ub,
"horizontal_resolution" / Default(Int16ub, 72), # TODO: actually a fixed point decimal
Padding(2),
"vertical_resolution" / Default(Int16ub, 72), # TODO: actually a fixed point decimal
Padding(2),
2025-03-18 00:23:51 +05:30
"data_size" / Const(0, Int32ub),
2025-03-18 00:17:27 +05:30
"frame_count" / Default(Int16ub, 1),
2025-03-18 00:23:51 +05:30
"compressor_name" / Default(PaddedString(32, "ascii"), None),
2025-03-18 00:17:27 +05:30
"depth" / Default(Int16ub, 24),
"color_table_id" / Default(Int16sb, -1),
2025-03-18 00:23:51 +05:30
"avc_data" / Prefixed(Int32ub, Struct(
"type" / PaddedString(4, "ascii"),
"data" / Switch(this.type, {
"avcC": AAVC,
"hvcC": HVCC,
}, GreedyBytes)
), includelength=True),
2025-03-18 00:17:27 +05:30
"sample_info" / LazyBound(lambda _: GreedyRange(Box))
)
2025-03-18 00:23:51 +05:30
SampleEntryBox = Prefixed(Int32ub, Struct(
"format" / PaddedString(4, "ascii"),
2025-03-18 00:17:27 +05:30
Padding(6, pattern=b"\x00"),
"data_reference_index" / Default(Int16ub, 1),
2025-03-18 00:23:51 +05:30
"data" / Switch(this.format, {
"ec-3": MP4ASampleEntryBox,
"mp4a": MP4ASampleEntryBox,
"enca": MP4ASampleEntryBox,
"avc1": AVC1SampleEntryBox,
"encv": AVC1SampleEntryBox,
"wvtt": Struct("children" / LazyBound(lambda: GreedyRange(Box)))
}, GreedyBytes)
), includelength=True)
2025-03-18 00:17:27 +05:30
BitRateBox = Struct(
"bufferSizeDB" / Int32ub,
"maxBitrate" / Int32ub,
"avgBirate" / Int32ub,
)
SampleDescriptionBox = Struct(
"version" / Default(Int8ub, 0),
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
"entries" / PrefixedArray(Int32ub, LazyBound(lambda: Box))
2025-03-18 00:17:27 +05:30
)
SampleSizeBox = Struct(
"version" / Int8ub,
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"sample_size" / Int32ub,
"sample_count" / Int32ub,
"entry_sizes" / If(this.sample_size == 0, Array(this.sample_count, Int32ub))
)
SampleSizeBox2 = Struct(
"version" / Int8ub,
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
Padding(3, pattern=b"\x00"),
"field_size" / Int8ub,
"sample_count" / Int24ub,
"entries" / Array(this.sample_count, Struct(
"entry_size" / LazyBound(lambda ctx: globals()["Int%dub" % ctx.field_size])
))
)
SampleDegradationPriorityBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
)
TimeToSampleBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"entries" / Default(PrefixedArray(Int32ub, Struct(
"sample_count" / Int32ub,
"sample_delta" / Int32ub,
)), [])
)
SyncSampleBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"entries" / Default(PrefixedArray(Int32ub, Struct(
"sample_number" / Int32ub,
)), [])
)
2025-03-18 00:23:51 +05:30
CompositionOffsetBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Const(0, Int24ub),
"entries" / Switch(this.version, {
0: Default(PrefixedArray(Int32ub, Struct(
"sample_count" / Int32ub,
"sample_offset" / Int32ub,
)), []),
1: Default(PrefixedArray(Int32ub, Struct(
"sample_count" / Int32ub,
"sample_offset" / Int32sb,
)), [])
})
)
2025-03-18 00:17:27 +05:30
SampleToChunkBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"entries" / Default(PrefixedArray(Int32ub, Struct(
"first_chunk" / Int32ub,
"samples_per_chunk" / Int32ub,
"sample_description_index" / Int32ub,
)), [])
)
ChunkOffsetBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"entries" / Default(PrefixedArray(Int32ub, Struct(
"chunk_offset" / Int32ub,
)), [])
)
ChunkLargeOffsetBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"entries" / PrefixedArray(Int32ub, Struct(
"chunk_offset" / Int64ub,
))
)
# Movie Fragment boxes, contained in moof box
MovieFragmentHeaderBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"sequence_number" / Int32ub
)
TrackFragmentBaseMediaDecodeTimeBox = Struct(
"version" / Int8ub,
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"baseMediaDecodeTime" / Switch(this.version, {1: Int64ub, 0: Int32ub})
)
TrackSampleFlags = BitStruct(
Padding(4),
"is_leading" / Default(Enum(BitsInteger(2), UNKNOWN=0, LEADINGDEP=1, NOTLEADING=2, LEADINGNODEP=3, default=0), 0),
"sample_depends_on" / Default(Enum(BitsInteger(2), UNKNOWN=0, DEPENDS=1, NOTDEPENDS=2, RESERVED=3, default=0), 0),
"sample_is_depended_on" / Default(Enum(BitsInteger(2), UNKNOWN=0, NOTDISPOSABLE=1, DISPOSABLE=2, RESERVED=3, default=0), 0),
"sample_has_redundancy" / Default(Enum(BitsInteger(2), UNKNOWN=0, REDUNDANT=1, NOTREDUNDANT=2, RESERVED=3, default=0), 0),
"sample_padding_value" / Default(BitsInteger(3), 0),
"sample_is_non_sync_sample" / Default(Flag, False),
"sample_degradation_priority" / Default(BitsInteger(16), 0),
)
TrackRunBox = Struct(
"version" / Int8ub,
"flags" / BitStruct(
Padding(12),
"sample_composition_time_offsets_present" / Flag,
"sample_flags_present" / Flag,
"sample_size_present" / Flag,
"sample_duration_present" / Flag,
Padding(5),
"first_sample_flags_present" / Flag,
Padding(1),
"data_offset_present" / Flag,
),
"sample_count" / Int32ub,
"data_offset" / Default(If(this.flags.data_offset_present, Int32sb), None),
"first_sample_flags" / Default(If(this.flags.first_sample_flags_present, Int32ub), None),
"sample_info" / Array(this.sample_count, Struct(
"sample_duration" / If(this._.flags.sample_duration_present, Int32ub),
"sample_size" / If(this._.flags.sample_size_present, Int32ub),
"sample_flags" / If(this._.flags.sample_flags_present, TrackSampleFlags),
"sample_composition_time_offsets" / If(
this._.flags.sample_composition_time_offsets_present,
IfThenElse(this._.version == 0, Int32ub, Int32sb)
),
))
)
TrackFragmentHeaderBox = Struct(
"version" / Int8ub,
"flags" / BitStruct(
Padding(6),
"default_base_is_moof" / Flag,
"duration_is_empty" / Flag,
Padding(10),
"default_sample_flags_present" / Flag,
"default_sample_size_present" / Flag,
"default_sample_duration_present" / Flag,
Padding(1),
"sample_description_index_present" / Flag,
"base_data_offset_present" / Flag,
),
"track_ID" / Int32ub,
"base_data_offset" / Default(If(this.flags.base_data_offset_present, Int64ub), None),
"sample_description_index" / Default(If(this.flags.sample_description_index_present, Int32ub), None),
"default_sample_duration" / Default(If(this.flags.default_sample_duration_present, Int32ub), None),
"default_sample_size" / Default(If(this.flags.default_sample_size_present, Int32ub), None),
"default_sample_flags" / Default(If(this.flags.default_sample_flags_present, TrackSampleFlags), None),
)
MovieExtendsHeaderBox = Struct(
"version" / Default(Int8ub, 0),
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"fragment_duration" / IfThenElse(this.version == 1,
Default(Int64ub, 0),
Default(Int32ub, 0))
)
TrackExtendsBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"track_ID" / Int32ub,
"default_sample_description_index" / Default(Int32ub, 1),
"default_sample_duration" / Default(Int32ub, 0),
"default_sample_size" / Default(Int32ub, 0),
"default_sample_flags" / Default(TrackSampleFlags, Container()),
)
SegmentIndexBox = Struct(
"version" / Int8ub,
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"reference_ID" / Int32ub,
"timescale" / Int32ub,
"earliest_presentation_time" / IfThenElse(this.version == 0, Int32ub, Int64ub),
"first_offset" / IfThenElse(this.version == 0, Int32ub, Int64ub),
Padding(2),
"reference_count" / Int16ub,
"references" / Array(this.reference_count, BitStruct(
"reference_type" / Enum(BitsInteger(1), INDEX=1, MEDIA=0),
"referenced_size" / BitsInteger(31),
"segment_duration" / BitsInteger(32),
"starts_with_SAP" / Flag,
"SAP_type" / BitsInteger(3),
"SAP_delta_time" / BitsInteger(28),
))
)
SampleAuxiliaryInformationSizesBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"flags" / BitStruct(
Padding(23),
"has_aux_info_type" / Flag,
),
# Optional fields
"aux_info_type" / Default(If(this.flags.has_aux_info_type, Int32ub), None),
"aux_info_type_parameter" / Default(If(this.flags.has_aux_info_type, Int32ub), None),
"default_sample_info_size" / Int8ub,
"sample_count" / Int32ub,
# only if sample default_sample_info_size is 0
"sample_info_sizes" / If(this.default_sample_info_size == 0,
Array(this.sample_count, Int8ub))
)
SampleAuxiliaryInformationOffsetsBox = Struct(
"version" / Int8ub,
"flags" / BitStruct(
Padding(23),
"has_aux_info_type" / Flag,
),
# Optional fields
"aux_info_type" / Default(If(this.flags.has_aux_info_type, Int32ub), None),
"aux_info_type_parameter" / Default(If(this.flags.has_aux_info_type, Int32ub), None),
# Short offsets in version 0, long in version 1
"offsets" / PrefixedArray(Int32ub, Switch(this.version, {0: Int32ub, 1: Int64ub}))
)
# Movie data box
MovieDataBox = Struct(
"data" / GreedyBytes
)
# Media Info Box
SoundMediaHeaderBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"balance" / Default(Int16sb, 0),
2025-03-18 00:23:51 +05:30
"reserved" / Const(0, Int16ub)
2025-03-18 00:17:27 +05:30
)
# DASH Boxes
ProtectionSystemHeaderBox = Struct(
"version" / Rebuild(Int8ub, lambda ctx: 1 if (hasattr(ctx, "key_IDs") and ctx.key_IDs) else 0),
2025-03-18 00:23:51 +05:30
"flags" / Const(0, Int24ub),
2025-03-18 00:17:27 +05:30
"system_ID" / UUIDBytes(Bytes(16)),
"key_IDs" / Default(If(this.version == 1,
PrefixedArray(Int32ub, UUIDBytes(Bytes(16)))),
None),
"init_data" / Prefixed(Int32ub, GreedyBytes)
)
TrackEncryptionBox = Struct(
"version" / Default(OneOf(Int8ub, (0, 1)), 0),
"flags" / Default(Int24ub, 0),
2025-03-18 00:23:51 +05:30
"_reserved" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"default_byte_blocks" / Default(IfThenElse(
this.version > 0,
BitStruct(
# count of encrypted blocks in the protection pattern, where each block is 16-bytes
"crypt" / Nibble,
# count of unencrypted blocks in the protection pattern
"skip" / Nibble
),
2025-03-18 00:23:51 +05:30
Const(0, Int8ub)
2025-03-18 00:17:27 +05:30
), 0),
"is_encrypted" / OneOf(Int8ub, (0, 1)),
"iv_size" / OneOf(Int8ub, (0, 8, 16)),
"key_ID" / UUIDBytes(Bytes(16)),
"constant_iv" / Default(If(
this.is_encrypted and this.iv_size == 0,
PrefixedArray(Int8ub, Byte)
), None)
)
SampleEncryptionBox = Struct(
2025-03-18 00:23:51 +05:30
"version" / Const(0, Int8ub),
2025-03-18 00:17:27 +05:30
"flags" / BitStruct(
Padding(22),
"has_subsample_encryption_info" / Flag,
Padding(1)
),
"sample_encryption_info" / PrefixedArray(Int32ub, Struct(
"iv" / Bytes(8),
# include the sub sample encryption information
"subsample_encryption_info" / Default(If(this.flags.has_subsample_encryption_info, PrefixedArray(Int16ub, Struct(
"clear_bytes" / Int16ub,
"cipher_bytes" / Int32ub
))), None)
))
)
OriginalFormatBox = Struct(
2025-03-18 00:23:51 +05:30
"original_format" / Default(PaddedString(4, "ascii"), "avc1")
2025-03-18 00:17:27 +05:30
)
SchemeTypeBox = Struct(
"version" / Default(Int8ub, 0),
"flags" / Default(Int24ub, 0),
2025-03-18 00:23:51 +05:30
"scheme_type" / Default(PaddedString(4, "ascii"), "cenc"),
2025-03-18 00:17:27 +05:30
"scheme_version" / Default(Int32ub, 0x00010000),
2025-03-18 00:23:51 +05:30
"schema_uri" / Default(If(this.flags & 1 == 1, CString("ascii")), None)
2025-03-18 00:17:27 +05:30
)
ProtectionSchemeInformationBox = Struct(
# TODO: define which children are required 'schm', 'schi' and 'tenc'
"children" / LazyBound(lambda _: GreedyRange(Box))
)
# PIFF boxes
UUIDBox = Struct(
"extended_type" / UUIDBytes(Bytes(16)),
"data" / Switch(this.extended_type, {
UUID("A2394F52-5A9B-4F14-A244-6C427C648DF4"): SampleEncryptionBox,
UUID("D08A4F18-10F3-4A82-B6C8-32D8ABA183D3"): ProtectionSystemHeaderBox,
UUID("8974DBCE-7BE7-4C51-84F9-7148F9882554"): TrackEncryptionBox
}, GreedyBytes)
)
# WebVTT boxes
CueIDBox = Struct(
"cue_id" / GreedyString("utf8")
)
CueSettingsBox = Struct(
"settings" / GreedyString("utf8")
)
CuePayloadBox = Struct(
"cue_text" / GreedyString("utf8")
)
WebVTTConfigurationBox = Struct(
"config" / GreedyString("utf8")
)
WebVTTSourceLabelBox = Struct(
"label" / GreedyString("utf8")
)
2025-03-18 00:23:51 +05:30
ContainerBoxLazy = LazyBound(lambda: ContainerBox)
2025-03-18 00:17:27 +05:30
2025-03-18 00:23:51 +05:30
Box = Prefixed(Int32ub, Struct(
2025-03-18 00:17:27 +05:30
"offset" / TellMinusSizeOf(Int32ub),
2025-03-18 00:23:51 +05:30
"type" / PaddedString(4, "ascii"),
"data" / Switch(this.type, {
"ftyp": FileTypeBox,
"styp": SegmentTypeBox,
"mvhd": MovieHeaderBox,
"moov": ContainerBoxLazy,
"moof": ContainerBoxLazy,
"mfhd": MovieFragmentHeaderBox,
"tfdt": TrackFragmentBaseMediaDecodeTimeBox,
"trun": TrackRunBox,
"tfhd": TrackFragmentHeaderBox,
"traf": ContainerBoxLazy,
"mvex": ContainerBoxLazy,
"mehd": MovieExtendsHeaderBox,
"trex": TrackExtendsBox,
"trak": ContainerBoxLazy,
"edts": ContainerBoxLazy,
"mdia": ContainerBoxLazy,
"tkhd": TrackHeaderBox,
"mdat": MovieDataBox,
"free": FreeBox,
"skip": SkipBox,
"mdhd": MediaHeaderBox,
"hdlr": HandlerReferenceBox,
"minf": ContainerBoxLazy,
"vmhd": VideoMediaHeaderBox,
"dinf": ContainerBoxLazy,
"dref": DataReferenceBox,
"url ": DataEntryUrlBox,
"urn ": DataEntryUrnBox,
"stbl": ContainerBoxLazy,
"stsd": SampleDescriptionBox,
"stsz": SampleSizeBox,
"stz2": SampleSizeBox2,
"stts": TimeToSampleBox,
"stss": SyncSampleBox,
"ctts": CompositionOffsetBox,
"stsc": SampleToChunkBox,
"stco": ChunkOffsetBox,
"co64": ChunkLargeOffsetBox,
"smhd": SoundMediaHeaderBox,
"sidx": SegmentIndexBox,
"saiz": SampleAuxiliaryInformationSizesBox,
"saio": SampleAuxiliaryInformationOffsetsBox,
"btrt": BitRateBox,
2025-03-18 00:17:27 +05:30
# dash
2025-03-18 00:23:51 +05:30
"tenc": TrackEncryptionBox,
"pssh": ProtectionSystemHeaderBox,
"senc": SampleEncryptionBox,
"sinf": ProtectionSchemeInformationBox,
"frma": OriginalFormatBox,
"schm": SchemeTypeBox,
"schi": ContainerBoxLazy,
2025-03-18 00:17:27 +05:30
# piff
2025-03-18 00:23:51 +05:30
"uuid": UUIDBox,
2025-03-18 00:17:27 +05:30
# HDS boxes
2025-03-18 00:23:51 +05:30
"abst": HDSSegmentBox,
"asrt": HDSSegmentRunBox,
"afrt": HDSFragmentRunBox,
2025-03-18 00:17:27 +05:30
# WebVTT
2025-03-18 00:23:51 +05:30
"vttC": WebVTTConfigurationBox,
"vlab": WebVTTSourceLabelBox,
"vttc": ContainerBoxLazy,
"vttx": ContainerBoxLazy,
"iden": CueIDBox,
"sttg": CueSettingsBox,
"payl": CuePayloadBox
}, default=RawBox),
"end" / TellPlusSizeOf(Int32ub)
), includelength=True)
2025-03-18 00:17:27 +05:30
ContainerBox = Struct(
"children" / GreedyRange(Box)
)
MP4 = GreedyRange(Box)