737 lines
23 KiB
Python
737 lines
23 KiB
Python
#!/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 *
|
|
|
|
from .adapters import ISO6392TLanguageCode, MaskedInteger, UUIDBytes
|
|
from .subconstructs import TellPlusSizeOf, TellMinusSizeOf
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
UNITY_MATRIX = [0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000]
|
|
|
|
|
|
# Header box
|
|
|
|
FileTypeBox = Struct(
|
|
"major_brand" / PaddedString(4, "ascii"),
|
|
"minor_version" / Int32ub,
|
|
"compatible_brands" / GreedyRange(PaddedString(4, "ascii")),
|
|
)
|
|
|
|
SegmentTypeBox = Struct(
|
|
"major_brand" / PaddedString(4, "ascii"),
|
|
"minor_version" / Int32ub,
|
|
"compatible_brands" / GreedyRange(PaddedString(4, "ascii")),
|
|
)
|
|
|
|
# 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),
|
|
"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}),
|
|
"rate" / Default(Int32sb, 65536),
|
|
"volume" / Default(Int16sb, 256),
|
|
# below could be just Padding(10) but why not
|
|
Const(0, Int16ub),
|
|
Const(0, Int32ub),
|
|
Const(0, Int32ub),
|
|
"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),
|
|
"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),
|
|
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,
|
|
"flags" / BitStruct(
|
|
Padding(1),
|
|
"profile" / Flag,
|
|
"live" / Flag,
|
|
"update" / Flag,
|
|
Padding(4)
|
|
),
|
|
"time_scale" / Int32ub,
|
|
"current_media_time" / Int64ub,
|
|
"smpte_time_code_offset" / Int64ub,
|
|
"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"),
|
|
"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),
|
|
"quality_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
|
|
"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,
|
|
"quality_entry_table" / PrefixedArray(Int8ub, CString("ascii")),
|
|
"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),
|
|
"flags" / Const(0, Int24ub),
|
|
"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),
|
|
"language" / ISO6392TLanguageCode(Int16ub),
|
|
Padding(2, pattern=b"\x00")
|
|
)
|
|
|
|
HandlerReferenceBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
Padding(4, pattern=b"\x00"),
|
|
"handler_type" / PaddedString(4, "ascii"),
|
|
Padding(12, pattern=b"\x00"), # Int32ub[3]
|
|
"name" / CString("utf8")
|
|
)
|
|
|
|
# Boxes contained by Media Info Box
|
|
|
|
VideoMediaHeaderBox = Struct(
|
|
"version" / Default(Int8ub, 0),
|
|
"flags" / Const(1, Int24ub),
|
|
"graphics_mode" / Default(Int16ub, 0),
|
|
"opcolor" / Struct(
|
|
"red" / Default(Int16ub, 0),
|
|
"green" / Default(Int16ub, 0),
|
|
"blue" / Default(Int16ub, 0),
|
|
),
|
|
)
|
|
|
|
DataEntryUrlBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / BitStruct(
|
|
Padding(23), "self_contained" / Rebuild(Flag, ~this._.location)
|
|
),
|
|
"location" / If(~this.flags.self_contained, CString("utf8")),
|
|
)
|
|
|
|
DataEntryUrnBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / BitStruct(
|
|
Padding(23), "self_contained" / Rebuild(Flag, ~(this._.name & this._.location))
|
|
),
|
|
"name" / If(this.flags == 0, CString("utf8")),
|
|
"location" / If(this.flags == 0, CString("utf8")),
|
|
)
|
|
|
|
DataReferenceBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Default(Int24ub, 0),
|
|
"data_entries" / PrefixedArray(Int32ub, LazyBound(lambda: Box)),
|
|
)
|
|
|
|
# Sample Table boxes (stbl)
|
|
|
|
MP4ASampleEntryBox = Struct(
|
|
"version" / Default(Int16ub, 0),
|
|
"revision" / Const(0, Int16ub),
|
|
"vendor" / Const(0, Int32ub),
|
|
"channels" / Default(Int16ub, 2),
|
|
"bits_per_sample" / Default(Int16ub, 16),
|
|
"compression_id" / Default(Int16sb, 0),
|
|
"packet_size" / Const(0, Int16ub),
|
|
"sampling_rate" / Int16ub,
|
|
Padding(2)
|
|
)
|
|
|
|
AAVC = Struct(
|
|
"version" / Const(1, Int8ub),
|
|
"profile" / Int8ub,
|
|
"compatibility" / Int8ub,
|
|
"level" / Int8ub,
|
|
"flags" / BitStruct(
|
|
Padding(6, pattern=b'\x01'),
|
|
"nal_unit_length_field" / Default(BitsInteger(2), 3),
|
|
),
|
|
"sps" / Default(PrefixedArray(MaskedInteger(Int8ub), PascalString(Int16ub, "ascii")), []),
|
|
"pps" / Default(PrefixedArray(Int8ub, PascalString(Int16ub, "ascii")), [])
|
|
)
|
|
|
|
HVCC = Struct(
|
|
"version" / Const(1, Int8ub),
|
|
"flags" / BitStruct(
|
|
"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),
|
|
"revision" / Const(0, Int16ub),
|
|
"vendor" / Default(PaddedString(4, "ascii"), "brdy"),
|
|
"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),
|
|
"data_size" / Const(0, Int32ub),
|
|
"frame_count" / Default(Int16ub, 1),
|
|
"compressor_name" / Default(PaddedString(32, "ascii"), None),
|
|
"depth" / Default(Int16ub, 24),
|
|
"color_table_id" / Default(Int16sb, -1),
|
|
"avc_data" / Prefixed(Int32ub, Struct(
|
|
"type" / PaddedString(4, "ascii"),
|
|
"data" / Switch(this.type, {
|
|
"avcC": AAVC,
|
|
"hvcC": HVCC,
|
|
}, GreedyBytes)
|
|
), includelength=True),
|
|
"sample_info" / LazyBound(lambda _: GreedyRange(Box))
|
|
)
|
|
|
|
SampleEntryBox = Prefixed(Int32ub, Struct(
|
|
"format" / PaddedString(4, "ascii"),
|
|
Padding(6, pattern=b"\x00"),
|
|
"data_reference_index" / Default(Int16ub, 1),
|
|
"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)
|
|
|
|
BitRateBox = Struct(
|
|
"bufferSizeDB" / Int32ub,
|
|
"maxBitrate" / Int32ub,
|
|
"avgBirate" / Int32ub,
|
|
)
|
|
|
|
SampleDescriptionBox = Struct(
|
|
"version" / Default(Int8ub, 0),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / PrefixedArray(Int32ub, LazyBound(lambda: Box))
|
|
)
|
|
|
|
SampleSizeBox = Struct(
|
|
"version" / Int8ub,
|
|
"flags" / Const(0, Int24ub),
|
|
"sample_size" / Int32ub,
|
|
"sample_count" / Int32ub,
|
|
"entry_sizes" / If(this.sample_size == 0, Array(this.sample_count, Int32ub))
|
|
)
|
|
|
|
SampleSizeBox2 = Struct(
|
|
"version" / Int8ub,
|
|
"flags" / Const(0, Int24ub),
|
|
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(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
)
|
|
|
|
TimeToSampleBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / Default(PrefixedArray(Int32ub, Struct(
|
|
"sample_count" / Int32ub,
|
|
"sample_delta" / Int32ub,
|
|
)), [])
|
|
)
|
|
|
|
SyncSampleBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / Default(PrefixedArray(Int32ub, Struct(
|
|
"sample_number" / Int32ub,
|
|
)), [])
|
|
)
|
|
|
|
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,
|
|
)), [])
|
|
})
|
|
)
|
|
|
|
SampleToChunkBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / Default(PrefixedArray(Int32ub, Struct(
|
|
"first_chunk" / Int32ub,
|
|
"samples_per_chunk" / Int32ub,
|
|
"sample_description_index" / Int32ub,
|
|
)), [])
|
|
)
|
|
|
|
ChunkOffsetBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / Default(PrefixedArray(Int32ub, Struct(
|
|
"chunk_offset" / Int32ub,
|
|
)), [])
|
|
)
|
|
|
|
ChunkLargeOffsetBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"entries" / PrefixedArray(Int32ub, Struct(
|
|
"chunk_offset" / Int64ub,
|
|
))
|
|
)
|
|
|
|
# Movie Fragment boxes, contained in moof box
|
|
|
|
MovieFragmentHeaderBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"sequence_number" / Int32ub
|
|
)
|
|
|
|
TrackFragmentBaseMediaDecodeTimeBox = Struct(
|
|
"version" / Int8ub,
|
|
"flags" / Const(0, Int24ub),
|
|
"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),
|
|
"flags" / Const(0, Int24ub),
|
|
"fragment_duration" / IfThenElse(this.version == 1,
|
|
Default(Int64ub, 0),
|
|
Default(Int32ub, 0))
|
|
)
|
|
|
|
TrackExtendsBox = Struct(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"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,
|
|
"flags" / Const(0, Int24ub),
|
|
"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(
|
|
"version" / Const(0, 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),
|
|
"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(
|
|
"version" / Const(0, Int8ub),
|
|
"flags" / Const(0, Int24ub),
|
|
"balance" / Default(Int16sb, 0),
|
|
"reserved" / Const(0, Int16ub)
|
|
)
|
|
|
|
|
|
# DASH Boxes
|
|
|
|
ProtectionSystemHeaderBox = Struct(
|
|
"version" / Rebuild(Int8ub, lambda ctx: 1 if (hasattr(ctx, "key_IDs") and ctx.key_IDs) else 0),
|
|
"flags" / Const(0, Int24ub),
|
|
"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),
|
|
"_reserved" / Const(0, Int8ub),
|
|
"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
|
|
),
|
|
Const(0, Int8ub)
|
|
), 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(
|
|
"version" / Const(0, Int8ub),
|
|
"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(
|
|
"original_format" / Default(PaddedString(4, "ascii"), "avc1")
|
|
)
|
|
|
|
SchemeTypeBox = Struct(
|
|
"version" / Default(Int8ub, 0),
|
|
"flags" / Default(Int24ub, 0),
|
|
"scheme_type" / Default(PaddedString(4, "ascii"), "cenc"),
|
|
"scheme_version" / Default(Int32ub, 0x00010000),
|
|
"schema_uri" / Default(If(this.flags & 1 == 1, CString("ascii")), None)
|
|
)
|
|
|
|
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")
|
|
)
|
|
|
|
ContainerBoxLazy = LazyBound(lambda: ContainerBox)
|
|
|
|
|
|
Box = Prefixed(Int32ub, Struct(
|
|
"offset" / TellMinusSizeOf(Int32ub),
|
|
"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,
|
|
# dash
|
|
"tenc": TrackEncryptionBox,
|
|
"pssh": ProtectionSystemHeaderBox,
|
|
"senc": SampleEncryptionBox,
|
|
"sinf": ProtectionSchemeInformationBox,
|
|
"frma": OriginalFormatBox,
|
|
"schm": SchemeTypeBox,
|
|
"schi": ContainerBoxLazy,
|
|
# piff
|
|
"uuid": UUIDBox,
|
|
# HDS boxes
|
|
"abst": HDSSegmentBox,
|
|
"asrt": HDSSegmentRunBox,
|
|
"afrt": HDSFragmentRunBox,
|
|
# WebVTT
|
|
"vttC": WebVTTConfigurationBox,
|
|
"vlab": WebVTTSourceLabelBox,
|
|
"vttc": ContainerBoxLazy,
|
|
"vttx": ContainerBoxLazy,
|
|
"iden": CueIDBox,
|
|
"sttg": CueSettingsBox,
|
|
"payl": CuePayloadBox
|
|
}, default=RawBox),
|
|
"end" / TellPlusSizeOf(Int32ub)
|
|
), includelength=True)
|
|
|
|
ContainerBox = Struct(
|
|
"children" / GreedyRange(Box)
|
|
)
|
|
|
|
MP4 = GreedyRange(Box)
|