from datetime import datetime from tags import Tags def dt_to_unix_ts(ts: datetime) -> int: return int(ts.timestamp()) def dt_to_mp4_ts(ts: datetime) -> int: return int(ts.timestamp()) + 2082844800 def s2b(s: str): return bytearray(s.encode("ascii")) def i2b(i: int, size: int = 4): return bytearray(i.to_bytes(size, "big")) class MP4MuxerEAC3: def __init__(self) -> None: self.data: bytearray = bytearray() self.timestamp: datetime = datetime.now() self.sample_rate: int = 0 self.number_of_samples: int = 0 self.bit_depth: int = 0 self.bit_rate: int = 0 self.sample_delta: int = 1536 self.default_sample_size: int = 3072 self.channel_count: int = 2 self.sample_sizes: list[int] = [] # these are the only important ones self.offsets: dict = { "stco": 0, "mdat": 0, } self.mdat_data: bytearray = bytearray() self.tags: Tags = None def create(self) -> None: self.ftyp() self.moov() self.free() self.mdat() self.rewrite_stco_chunk() def out(self, filename: str) -> None: with open(filename, "wb") as f: f.write(self.data) def w(self, b: bytearray): if isinstance(b, bytes): b = bytearray(b) self.data.extend(b) def set_sample_rate(self, sr: int) -> None: self.sample_rate = sr def set_number_of_samples(self, nr: int) -> None: self.number_of_samples = nr def set_bit_depth(self, bd: int) -> None: self.bit_depth = bd def set_bit_rate(self, br) -> None: self.bit_rate = br def set_sample_sizes(self, ss: int) -> None: self.sample_sizes = ss def set_mdat_data(self, m: bytearray) -> None: self.mdat_data = m def set_tags(self, t: Tags) -> None: self.tags = t def set_timestamp(self, t: datetime) -> None: self.timestamp = t def ftyp(self) -> None: major_brand: str = "mp42" minor_version: int = 0 compatible_brands: list[str] = ["mp42", "dby1", "isom"] # size + box string + major brand + minor version size: int = 16 for c in compatible_brands: size += 4 self.w(i2b(size)) self.w(s2b("ftyp")) self.w(s2b(major_brand)) self.w(i2b(minor_version)) for c in compatible_brands: self.w(s2b(c)) def moov_size(self) -> int: total_size: int = 8 total_size += self.mvhd_size() total_size += self.trak_size() total_size += self.iods_size() if self.tags: total_size += self.udta_size() return total_size def moov(self) -> None: self.w(i2b(self.moov_size())) self.w(s2b("moov")) self.mvhd() self.trak() self.iods() if self.tags: self.udta() def mvhd_size(self) -> int: return 108 def mvhd(self) -> None: version: int = 0 creation_time: datetime = self.timestamp modification_time: datetime = self.timestamp time_scale: int = self.sample_rate duration: int = int(self.sample_delta * len(self.sample_sizes)) # 1.0 rate: int = 0x10000 # 1.0 volume: int = 0x100 next_track_id: int = 2 self.w(i2b(self.mvhd_size())) self.w(s2b("mvhd")) self.w(i2b(version)) self.w(i2b(dt_to_mp4_ts(creation_time))) self.w(i2b(dt_to_mp4_ts(modification_time))) self.w(i2b(time_scale)) self.w(i2b(duration)) self.w(i2b(rate)) self.w(i2b(volume, 2)) # const bit(16) reserved = 0 self.w(i2b(0, 2)) # const unsigned int(32)[2] reserved = 0 self.w(i2b(0, 8)) # template int(32)[9] matrix # { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 } self.w(i2b(0x10000)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0x10000)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0x40000000)) # Unity matrix # bit(32)[6] pre_defined = 0 self.w(i2b(0, 24)) self.w(i2b(next_track_id)) def trak_size(self) -> int: total_size: int = 8 total_size += self.tkhd_size() total_size += self.mdia_size() return total_size def trak(self) -> None: self.w(i2b(self.trak_size())) self.w(s2b("trak")) self.tkhd() self.mdia() def tkhd_size(self) -> int: return 92 def tkhd(self) -> None: flags: int = 15 creation_time: datetime = self.timestamp modification_time: datetime = self.timestamp track_id: int = 1 duration: int = int(self.sample_delta * len(self.sample_sizes)) layer: int = 0 alternate_group: int = 2 # 1.0 volume: int = 0x100 width: int = 0 height: int = 0 self.w(i2b(self.tkhd_size())) self.w(s2b("tkhd")) self.w(i2b(flags)) self.w(i2b(dt_to_mp4_ts(creation_time))) self.w(i2b(dt_to_mp4_ts(modification_time))) self.w(i2b(track_id)) # const unsigned int (32) reserved = 0 self.w(i2b(0)) self.w(i2b(duration)) # reserved self.w(i2b(0)) # const unsigned int (32) [2] reserved = 0 self.w(i2b(0)) self.w(i2b(layer, 2)) self.w(i2b(alternate_group, 2)) # template int (16) volume = {if track_is_audio 0x0100 else 0} self.w(i2b(volume, 2)) # const unsigned int (16) reserved = 0 self.w(i2b(0, 2)) # template int (32) [9] matrix # { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 } self.w(i2b(0x10000)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0x10000)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0)) self.w(i2b(0x40000000)) # Unity matrix # unsigned int (32) width self.w(i2b(width)) # unsigned int (32) height self.w(i2b(height)) def mdia_size(self) -> int: total_size: int = 8 total_size += self.mdhd_size() total_size += self.hdlr_size() total_size += self.minf_size() return total_size def mdia(self) -> None: self.w(i2b(self.mdia_size())) self.w(s2b("mdia")) self.mdhd() self.hdlr() self.minf() def mdhd_size(self) -> int: return 32 def mdhd(self) -> None: version: int = 0 flags: int = 0 creation_time: datetime = self.timestamp modification_time: datetime = self.timestamp time_scale: int = self.sample_rate duration: int = int(self.sample_delta * len(self.sample_sizes)) language: int = 0x55C4 # undefined quality: int = 0 self.w(i2b(self.mdhd_size())) self.w(s2b("mdhd")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(dt_to_mp4_ts(creation_time))) self.w(i2b(dt_to_mp4_ts(modification_time))) self.w(i2b(time_scale)) self.w(i2b(duration)) self.w(i2b(language, 2)) self.w(i2b(quality, 2)) def hdlr_size(self) -> int: return 46 def hdlr(self) -> None: version = 0 flags = 0 component_type = "mhlr" component_subtype = "soun" component_manufacturer = 0 component_flags = 0 component_flags_mask = 0 component_name = "sound handler" self.w(i2b(self.hdlr_size())) self.w(s2b("hdlr")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(s2b(component_type)) self.w(s2b(component_subtype)) self.w(i2b(component_manufacturer)) self.w(i2b(component_flags)) self.w(i2b(component_flags_mask)) self.w(s2b(component_name)) self.w(bytearray(b"\x00")) # terminating null byte def minf_size(self) -> int: total_size: int = 8 total_size += self.smhd_size() total_size += self.dinf_size() total_size += self.stbl_size() return total_size def minf(self) -> None: self.w(i2b(self.minf_size())) self.w(s2b("minf")) self.smhd() self.dinf() self.stbl() def smhd_size(self) -> int: return 16 def smhd(self) -> None: version: int = 0 flags: int = 0 audio_balance: int = 0 self.w(i2b(self.smhd_size())) self.w(s2b("smhd")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(audio_balance, 2)) self.w(i2b(0, 2)) # reserved def dinf_size(self) -> int: total_size: int = 8 total_size += self.dref_size() return total_size def dinf(self) -> None: self.w(i2b(self.dinf_size())) self.w(s2b("dinf")) self.dref() pass def dref_size(self) -> int: return 28 def dref(self) -> None: version: int = 0 flags: int = 0 entry_count: int = 1 self.w(i2b(self.dref_size())) self.w(s2b("dref")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(entry_count)) data_location_size: int = 12 data_location_name: str = "url " data_location_version: int = 0 data_location_flags: int = 1 # same file self.w(i2b(data_location_size)) self.w(s2b(data_location_name)) self.w(i2b(data_location_version, 1)) self.w(i2b(data_location_flags, 3)) def stbl_size(self) -> int: total_size: int = 8 total_size += self.stsd_size() total_size += self.stts_size() total_size += self.stsz_size() total_size += self.stsc_size() total_size += self.stco_size() return total_size def stbl(self) -> None: self.data.extend(self.stbl_size().to_bytes(4, "big")) self.data.extend("stbl".encode("ascii")) self.stsd() self.stts() self.stsz() self.stsc() self.stco() def stsd_size(self) -> int: return 67 def stsd(self) -> None: version: int = 0 flags: int = 0 count: int = 1 audio_size = 51 audio_name = "ec-3" data_reference_index = 1 channel_count = 2 sample_size = self.bit_depth sample_rate = self.sample_rate self.w(i2b(self.stsd_size())) self.w(s2b("stsd")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(count)) self.w(i2b(audio_size)) self.w(s2b(audio_name)) self.w(i2b(0, 6)) # reserved self.w(i2b(data_reference_index, 2)) # data reference index self.w(i2b(0)) # reserved self.w(i2b(0)) # reserved self.w(i2b(channel_count, 2)) self.w(i2b(sample_size, 2)) self.w(i2b(0, 2)) # pre-defined self.w(i2b(0, 2)) # reserved self.w(i2b(sample_rate, 2)) self.w(i2b(0, 2)) # sample rate (again? set to zero for some reason) # EAC3 specific box eac3_size = 15 eac3_name = "dec3" self.data.extend(eac3_size.to_bytes(4, "big")) self.data.extend(eac3_name.encode("ascii")) data_rate = self.bit_rate # 13 bits num_ind_sub = 0 # 3 bits b = data_rate << 3 b += num_ind_sub self.data.extend(b.to_bytes(2, "big")) # independed substrem fscod = 0 # 2 bits bsid = 16 # 5 bits reserved_bit_1 = 0 # 1 bit asvc = 0 # 1 bit bsmod = 0 # 3 bits acmod = 7 # 3 bits lfeon = 1 # 1 bit reserved_bit_2 = 0 # 3 bits num_dep_sub = 0 # 4 bits reserved_bit_3 = 0 # 1 bit b = fscod << (8 + 8 + 6) b += bsid << (8 + 8 + 1) b += reserved_bit_1 << (8 + 7) b += asvc << (8 + 6) b += bsmod << (8 + 4) b += acmod << (8 + 1) b += lfeon << 8 b += reserved_bit_2 << 5 b += num_dep_sub << 1 b += reserved_bit_3 self.data.extend(b.to_bytes(3, "big")) # JOC extension # both values 1 byte each ec3_job_flag = 1 joc_complexity_index = 16 self.data.extend(ec3_job_flag.to_bytes(1, "big")) self.data.extend(joc_complexity_index.to_bytes(1, "big")) def stts_size(self) -> int: return 24 def stts(self) -> None: version: int = 0 flags: int = 0 number_of_entries: int = 1 sample_count: int = len(self.sample_sizes) self.w(i2b(self.stts_size())) self.w(s2b("stts")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(number_of_entries)) self.w(i2b(sample_count)) self.w(i2b(self.sample_delta)) def stsz_size(self) -> int: total_size: int = 20 total_size += 4 * len(self.sample_sizes) return total_size def stsz(self) -> None: version: int = 0 flags: int = 0 sample_size: int = self.default_sample_size sample_count: int = len(self.sample_sizes) self.w(i2b(self.stsz_size())) self.w(s2b("stsz")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(sample_size)) self.w(i2b(sample_count)) for s in self.sample_sizes: self.w(i2b(s)) def stsc_size(self) -> int: number_of_entries: int = 1 entries_per_second: int = int(round(self.sample_rate / self.sample_delta)) last_entry: int = len(self.sample_sizes) % entries_per_second if last_entry != 0: number_of_entries = 2 total_size: int = 16 + (number_of_entries * 12) return total_size # last chunk is forced to 1536, so we only need one entry def stsc(self) -> None: version: int = 0 flags: int = 0 number_of_entries: int = 1 entries_per_second: int = int(round(self.sample_rate / self.sample_delta)) last_entry: int = len(self.sample_sizes) % entries_per_second first_chunk_count = int( (len(self.sample_sizes) - last_entry) / entries_per_second ) entries = [] entries.append( { "first_chunk": 1, "samples_per_chunk": entries_per_second, "sample_description_index": 1, } ) if last_entry != 0: number_of_entries = 2 entries.append( { "first_chunk": first_chunk_count + 1, "samples_per_chunk": last_entry, "sample_description_index": 1, } ) self.w(i2b(self.stsc_size())) self.w(s2b("stsc")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(number_of_entries)) for s in entries: first_chunk: int = s["first_chunk"] samples_per_chunk: int = s["samples_per_chunk"] sample_description_index: int = s["sample_description_index"] self.w(i2b(first_chunk)) self.w(i2b(samples_per_chunk)) self.w(i2b(sample_description_index)) def stco_size(self) -> int: total_size: int = 16 entries_per_second: int = int(round(self.sample_rate / self.sample_delta)) last_entry: int = len(self.sample_sizes) % entries_per_second first_chunk_count = int( (len(self.sample_sizes) - last_entry) / entries_per_second ) number_of_stco_entries = first_chunk_count if last_entry != 0: number_of_stco_entries += 1 total_size += 4 * number_of_stco_entries return total_size def stco(self) -> None: version: int = 0 flags: int = 0 self.offsets["stco"] = len(self.data) entries_per_second: int = int(round(self.sample_rate / self.sample_delta)) last_entry: int = len(self.sample_sizes) % entries_per_second first_chunk_count = int( (len(self.sample_sizes) - last_entry) / entries_per_second ) number_of_stco_entries = first_chunk_count if last_entry != 0: number_of_stco_entries += 1 self.w(i2b(self.stco_size())) self.w(s2b("stco")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(number_of_stco_entries)) for s in range(number_of_stco_entries): self.w(i2b(0xFFFFFFFF)) # placeholder value def free(self) -> None: self.w(i2b(8)) self.w(s2b("free")) def mdat(self) -> None: self.offsets["mdat"] = len(self.data) self.w(i2b(8 + len(self.mdat_data))) self.w(s2b("mdat")) self.w(self.mdat_data) def rewrite_stco_chunk(self) -> None: stco_pos: int = self.offsets["stco"] + 16 first_chunk_offset: int = self.offsets["mdat"] + 8 entries_per_second: int = int(round(self.sample_rate / self.sample_delta)) last_entry: int = len(self.sample_sizes) % entries_per_second first_chunk_count = int( (len(self.sample_sizes) - last_entry) / entries_per_second ) number_of_stco_entries = first_chunk_count if last_entry != 0: number_of_stco_entries += 1 for s in range(number_of_stco_entries): bytes_to_write = i2b( first_chunk_offset + (sum(self.sample_sizes[: entries_per_second * s])) ) for index, b in enumerate(bytes_to_write): self.data[stco_pos + (s * 4) + index] = b def udta_size(self) -> int: return 8 + self.meta_size() def udta(self) -> None: self.w(i2b(self.udta_size())) self.w(s2b("udta")) self.meta() def meta_size(self) -> int: total_size: int = 12 total_size += self.meta_hdlr_size() total_size += self.ilst_size() return total_size def meta(self) -> None: version: int = 0 flags: int = 0 self.w(i2b(self.meta_size())) self.w(s2b("meta")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.meta_hdlr() self.ilst() def meta_hdlr_size(self) -> int: return 33 def meta_hdlr(self) -> None: version: int = 0 flags: int = 0 type_quicktime: int = 0 metadata_type: str = "mdir" manufacturer: str = "appl" component_reserved_flags: int = 0 component_reserved_flags_mask: int = 0 component_type_name: int = 0 self.w(i2b(self.meta_hdlr_size())) self.w(s2b("hdlr")) self.w(i2b(version, 1)) self.w(i2b(flags, 3)) self.w(i2b(type_quicktime)) self.w(s2b(metadata_type)) self.w(s2b(manufacturer)) self.w(i2b(component_reserved_flags)) self.w(i2b(component_reserved_flags_mask)) self.w(i2b(component_type_name, 1)) def ilst_size(self) -> int: total_size: int = 8 if self.tags.track_name: total_size += 24 + len(self.tags.track_name.encode("utf-8")) if self.tags.artist: total_size += 24 + len(self.tags.artist.encode("utf-8")) if self.tags.album_artist: total_size += 24 + len(self.tags.album_artist.encode("utf-8")) if self.tags.composer: total_size += 24 + len(self.tags.composer.encode("utf-8")) if self.tags.album_name: total_size += 24 + len(self.tags.album_name.encode("utf-8")) if self.tags.genre: total_size += 24 + len(self.tags.genre.encode("utf-8")) if self.tags.date: total_size += 24 + len(self.tags.date.encode("utf-8")) if self.tags.isrc: total_size += 24 + len(self.tags.isrc.encode("utf-8")) if self.tags.copyright: total_size += 24 + len(self.tags.copyright.encode("utf-8")) if self.tags.track_number or self.tags.total_number_of_tracks: total_size += 32 if self.tags.disc_number or self.tags.total_number_of_discs: total_size += 32 if self.tags.upc: total_size += len(self.tags.upc.encode("utf-8")) + 64 + len("UPC") if self.tags.label: total_size += len(self.tags.label.encode("utf-8")) + 64 + len("LABEL") if self.tags.apple_store_catalog_id: total_size += 28 if self.tags.playlist_id: total_size += 28 if self.tags.album_title_id: total_size += 28 if self.tags.cover_data: total_size += 24 + len(self.tags.cover_data) return total_size def write_mp4_tag_utf8(self, box: bytes, content: str): b: bytearray = bytearray(content.encode("utf-8")) data_size: int = len(b) + 16 self.w(i2b(data_size + 8)) self.w(box) self.w(i2b(data_size)) self.w(s2b("data")) # 0 = binary; 1 = utf-8 kind: int = 1 language: int = 0 self.w(i2b(kind)) self.w(i2b(language)) self.w(b) def write_mp4_tag_int(self, box: bytes, content: int): kind: int = 21 # signed integer language: int = 0 size: int = 24 + len(box) self.w(i2b(size)) self.w(box) size -= 8 self.w(i2b(size)) self.w(s2b("data")) self.w(i2b(kind)) self.w(i2b(language)) self.w(i2b(content)) def write_itunes_tag_utf8(self, box: str, content: str): b: bytearray = bytearray(content.encode("utf-8")) full_size: int = len(b) + 64 + len(box) self.w(i2b(full_size)) self.w(s2b("----")) mean_size: int = 28 self.w(i2b(mean_size)) self.w(s2b("mean")) self.w(i2b(0)) # unknown self.w(s2b("com.apple.iTunes")) name_size: int = 12 + len(box) self.w(i2b(name_size)) self.w(s2b("name")) self.w(i2b(0)) # unknown self.w(s2b(box)) data_size = len(b) + 16 self.w(i2b(data_size)) self.w(s2b("data")) # 0 = binary; 1 = utf-8 kind: int = 1 language: int = 0 self.w(i2b(kind)) self.w(i2b(language)) self.w(b) def write_mp4_tag_tuple_int(self, box: bytes, curr: int, total: int): self.w(i2b(32)) # size self.w(box) self.w(i2b(24)) # data size self.w(s2b("data")) kind: int = 0 # binary language: int = 0 self.w(i2b(kind)) self.w(i2b(language)) self.w(i2b(0, 2)) # reserved self.w(i2b(curr, 2)) self.w(i2b(total, 2)) self.w(i2b(0, 2)) # reserved def ilst(self) -> None: self.w(i2b(self.ilst_size())) self.w(s2b("ilst")) if self.tags.track_name: self.write_mp4_tag_utf8(b"\xA9\x6E\x61\x6D", self.tags.track_name) # ©nam if self.tags.artist: self.write_mp4_tag_utf8(b"\xA9\x41\x52\x54", self.tags.artist) # ©ART if self.tags.album_artist: self.write_mp4_tag_utf8(b"\x61\x41\x52\x54", self.tags.album_artist) # aART if self.tags.composer: self.write_mp4_tag_utf8(b"\xA9\x77\x72\x74", self.tags.composer) # ©wrt if self.tags.album_name: self.write_mp4_tag_utf8(b"\xA9\x61\x6C\x62", self.tags.album_name) # ©alb if self.tags.genre: self.write_mp4_tag_utf8(b"\xA9\x67\x65\x6E", self.tags.genre) # ©gen if self.tags.date: self.write_mp4_tag_utf8(b"\xA9\x64\x61\x79", self.tags.date) # ©day if self.tags.isrc: self.write_mp4_tag_utf8(b"\x49\x53\x52\x43", self.tags.isrc) # ISRC if self.tags.copyright: self.write_mp4_tag_utf8(b"\x63\x70\x72\x74", self.tags.copyright) # cprt if self.tags.apple_store_catalog_id: self.write_mp4_tag_int( b"\x63\x6E\x49\x44", self.tags.apple_store_catalog_id ) # cnID if self.tags.playlist_id: self.write_mp4_tag_int(b"\x70\x6C\x49\x44", self.tags.playlist_id) # plID if self.tags.album_title_id: self.write_mp4_tag_int( b"\x61\x74\x49\x44", self.tags.album_title_id ) # atID if self.tags.upc: self.write_itunes_tag_utf8("UPC", self.tags.upc) if self.tags.label: self.write_itunes_tag_utf8("LABEL", self.tags.label) if self.tags.track_number or self.tags.total_number_of_tracks: curr: int = 0 if self.tags.track_number: curr = self.tags.track_number total: int = 0 if self.tags.total_number_of_tracks: total = self.tags.total_number_of_tracks self.write_mp4_tag_tuple_int(b"\x74\x72\x6B\x6E", curr, total) if self.tags.disc_number or self.tags.total_number_of_discs: curr: int = 0 if self.tags.disc_number: curr = self.tags.disc_number total: int = 0 if self.tags.total_number_of_discs: total = self.tags.total_number_of_discs self.write_mp4_tag_tuple_int(b"\x64\x69\x73\x6B", curr, total) if self.tags.cover_data: if self.tags.cover_format == "jpeg": kind: int = 13 elif self.tags.cover_format == "png": kind: int = 14 language: int = 0 b: bytearray = self.tags.cover_data full_size: int = len(b) + 24 self.w(i2b(full_size)) self.w(s2b("covr")) data_size: int = full_size - 8 self.w(i2b(data_size)) self.w(s2b("data")) self.w(i2b(kind)) self.w(i2b(language)) self.w(b) def iods_size(self) -> int: return 27 def iods(self) -> None: self.w(i2b(self.iods_size())) self.w(s2b("iods")) version: int = 0 flags: int = 0 self.w(i2b(version, 1)) self.w(i2b(flags, 3)) # MP4_IOD_Tag # header iod_tag_type: int = 16 iod_tag_size: int = 13 object_descriptor_id: int = 1 # 10 bits url_flag: int = 0 # 1 bit include_inline_profile_level_flag: int = 0 # 1 bit reserved_bits: int = 15 # 4 bits od_profile_level_indication: int = 255 scene_profile_level_indication: int = 255 audio_profile_level_indication: int = 255 visual_profile_level_indication: int = 255 graphics_profile_level_indication: int = 255 self.w(i2b(iod_tag_type, 1)) self.w(i2b(iod_tag_size, 1)) b: int = object_descriptor_id << 6 b += url_flag << 5 b += include_inline_profile_level_flag << 4 b += reserved_bits self.w(i2b(b, 2)) self.w(i2b(od_profile_level_indication, 1)) self.w(i2b(scene_profile_level_indication, 1)) self.w(i2b(audio_profile_level_indication, 1)) self.w(i2b(visual_profile_level_indication, 1)) self.w(i2b(graphics_profile_level_indication, 1)) # ES_ID_IncTag es_id_type: int = 14 es_id_size: int = 4 es_id_track_id: int = 1 self.w(i2b(es_id_type, 1)) self.w(i2b(es_id_size, 1)) self.w(i2b(es_id_track_id, 4))