logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

ism.py (11630B)


  1. import binascii
  2. import io
  3. import struct
  4. import time
  5. from .fragment import FragmentFD
  6. from ..networking.exceptions import HTTPError
  7. from ..utils import RetryManager
  8. u8 = struct.Struct('>B')
  9. u88 = struct.Struct('>Bx')
  10. u16 = struct.Struct('>H')
  11. u1616 = struct.Struct('>Hxx')
  12. u32 = struct.Struct('>I')
  13. u64 = struct.Struct('>Q')
  14. s88 = struct.Struct('>bx')
  15. s16 = struct.Struct('>h')
  16. s1616 = struct.Struct('>hxx')
  17. s32 = struct.Struct('>i')
  18. unity_matrix = (s32.pack(0x10000) + s32.pack(0) * 3) * 2 + s32.pack(0x40000000)
  19. TRACK_ENABLED = 0x1
  20. TRACK_IN_MOVIE = 0x2
  21. TRACK_IN_PREVIEW = 0x4
  22. SELF_CONTAINED = 0x1
  23. def box(box_type, payload):
  24. return u32.pack(8 + len(payload)) + box_type + payload
  25. def full_box(box_type, version, flags, payload):
  26. return box(box_type, u8.pack(version) + u32.pack(flags)[1:] + payload)
  27. def write_piff_header(stream, params):
  28. track_id = params['track_id']
  29. fourcc = params['fourcc']
  30. duration = params['duration']
  31. timescale = params.get('timescale', 10000000)
  32. language = params.get('language', 'und')
  33. height = params.get('height', 0)
  34. width = params.get('width', 0)
  35. stream_type = params['stream_type']
  36. creation_time = modification_time = int(time.time())
  37. ftyp_payload = b'isml' # major brand
  38. ftyp_payload += u32.pack(1) # minor version
  39. ftyp_payload += b'piff' + b'iso2' # compatible brands
  40. stream.write(box(b'ftyp', ftyp_payload)) # File Type Box
  41. mvhd_payload = u64.pack(creation_time)
  42. mvhd_payload += u64.pack(modification_time)
  43. mvhd_payload += u32.pack(timescale)
  44. mvhd_payload += u64.pack(duration)
  45. mvhd_payload += s1616.pack(1) # rate
  46. mvhd_payload += s88.pack(1) # volume
  47. mvhd_payload += u16.pack(0) # reserved
  48. mvhd_payload += u32.pack(0) * 2 # reserved
  49. mvhd_payload += unity_matrix
  50. mvhd_payload += u32.pack(0) * 6 # pre defined
  51. mvhd_payload += u32.pack(0xffffffff) # next track id
  52. moov_payload = full_box(b'mvhd', 1, 0, mvhd_payload) # Movie Header Box
  53. tkhd_payload = u64.pack(creation_time)
  54. tkhd_payload += u64.pack(modification_time)
  55. tkhd_payload += u32.pack(track_id) # track id
  56. tkhd_payload += u32.pack(0) # reserved
  57. tkhd_payload += u64.pack(duration)
  58. tkhd_payload += u32.pack(0) * 2 # reserved
  59. tkhd_payload += s16.pack(0) # layer
  60. tkhd_payload += s16.pack(0) # alternate group
  61. tkhd_payload += s88.pack(1 if stream_type == 'audio' else 0) # volume
  62. tkhd_payload += u16.pack(0) # reserved
  63. tkhd_payload += unity_matrix
  64. tkhd_payload += u1616.pack(width)
  65. tkhd_payload += u1616.pack(height)
  66. trak_payload = full_box(b'tkhd', 1, TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW, tkhd_payload) # Track Header Box
  67. mdhd_payload = u64.pack(creation_time)
  68. mdhd_payload += u64.pack(modification_time)
  69. mdhd_payload += u32.pack(timescale)
  70. mdhd_payload += u64.pack(duration)
  71. mdhd_payload += u16.pack(((ord(language[0]) - 0x60) << 10) | ((ord(language[1]) - 0x60) << 5) | (ord(language[2]) - 0x60))
  72. mdhd_payload += u16.pack(0) # pre defined
  73. mdia_payload = full_box(b'mdhd', 1, 0, mdhd_payload) # Media Header Box
  74. hdlr_payload = u32.pack(0) # pre defined
  75. if stream_type == 'audio': # handler type
  76. hdlr_payload += b'soun'
  77. hdlr_payload += u32.pack(0) * 3 # reserved
  78. hdlr_payload += b'SoundHandler\0' # name
  79. elif stream_type == 'video':
  80. hdlr_payload += b'vide'
  81. hdlr_payload += u32.pack(0) * 3 # reserved
  82. hdlr_payload += b'VideoHandler\0' # name
  83. elif stream_type == 'text':
  84. hdlr_payload += b'subt'
  85. hdlr_payload += u32.pack(0) * 3 # reserved
  86. hdlr_payload += b'SubtitleHandler\0' # name
  87. else:
  88. assert False
  89. mdia_payload += full_box(b'hdlr', 0, 0, hdlr_payload) # Handler Reference Box
  90. if stream_type == 'audio':
  91. smhd_payload = s88.pack(0) # balance
  92. smhd_payload += u16.pack(0) # reserved
  93. media_header_box = full_box(b'smhd', 0, 0, smhd_payload) # Sound Media Header
  94. elif stream_type == 'video':
  95. vmhd_payload = u16.pack(0) # graphics mode
  96. vmhd_payload += u16.pack(0) * 3 # opcolor
  97. media_header_box = full_box(b'vmhd', 0, 1, vmhd_payload) # Video Media Header
  98. elif stream_type == 'text':
  99. media_header_box = full_box(b'sthd', 0, 0, b'') # Subtitle Media Header
  100. else:
  101. assert False
  102. minf_payload = media_header_box
  103. dref_payload = u32.pack(1) # entry count
  104. dref_payload += full_box(b'url ', 0, SELF_CONTAINED, b'') # Data Entry URL Box
  105. dinf_payload = full_box(b'dref', 0, 0, dref_payload) # Data Reference Box
  106. minf_payload += box(b'dinf', dinf_payload) # Data Information Box
  107. stsd_payload = u32.pack(1) # entry count
  108. sample_entry_payload = u8.pack(0) * 6 # reserved
  109. sample_entry_payload += u16.pack(1) # data reference index
  110. if stream_type == 'audio':
  111. sample_entry_payload += u32.pack(0) * 2 # reserved
  112. sample_entry_payload += u16.pack(params.get('channels', 2))
  113. sample_entry_payload += u16.pack(params.get('bits_per_sample', 16))
  114. sample_entry_payload += u16.pack(0) # pre defined
  115. sample_entry_payload += u16.pack(0) # reserved
  116. sample_entry_payload += u1616.pack(params['sampling_rate'])
  117. if fourcc == 'AACL':
  118. sample_entry_box = box(b'mp4a', sample_entry_payload)
  119. if fourcc == 'EC-3':
  120. sample_entry_box = box(b'ec-3', sample_entry_payload)
  121. elif stream_type == 'video':
  122. sample_entry_payload += u16.pack(0) # pre defined
  123. sample_entry_payload += u16.pack(0) # reserved
  124. sample_entry_payload += u32.pack(0) * 3 # pre defined
  125. sample_entry_payload += u16.pack(width)
  126. sample_entry_payload += u16.pack(height)
  127. sample_entry_payload += u1616.pack(0x48) # horiz resolution 72 dpi
  128. sample_entry_payload += u1616.pack(0x48) # vert resolution 72 dpi
  129. sample_entry_payload += u32.pack(0) # reserved
  130. sample_entry_payload += u16.pack(1) # frame count
  131. sample_entry_payload += u8.pack(0) * 32 # compressor name
  132. sample_entry_payload += u16.pack(0x18) # depth
  133. sample_entry_payload += s16.pack(-1) # pre defined
  134. codec_private_data = binascii.unhexlify(params['codec_private_data'].encode())
  135. if fourcc in ('H264', 'AVC1'):
  136. sps, pps = codec_private_data.split(u32.pack(1))[1:]
  137. avcc_payload = u8.pack(1) # configuration version
  138. avcc_payload += sps[1:4] # avc profile indication + profile compatibility + avc level indication
  139. avcc_payload += u8.pack(0xfc | (params.get('nal_unit_length_field', 4) - 1)) # complete representation (1) + reserved (11111) + length size minus one
  140. avcc_payload += u8.pack(1) # reserved (0) + number of sps (0000001)
  141. avcc_payload += u16.pack(len(sps))
  142. avcc_payload += sps
  143. avcc_payload += u8.pack(1) # number of pps
  144. avcc_payload += u16.pack(len(pps))
  145. avcc_payload += pps
  146. sample_entry_payload += box(b'avcC', avcc_payload) # AVC Decoder Configuration Record
  147. sample_entry_box = box(b'avc1', sample_entry_payload) # AVC Simple Entry
  148. else:
  149. assert False
  150. elif stream_type == 'text':
  151. if fourcc == 'TTML':
  152. sample_entry_payload += b'http://www.w3.org/ns/ttml\0' # namespace
  153. sample_entry_payload += b'\0' # schema location
  154. sample_entry_payload += b'\0' # auxilary mime types(??)
  155. sample_entry_box = box(b'stpp', sample_entry_payload)
  156. else:
  157. assert False
  158. else:
  159. assert False
  160. stsd_payload += sample_entry_box
  161. stbl_payload = full_box(b'stsd', 0, 0, stsd_payload) # Sample Description Box
  162. stts_payload = u32.pack(0) # entry count
  163. stbl_payload += full_box(b'stts', 0, 0, stts_payload) # Decoding Time to Sample Box
  164. stsc_payload = u32.pack(0) # entry count
  165. stbl_payload += full_box(b'stsc', 0, 0, stsc_payload) # Sample To Chunk Box
  166. stco_payload = u32.pack(0) # entry count
  167. stbl_payload += full_box(b'stco', 0, 0, stco_payload) # Chunk Offset Box
  168. minf_payload += box(b'stbl', stbl_payload) # Sample Table Box
  169. mdia_payload += box(b'minf', minf_payload) # Media Information Box
  170. trak_payload += box(b'mdia', mdia_payload) # Media Box
  171. moov_payload += box(b'trak', trak_payload) # Track Box
  172. mehd_payload = u64.pack(duration)
  173. mvex_payload = full_box(b'mehd', 1, 0, mehd_payload) # Movie Extends Header Box
  174. trex_payload = u32.pack(track_id) # track id
  175. trex_payload += u32.pack(1) # default sample description index
  176. trex_payload += u32.pack(0) # default sample duration
  177. trex_payload += u32.pack(0) # default sample size
  178. trex_payload += u32.pack(0) # default sample flags
  179. mvex_payload += full_box(b'trex', 0, 0, trex_payload) # Track Extends Box
  180. moov_payload += box(b'mvex', mvex_payload) # Movie Extends Box
  181. stream.write(box(b'moov', moov_payload)) # Movie Box
  182. def extract_box_data(data, box_sequence):
  183. data_reader = io.BytesIO(data)
  184. while True:
  185. box_size = u32.unpack(data_reader.read(4))[0]
  186. box_type = data_reader.read(4)
  187. if box_type == box_sequence[0]:
  188. box_data = data_reader.read(box_size - 8)
  189. if len(box_sequence) == 1:
  190. return box_data
  191. return extract_box_data(box_data, box_sequence[1:])
  192. data_reader.seek(box_size - 8, 1)
  193. class IsmFD(FragmentFD):
  194. """
  195. Download segments in a ISM manifest
  196. """
  197. def real_download(self, filename, info_dict):
  198. segments = info_dict['fragments'][:1] if self.params.get(
  199. 'test', False) else info_dict['fragments']
  200. ctx = {
  201. 'filename': filename,
  202. 'total_frags': len(segments),
  203. }
  204. self._prepare_and_start_frag_download(ctx, info_dict)
  205. extra_state = ctx.setdefault('extra_state', {
  206. 'ism_track_written': False,
  207. })
  208. skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True)
  209. frag_index = 0
  210. for segment in segments:
  211. frag_index += 1
  212. if frag_index <= ctx['fragment_index']:
  213. continue
  214. retry_manager = RetryManager(self.params.get('fragment_retries'), self.report_retry,
  215. frag_index=frag_index, fatal=not skip_unavailable_fragments)
  216. for retry in retry_manager:
  217. try:
  218. success = self._download_fragment(ctx, segment['url'], info_dict)
  219. if not success:
  220. return False
  221. frag_content = self._read_fragment(ctx)
  222. if not extra_state['ism_track_written']:
  223. tfhd_data = extract_box_data(frag_content, [b'moof', b'traf', b'tfhd'])
  224. info_dict['_download_params']['track_id'] = u32.unpack(tfhd_data[4:8])[0]
  225. write_piff_header(ctx['dest_stream'], info_dict['_download_params'])
  226. extra_state['ism_track_written'] = True
  227. self._append_fragment(ctx, frag_content)
  228. except HTTPError as err:
  229. retry.error = err
  230. continue
  231. if retry_manager.error:
  232. if not skip_unavailable_fragments:
  233. return False
  234. self.report_skip_fragment(frag_index)
  235. return self._finish_frag_download(ctx, info_dict)