A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2055 lines
54 KiB

  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2006-2007 John-Mark Gurney.
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. # 1. Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. # SUCH DAMAGE.
  26. #
  27. # $Id$
  28. #
  29. import atschuff
  30. import itertools
  31. import os
  32. import struct
  33. import time
  34. import traceback
  35. from functools import reduce
  36. TSSYNC = '\x47'
  37. TSPKTLEN = 188
  38. READBLK = 1024
  39. def attribreprlist(obj, attrs):
  40. return list(map(lambda x, y = obj: '%s: %s' % (x, repr(getattr(y, x))),
  41. list(filter(lambda x, y = obj: hasattr(y, x), attrs))))
  42. class UnReadSTR:
  43. def __init__(self, s):
  44. self.s = s
  45. self.pos = 0
  46. self._buf = []
  47. self._buftot = 0
  48. def __bool__(self):
  49. return self._buftot or self.pos < len(self.s)
  50. def tell(self):
  51. return self.pos - self._buftot
  52. def unread(self, buf):
  53. self._buf.append(buf)
  54. self._buftot += len(buf)
  55. def peek(self, size):
  56. r = self.read(size)
  57. self.unread(r)
  58. return r
  59. def sread(self, cnt = -1):
  60. oldpos = self.pos
  61. if cnt == -1:
  62. self.pos = len(self.s)
  63. return self.s[oldpos:]
  64. self.pos += cnt
  65. if self.pos > len(self.s):
  66. self.pos = len(self.s)
  67. return self.s[oldpos:self.pos]
  68. def read(self, size = None):
  69. if size is None and self._buf:
  70. ret = self._buf.pop()
  71. self._buftot -= len(ret)
  72. elif size is None:
  73. ret = self.sread()
  74. else:
  75. ret = []
  76. while size and self._buftot:
  77. ret.append(self._buf[-1][:size])
  78. l = len(ret[-1])
  79. if size > l:
  80. assert len(self._buf[-1]) == l
  81. self._buf.pop()
  82. else:
  83. self._buf[-1] = self._buf[-1][size:]
  84. self._buftot -= l
  85. size -= l
  86. if size:
  87. ret.append(self.sread(size))
  88. ret = ''.join(ret)
  89. return ret
  90. def DVDAudioFilter(itr, subchan):
  91. '''subchan should be in the range [0x80, 0x8f], this will filter out all other subchannels in that range that do not match subchan.'''
  92. assert subchan >= 0x80 and subchan <= 0x8f
  93. def checksubchan(pes, sc = subchan):
  94. if pes.stream_id != 0xbd:
  95. return False
  96. subchan = ord(pes.data[0])
  97. if subchan == sc or subchan < 0x80 or subchan > 0x8f:
  98. return True
  99. return False
  100. # XXX - I probably should mess w/ muxr so SCR is stable.
  101. for i in itr:
  102. j = Pack(UnReadSTR(i))
  103. if list(filter(checksubchan, j)):
  104. yield i
  105. def findcodes(buf):
  106. ret = []
  107. i = 0
  108. l = len(buf)
  109. while i < l:
  110. j = buf.find('\x00\x00\x01', i)
  111. if j == -1 or (i + 4) >= l:
  112. break
  113. ret.append((j, buf[j + 3]))
  114. i = j + 4
  115. return ret
  116. if False:
  117. class UnRead(file):
  118. def __init__(self, *args, **kwargs):
  119. super(UnRead, self).__init__(*args, **kwargs)
  120. self._buf = []
  121. self._buftot = 0
  122. def unread(self, buf):
  123. self._buf.append(buf)
  124. self._buftot += len(buf)
  125. def peek(self, size):
  126. r = self.read(size)
  127. self.unread(r)
  128. return r
  129. def read(self, size = None):
  130. if size is None and self._buf:
  131. ret = self._buf.pop()
  132. self._buftot -= len(ret)
  133. elif size is None:
  134. ret = super(UnRead, self).read()
  135. else:
  136. ret = []
  137. while size and self._buftot:
  138. ret.append(self._buf[-1][:size])
  139. l = len(ret[-1])
  140. if size > l:
  141. assert len(self._buf[-1]) == l
  142. self._buf.pop()
  143. else:
  144. self._buf[-1] = self._buf[-1][size:]
  145. self._buftot -= l
  146. size -= l
  147. if size:
  148. ret.append(super(UnRead, self).read(size))
  149. ret = ''.join(ret)
  150. return ret
  151. def read_timestamp(buf):
  152. assert len(buf) == 5
  153. assert (ord(buf[0]) & 0x1) == 1
  154. assert (ord(buf[2]) & 0x1) == 1
  155. assert (ord(buf[4]) & 0x1) == 1
  156. return (int(ord(buf[0]) & 0xe) << 29) | (ord(buf[1]) << 21) | \
  157. ((ord(buf[2]) & 0xfe) << 14) | (ord(buf[3]) << 7) | \
  158. ((ord(buf[4]) & 0xfe) >> 1)
  159. def read_escr(buf):
  160. assert len(buf) == 6
  161. assert (ord(buf[0]) & 0x4) == 0x4 and (ord(buf[2]) & 0x4) == 0x4
  162. assert (ord(buf[4]) & 0x4) == 0x4 and (ord(buf[5]) & 0x1) == 0x1
  163. base = (int(ord(buf[0]) & 0x38) << 27) | ((ord(buf[0]) & 0x3) << 28) |\
  164. (ord(buf[1]) << 20) | ((ord(buf[2]) & 0xf8) << 15) | \
  165. ((ord(buf[2]) & 0x3) << 13) | (ord(buf[3]) << 5) | \
  166. ((ord(buf[4]) & 0xf8) >> 3)
  167. extension = ((ord(buf[4]) & 0x3) << 7) | (ord(buf[5]) >> 1)
  168. return (base, extension)
  169. class MPEGWriter:
  170. END_CODE = '\xb9'
  171. def __init__(self, f):
  172. self.f = f
  173. self.SCR = (0, 0)
  174. def write_header(self, header):
  175. self.f.write('\x00\x00\x01' + header)
  176. def close(self):
  177. self.write_header(self.END_CODE)
  178. def __del__(self):
  179. self.close()
  180. class PES:
  181. PROGRAM_STREAM_MAP_ID = 0xbc
  182. PRIVATE_1_ID = 0xbd
  183. PADDING_ID = 0xbe
  184. PRIVATE_2_ID = 0xbf
  185. IS_AUDIO_ID = lambda x: (x & 0xe0) == 0xc0
  186. IS_VIDEO_ID = lambda x: (x & 0xf0) == 0xe0
  187. ECM_ID = 0xf0
  188. EMM_ID = 0xf1
  189. DSMCC_ID = 0xf2
  190. H2221_TYPE_E_ID = 0xf8
  191. PROGRAM_STREAM_DIRECTORY_ID = 0xff
  192. def __init__(self, buf):
  193. # Pull out an IndexError first
  194. assert buf[0] == '\x00' and buf[:3] == '\x00\x00\x01'
  195. self.stream_id = ord(buf[3])
  196. self.length = (ord(buf[4]) << 8) | ord(buf[5])
  197. if self.length == 0:
  198. self.length = len(buf)
  199. else:
  200. self.length += 6
  201. if len(buf) < self.length:
  202. raise IndexError('not enough data')
  203. if self.stream_id == self.PADDING_ID:
  204. # Validate padding?
  205. #self.length -= 6
  206. pass
  207. elif self.stream_id in (self.PROGRAM_STREAM_MAP_ID,
  208. self.PRIVATE_2_ID, self.ECM_ID, self.EMM_ID, self.DSMCC_ID,
  209. self.H2221_TYPE_E_ID, self.PROGRAM_STREAM_DIRECTORY_ID, ):
  210. self.data = buf[6:self.length]
  211. else:
  212. i = 6
  213. assert (ord(buf[i]) & 0xc0) == 0x80
  214. self.scrambling_control = (ord(buf[i]) & 0x30) >> 4
  215. self.priority = bool(ord(buf[i]) & 0x8)
  216. self.data_alignment = bool(ord(buf[i]) & 0x4)
  217. self.copyright = bool(ord(buf[i]) & 0x2)
  218. self.originalcopy = bool(ord(buf[i]) & 0x1)
  219. i +=1
  220. ptsdts_flag = (ord(buf[i]) & 0xc0) >> 6
  221. escr_flag = bool(ord(buf[i]) & 0x20)
  222. es_rate_flag = bool(ord(buf[i]) & 0x10)
  223. dsm_trick_mode_flag = bool(ord(buf[i]) & 0x8)
  224. additional_copy_info_flag = bool(ord(buf[i]) & 0x4)
  225. crc_flag = bool(ord(buf[i]) & 0x2)
  226. extension_flag = bool(ord(buf[i]) & 0x1)
  227. header_end = i + 2 + ord(buf[i + 1])
  228. i += 2
  229. if ptsdts_flag == 0x2:
  230. assert (ord(buf[i]) & 0xf0) == 0x20
  231. self.PTS = read_timestamp(buf[i:i + 5])
  232. i += 5
  233. elif ptsdts_flag == 0x3:
  234. assert (ord(buf[i]) & 0xf0) == 0x30
  235. self.PTS = read_timestamp(buf[i:i + 5])
  236. i += 5
  237. assert (ord(buf[i]) & 0xf0) == 0x10
  238. self.DTS = read_timestamp(buf[i:i + 5])
  239. i += 5
  240. elif ptsdts_flag == 0x1:
  241. raise ValueError("ptsdts flag forbidden: %d" % ptsdts_flag)
  242. if escr_flag:
  243. self.ESCR = read_escr(buf[i:i + 6])
  244. i += 6
  245. if es_rate_flag:
  246. assert (ord(buf[i]) & 0x80) == 0x80
  247. assert (ord(buf[i + 2]) & 0x01) == 0x01
  248. self.ES_rate = ((ord(buf[i]) & 0x7f) << 15) | \
  249. (ord(buf[i + 1]) << 7) | \
  250. (ord(buf[i + 2]) >> 1)
  251. i += 3
  252. if dsm_trick_mode_flag:
  253. self.trick_mode_control = ord(buf[i]) >> 5
  254. self.trick_mode_bits = ord(buf[i]) & 0x1f
  255. i += 1
  256. if additional_copy_info_flag:
  257. assert (ord(buf[i]) & 0x80) == 0x80
  258. self.additional_copy_info = ord(buf[i]) & 0x7f
  259. i += 1
  260. if crc_flag:
  261. self.prev_crc = (ord(buf[i]) << 8) | \
  262. ord(buf[i + 1])
  263. i += 2
  264. if extension_flag:
  265. private_data_flag = bool(ord(buf[i]) & 0x80)
  266. pack_header_field_flag = bool(ord(buf[i]) & \
  267. 0x40)
  268. program_packet_sequence_counter_flag = \
  269. bool(ord(buf[i]) & 0x20)
  270. pstd_buffer_flag = bool(ord(buf[i]) & 0x10)
  271. pes_extension_flag_2 = bool(ord(buf[i]) & 0x01)
  272. i += 1
  273. if private_data_flag:
  274. self.private_data = buf[i:i + 16]
  275. i += 16
  276. if pack_header_field_flag:
  277. pack_field_length = ord(buf[i])
  278. self.pack_header = buf[i + 1:i + 1 +
  279. pack_field_length]
  280. i += 1 + pack_field_length
  281. if program_packet_sequence_counter_flag:
  282. assert (ord(buf[i]) & 0x80) == 0x80
  283. self.sequence_counter = \
  284. ord(buf[i]) & 0x7f
  285. i += 1
  286. assert (ord(buf[i]) & 0x80) == 0x80
  287. self.mpeg1_mpeg2_ident = \
  288. bool(ord(buf[i]) & 0x4)
  289. self.original_stuff_len = \
  290. ord(buf[i]) & 0x3f
  291. i += 1
  292. if pstd_buffer_flag:
  293. assert (ord(buf[i]) & 0xc0) == 0x40
  294. self.pstd_buffer_scale = \
  295. bool(ord(buf[i]) & 0x20)
  296. self.pstd_buffer_size = \
  297. ((ord(buf[i]) & 0x1f) << 8) | \
  298. ord(buf[i + 1])
  299. i += 2
  300. if pes_extension_flag_2:
  301. assert (ord(buf[i]) & 0x80) == 0x80
  302. extension_field_length = \
  303. ord(buf[i]) & 0x7f
  304. self.extension_field = buf[i + 1:i + \
  305. 1 + extension_field_length]
  306. i += 1 + extension_field_length
  307. assert i <= header_end
  308. self.data = buf[header_end:self.length]
  309. def __repr__(self):
  310. # XXX - data length
  311. v = ( 'length', 'scrambling_control',
  312. 'priority', 'data_alignment', 'copyright',
  313. 'originalcopy', 'PTS', 'DTS', 'ESCR', 'ES_rate',
  314. 'trick_mode_control', 'trick_mode_bits',
  315. 'additional_copy_info', 'pack_header',
  316. 'sequence_counter', 'mpeg1_mpeg2_ident',
  317. 'original_stuff_len', 'pstd_buffer_scale',
  318. 'pstd_buffer_size', 'extension_field', )
  319. return '<PES: stream_id: %#x, %s>' % (self.stream_id,
  320. ', '.join(attribreprlist(self, v)), )
  321. class Pack(list):
  322. def __init__(self, f = None, **keyw):
  323. super(Pack, self).__init__()
  324. if f is not None:
  325. d = f.read(14)
  326. assert d[:4] == '\x00\x00\x01\xba'
  327. assert (ord(d[4]) & 0xc0) == 0x40
  328. self.SCR = read_escr(d[4:10])
  329. assert ord(d[12]) & 0x3 == 0x3
  330. m = list(map(ord, d[10:13]))
  331. self.muxr = (m[0] << 14) | (m[1] << 6) | (m[2] >> 2)
  332. self.stuff_len = ord(d[13]) & 0x7
  333. f.read(self.stuff_len)
  334. # system header
  335. d = f.peek(6)
  336. if d[:4] == '\x00\x00\x01\xbb':
  337. f.read(6)
  338. hlen = (ord(d[4]) << 8) | ord(d[5])
  339. header = f.read(hlen)
  340. oh = list(map(ord, header))
  341. assert (oh[0] & 0x80) == 0x80 and \
  342. (oh[2] & 0x1) == 0x1
  343. self.rate_bound = ((oh[0] & 0x7f) << 15) | \
  344. (oh[1] << 7) | (oh[2] >> 1)
  345. self.audio_bound = oh[3] >> 2
  346. self.fixed = bool(oh[3] & 0x2)
  347. self.CSPS = bool(oh[3] & 0x1)
  348. self.system_audio_lock = bool(oh[4] & 0x80)
  349. self.system_video_lock = bool(oh[4] & 0x40)
  350. assert (oh[4] & 0x20) == 0x20
  351. self.video_bound = oh[4] & 0x1f
  352. self.packet_rate_restriction = \
  353. bool(oh[5] & 0x80)
  354. d = f.peek(1)
  355. self.streams = {}
  356. while ord(d) & 0x80:
  357. d = list(map(ord, f.read(3)))
  358. assert (d[1] & 0xc0) == 0xc0
  359. scaleflag = bool(d[1] & 0x20)
  360. self.streams[d[0]] = (((d[1] & 0x1f) <<
  361. 8) | d[2]) * (128, 1024)[scaleflag]
  362. d = f.peek(1)
  363. # PES packets
  364. d = f.peek(3)
  365. bytestoread = 2048
  366. while (f or d) and d == '\x00\x00\x01':
  367. try:
  368. d = f.read(bytestoread)
  369. self.append(PES(d))
  370. f.unread(d[self[-1].length:])
  371. except IndexError:
  372. f.unread(d)
  373. bytestoread <<= 2
  374. d = f.peek(4)
  375. else:
  376. self.SCR = keyw['SCR']
  377. self.muxr = keyw['muxr'] # in bps (converts to 50 bytes/sec)
  378. self.stuff_len = 0
  379. def __repr__(self):
  380. v = [ 'SCR', 'muxr', 'stuff_len',
  381. 'rate_bound', 'audio_bound', 'fixed', 'CSPS',
  382. 'system_audio_lock', 'system_video_lock',
  383. 'video_bound', 'packet_rate_restriction',
  384. 'streams',
  385. ]
  386. return '<Pack: %s %s>' % (', '.join(attribreprlist(self, v)),
  387. list.__repr__(self))
  388. def __str__(self):
  389. buf = []
  390. buf.append('\x00\x00\x01\xba')
  391. clock = (1 << 46) | (((self.SCR[0] >> 30) & 0x7) << 43) | \
  392. (1 << 42) | (((self.SCR[0] >> 15) & 0x7ffff) << 27) | \
  393. (1 << 26) | ((self.SCR[0] & 0x7fff) << 11) | (1 << 10) | \
  394. ((self.SCR[1] << 1) & 0x3fe) | 0x1
  395. for i in range(6):
  396. buf.append(chr(clock >> ((5 - i) * 8) & 0xff))
  397. muxr = self.muxr / 50 / 8
  398. buf.append(chr((muxr >> 14) & 0xff))
  399. buf.append(chr((muxr >> 6) & 0xff))
  400. buf.append(chr(((muxr << 2) & 0xfc) | 0x3))
  401. buf.append(chr(0xf8 | (self.stuff_len & 7)))
  402. buf.append('\xff' * self.stuff_len)
  403. buf.extend(list(map(str, self)))
  404. return ''.join(buf)
  405. # These are strings due to the floating point numbers
  406. frame_rate_code = {
  407. 0x0: 'forbidden', 0x1: '23.976', 0x2: '24', 0x3: '25',
  408. 0x4: '29.97', 0x5: '30', 0x6: '50', 0x7: '59.95',
  409. 0x8: '60', 0x9: 'reserved', 0xa: 'reserved',
  410. 0xb: 'reserved', 0xc: 'reserved', 0xd: 'reserved',
  411. 0xe: 'reserved', 0xf: 'reserved',
  412. }
  413. chroma_format = {
  414. 0x0: 'reserved', 0x1: '4:2:0', 0x2: '4:2:2', 0x3: '4:4:4',
  415. }
  416. class BitRate(int):
  417. def __init__(self, bitrate):
  418. super(BitRate, self).__init__(bitrate)
  419. def __str__(self):
  420. return repr(self)
  421. def __repr__(self):
  422. return '%dbps' % self
  423. def max_bitrate_descriptor(b):
  424. assert len(b) == 3
  425. return BitRate((((ord(b[0]) & 0x3f) << 16) |
  426. ((ord(b[1]) & 0xff) << 8) | (ord(b[0]) & 0xff)) * 50 * 8)
  427. class ISO639LangDescriptor(list):
  428. atypedict = {
  429. 0: 'undefined',
  430. 1: 'clean effects',
  431. 2: 'hearing impaired',
  432. 3: 'visual impaired commentary',
  433. }
  434. def __init__(self, b):
  435. assert len(b) % 4 == 0
  436. for i in range(len(b) / 4):
  437. lang = str(b[i * 4:i * 4 + 3], 'iso8859-1')
  438. atype = self.atypedict[ord(b[i * 4 + 3])]
  439. self.append((lang, atype))
  440. class VStreamDescriptor:
  441. def __init__(self, b):
  442. if not b:
  443. self.mpeg2 = None
  444. self.multiple_frame_rate = None
  445. self.frame_rate_code = None
  446. self.constrained_parameter = None
  447. self.still_picture = None
  448. return
  449. fb = ord(b[0])
  450. # XXX - libdvbpsi says no not for mpeg2 flag, but my data
  451. # seems to say otherwise.
  452. self.mpeg2 = not bool(fb & 0x04)
  453. assert (not self.mpeg2 and len(b) == 1) or (self.mpeg2 and
  454. len(b) == 3)
  455. self.multiple_frame_rate = bool(fb & 0x80)
  456. self.frame_rate_code = frame_rate_code[(fb & 0x78) >> 3]
  457. self.constrained_parameter = bool(fb & 0x02)
  458. self.still_picture = bool(fb & 0x01)
  459. if self.mpeg2:
  460. self.profile_level_indication = ord(b[1])
  461. tb = ord(b[2])
  462. self.chroma_format = chroma_format[(tb & 0xc0) >> 6]
  463. self.frame_rate_extension = bool(tb & 0x20)
  464. def __repr__(self):
  465. v = ['mpeg2', 'multiple_frame_rate', 'frame_rate_code',
  466. 'constrained_parameter', 'still_picture',
  467. 'profile_level_indication', 'chroma_format',
  468. 'frame_rate_extension', ]
  469. return '<VStream: %s>' % (', '.join(attribreprlist(self, v)), )
  470. class AC3Descriptor:
  471. src_dict = { 0: '48k', 1: '44.1k', 2: '32k', 3: None, 4: '48k or 44.1k',
  472. 5: '48k or 32k', 6: '44.1k or 32k', 7: '48k or 44.1k or 32k' }
  473. brc_dict = { 0: 32, 1: 40, 2: 48, 3: 56, 4: 64, 5: 80, 6: 96, 7: 112,
  474. 8: 128, 9: 160, 10: 192, 11: 224, 12: 256, 13: 320, 14: 384,
  475. 15: 448, 16: 512, 17: 576, 18: 640, }
  476. sm_dict = { 0: 'Not indicated', 1: 'NOT Dolby surround encoded',
  477. 2: 'Dolby surround encoded', 3: 'Reserved', }
  478. bsmod_dict = { 0: 'main: complete', 1: 'main: music and effects',
  479. 2: 'associated: visually imparied',
  480. 3: 'associated: hearing imparied', 4: 'associated: dialogue',
  481. 5: 'associated: commentary', 6: 'associated: emergency', }
  482. bs_mod = property(lambda x: x.bsmoddesc())
  483. num_channels = property(lambda x: x.numchan_dict[x.numchan])
  484. def bsmoddesc(self):
  485. if self.bsmod == 7:
  486. if (self.numchan & 0x8) and self.numchan == 1:
  487. return 'associated: voice over'
  488. else:
  489. return 'main: karaoke'
  490. else:
  491. return self.bsmod_dict[self.bsmod]
  492. numchan_dict = { 0: '1+1', 1: '1/0', 2: '2/0', 3: '3/0', 4: '2/1',
  493. 5: '3/1', 6: '2/2', 7: '3/2', 8: '1', 9: '<=2', 10: '<=3',
  494. 11: '<=4', 12: '<=5', 13: '<=6', 14: 'Reserved',
  495. 15: 'Reserved', }
  496. def __init__(self, data):
  497. srcbsid = ord(data[0])
  498. self.sample_rate = self.src_dict[srcbsid >> 5]
  499. self.bsid = srcbsid & 0x1f
  500. brcsm = ord(data[1])
  501. self.br_exact = (brcsm & 0x80) == 0x80
  502. self.bitrate = self.brc_dict[(brcsm >> 2) & 0x1f]
  503. self.surround_mode = self.sm_dict[brcsm & 0x3]
  504. bsmodnumchanfullsvc = ord(data[2])
  505. self.bsmod = bsmodnumchanfullsvc >> 6
  506. numchan = (bsmodnumchanfullsvc >> 1) & 0xf
  507. self.numchan = numchan
  508. # KTVU only puts 3 bytes here
  509. if len(data) == 3:
  510. return
  511. i = 4
  512. # dropped langcod as not used per A/52a 3.4
  513. if numchan == 0:
  514. i += 1
  515. if self.bsmod < 2:
  516. self.mainid = ord(data[i]) >> 5
  517. else:
  518. self.asvcflags = ord(data[i])
  519. i += 1
  520. if i >= len(data):
  521. self.text = ''
  522. return
  523. textlangcode = ord(data[i])
  524. textlen = textlangcode >> 1
  525. i += 1
  526. txt = data[i:i + textlen]
  527. if textlangcode & 1:
  528. self.text = txt.decode('latin-1')
  529. else:
  530. assert NotImplementedError, \
  531. 'the following code is untested'
  532. self.text = ''.join([chr(ord(x[0]) * 256 + ord(x[1])) for x in [txt[i:i+2] for i in range(0, len(txt), 2)]])
  533. def __repr__(self):
  534. v = ['sample_rate', 'bsid', 'br_exact', 'bitrate',
  535. 'surround_mode', 'bs_mod', 'num_channels', 'mainid',
  536. 'asvcflags', 'text', ]
  537. return '<AC3Descritor: %s>' % (', '.join(attribreprlist(self,
  538. v)), )
  539. class ContentAdvisory(list):
  540. def __init__(self, data):
  541. list.__init__(self)
  542. cnt = ord(data[0]) & 0x3f
  543. i = 1
  544. for j in range(cnt):
  545. region, dim = struct.unpack('>BB', data[i: i + 2])
  546. i += 2
  547. d = {}
  548. for j in range(dim):
  549. d[ord(data[i])] = ord(data[i + 1]) & 0xf
  550. i += 2
  551. desclen = ord(data[i])
  552. desc = MultiStringStruct(data[i + 1: i + 1 + desclen])
  553. self.append((region, d, desc))
  554. def __repr__(self):
  555. return '<ContentAdvisory: %s>' % list.__repr__(self)
  556. class ServiceLocationDescriptor(list):
  557. tag = 0xa1
  558. sldel = '>BH3c'
  559. def __init__(self, data):
  560. step = struct.calcsize(self.sldel)
  561. assert ((len(data) - 3) % step) == 0
  562. list.__init__(self)
  563. self.pcr_pid, cnt = struct.unpack('>HB', data[:3])
  564. self.pcr_pid &= 0x1fff
  565. for i in range(cnt):
  566. type, pid, a, b, c = struct.unpack(self.sldel,
  567. data[3 + i * step:3 + (i + 1) * step])
  568. pid &= 0x1fff
  569. lang = a + b + c
  570. if lang == '\x00' * 3:
  571. lang = None
  572. self.append({ 'type': type, 'pid': pid, 'lang': lang })
  573. def __repr__(self):
  574. return '<ServiceLocationDescriptor: pcr_pid: %d, %s>' % \
  575. (self.pcr_pid, list.__repr__(self))
  576. class MultiStringStruct(dict):
  577. '''Conforms to Section 6.10 of A/65b.'''
  578. def decode(self, comp, mode, data):
  579. assert (mode == 0 and comp in (1, 2)) or comp == 0, \
  580. 'mode: %#x, comp: %#x' % (mode, comp)
  581. if comp == 0:
  582. if mode == 0x3f:
  583. return data.decode('UTF-16-BE')
  584. elif mode == 0x3e:
  585. # http://www.unicode.org/reports/tr6/
  586. raise NotImplementedError('Unicode Technical Report #6, A Standard Compression Scheme for Unicode')
  587. # There are additional limitations
  588. assert mode < 0x34, 'Invalid mode: %#x' % mode
  589. return ''.join(map(lambda x, y = mode * 256:
  590. chr(ord(x) + y), data))
  591. assert (comp == 1 or comp == 2) and mode == 0xff, \
  592. 'Invalid comp: %#x, mode: %#x' % (comp, mode)
  593. if comp == 1:
  594. return atschuff.decode_title(data)
  595. else:
  596. return atschuff.decode_description(data)
  597. def __init__(self, data):
  598. cnt = ord(data[0])
  599. off = 1
  600. for i in range(cnt):
  601. lang = data[off:off + 3]
  602. nseg = ord(data[off + 3])
  603. segs = []
  604. self[lang] = segs
  605. off += 4
  606. for j in range(nseg):
  607. comp_type = ord(data[off])
  608. mode = ord(data[off + 1])
  609. nbytes = ord(data[off + 2])
  610. segs.append(self.decode(comp_type, mode,
  611. data[off + 3: off + 3 + nbytes]))
  612. class ComponentNameDescriptor(MultiStringStruct):
  613. def __repr__(self):
  614. return '<ComponentNameDescriptor: %s>' % \
  615. MultiStringStruct.__repr__(self)
  616. def FindMe(data):
  617. raise RuntimeError('found me')
  618. Descriptors = {
  619. # 0-63 Are listed in ISO 13818-1 Table 2-40
  620. 2: VStreamDescriptor, # ISO 13818-1 Section 2.6.2
  621. # 3: Audio, # ISO 13818-1 Section 2.6.3
  622. 10: ISO639LangDescriptor, # ISO 13818-1 Section 2.6.18
  623. 14: max_bitrate_descriptor, # ISO 13818-1 Section 2.6.26
  624. 0x81: AC3Descriptor, # A/53d Section 5.7.3.1
  625. 0x87: ContentAdvisory, # A/65b Section 6.9.4
  626. # 0xa0: ExtendedChannelName, # A/65b Section 6.9.5
  627. 0xa1: ServiceLocationDescriptor, # A/65b Section 6.9.6
  628. # 0xa2: TimeShiftedService, # A/65b Section 6.9.7
  629. 0xa3: ComponentNameDescriptor, # A/65b Section 6.9.8
  630. # 0xad: undefined, # A/53d Section 5.7.3.4
  631. 0xb6: FindMe, # A/57a Section 7 (ContentId)
  632. }
  633. PIDs = {
  634. 0x00: ('PAT', 'Program Association Table'),
  635. 0x01: ('CAT', 'Conditional Access Table'),
  636. 0x02: ('TSDT', 'Program Stream Descriptor Table'),
  637. 0x10: ('NIT', 'Network Information Table'),
  638. 0x11: ('BAT', 'Bouquet Association Table'),
  639. 0x11: ('SDT', 'Service Descriptor Table'),
  640. 0x12: ('EIT', 'Event Information Table'),
  641. 0x13: ('RST', 'running Status Table'),
  642. 0x14: ('TOT', 'Time Offset Table'),
  643. }
  644. def psip_calc_crc32(data, verbose = False, table = (
  645. 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
  646. 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
  647. 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
  648. 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
  649. 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
  650. 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
  651. 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
  652. 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
  653. 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
  654. 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
  655. 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
  656. 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
  657. 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
  658. 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
  659. 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
  660. 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
  661. 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
  662. 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
  663. 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
  664. 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
  665. 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
  666. 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
  667. 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
  668. 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
  669. 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
  670. 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
  671. 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
  672. 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
  673. 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
  674. 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
  675. 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
  676. 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
  677. 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
  678. 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
  679. 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
  680. 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
  681. 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
  682. 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
  683. 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
  684. 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
  685. 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
  686. 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
  687. 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
  688. 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
  689. 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
  690. 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
  691. 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
  692. 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
  693. 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
  694. 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
  695. 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
  696. 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
  697. 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
  698. 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
  699. 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
  700. 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
  701. 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
  702. 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
  703. 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
  704. 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
  705. 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
  706. 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
  707. 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
  708. 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
  709. )):
  710. '''Validate a PSIP CRC. Include the CRC in the data. The return value will be the valid data, or an exception will be raised if invalid.'''
  711. if verbose:
  712. i_crc = 0xffffffff
  713. for i in data:
  714. i_crc = ((i_crc << 8) & 0xffffffff) ^ table[(i_crc >>
  715. 24) ^ ord(i)]
  716. print(hex(i_crc))
  717. else:
  718. i_crc = reduce(lambda x, y: ((x << 8) & 0xffffffff) ^
  719. table[(x >> 24) ^ ord(y)], data, 0xffffffff)
  720. return i_crc
  721. def psip_crc32(data):
  722. return psip_calc_crc32(data) == 0
  723. def getdescriptors(tb):
  724. d = {}
  725. i = 0
  726. while i < len(tb):
  727. t = ord(tb[i])
  728. if t in d:
  729. l = ord(tb[i + 1])
  730. data = tb[i + 2: i + 2 + l]
  731. #print repr(d[t]), t, repr(data)
  732. #assert not d.has_key(t)
  733. l = ord(tb[i + 1])
  734. data = tb[i + 2: i + 2 + l]
  735. try:
  736. item = Descriptors[t](data)
  737. except KeyError:
  738. item = data
  739. try:
  740. d[t].append(item)
  741. except KeyError:
  742. d[t] = [ item ]
  743. i += 2 + l
  744. return d
  745. class TSPSIPHandler(dict):
  746. '''This object is used to represent the tables that come in on a
  747. specific PID. Since there can be multiple tables on a specific PID
  748. (ATSC's 0x1ffb), a dictionary of callable objects must be passed in,
  749. and the key is the table number.'''
  750. def __init__(self, *t):
  751. super(TSPSIPHandler, self).__init__()
  752. self.update(*t)
  753. self.discontinuity = True
  754. self.complete = False
  755. self.last_continuity = None
  756. # config knobs
  757. self.current_only = True
  758. self.ignerror = False
  759. def next_continuity(self, nc):
  760. if self.last_continuity is None:
  761. return True
  762. return ((self.last_continuity + 1) % 16) != nc
  763. def get_table_id(self):
  764. if self.complete:
  765. return self._table_id
  766. return None
  767. table_id = property(get_table_id)
  768. def decode_section_header(self, payload, i):
  769. self._table_id = ord(payload[i])
  770. self.syntax = bool(ord(payload[i + 1]) & 0x80)
  771. self.private = bool(ord(payload[i + 1]) & 0x40)
  772. self.sect_len = (((ord(payload[i + 1]) & 0xf) << 8) | \
  773. ord(payload[i + 2])) + 3
  774. self.stored_sects = [ payload[i:] ]
  775. #print 'bar', i, repr(payload)
  776. self.stored_len = len(self.stored_sects[0])
  777. self.discontinuity = False
  778. def __call__(self, p):
  779. '''Pass in a TSPacket instance.'''
  780. if p.error and not self.ignerror:
  781. return
  782. if p.start:
  783. payload = p.payload
  784. i = ord(payload[0]) + 1
  785. self.decode_section_header(payload, i)
  786. else:
  787. if self.discontinuity or \
  788. self.next_continuity(p.continuity):
  789. self.discontinuity = True
  790. return
  791. self.stored_sects.append(p.payload)
  792. self.stored_len += len(p.payload)
  793. while self.table_id != 0xff:
  794. if self.stored_len < self.sect_len:
  795. # we need more data
  796. self.last_continuity = p.continuity
  797. return
  798. payload = ''.join(self.stored_sects)
  799. assert len(payload) == self.stored_len
  800. if self.syntax:
  801. # XXX I may need to include the skipped part
  802. # above in the crc calculations.
  803. if not psip_crc32(payload[:self.sect_len]):
  804. raise ValueError('CRC check failed: %s' % \
  805. repr(payload[:self.sect_len]))
  806. self.extension = (ord(payload[3]) << 8) | \
  807. ord(payload[4])
  808. self.version = (ord(payload[5]) & 0x3e) >> 1
  809. self.current_next = bool(ord(payload[5]) & 1)
  810. self.section_number = ord(payload[6])
  811. self.last_section_number = ord(payload[7])
  812. self.protocol_version = ord(payload[8])
  813. # don't include the CRC
  814. self.table = payload[8:self.sect_len - 4]
  815. #if self.last_section_number:
  816. # print repr(self), repr(p)
  817. else:
  818. self.table = payload[3:self.sect_len]
  819. self.complete = True
  820. if self.current_only and not self.current_next:
  821. continue
  822. # If this fails there are multiple sections
  823. try:
  824. self[self.table_id].clean_up()
  825. self[self.table_id](self)
  826. except KeyError:
  827. pass # No handler, ignore or raise exception?
  828. # hmm. I had a packet with some low bits clear
  829. # the spec seems to imply that there can be multiple
  830. # sections, but every case I've seen in the world
  831. # there isn't.
  832. if ord(payload[self.sect_len]) != 0xff:
  833. #print 'prev:', self.last_section_number
  834. # I should make sure there is enough data
  835. self.decode_section_header(payload,
  836. self.sect_len)
  837. #print 'starting next section:', repr(self), repr(payload)
  838. continue
  839. else:
  840. break
  841. def __repr__(self, v=('table_id', 'syntax', 'private', 'table',
  842. 'extension', 'version', 'current_next', 'section_number',
  843. 'last_section_number', 'protocol_version', )):
  844. return '<TSPSIPHandler: %s, table objects: %s>' % \
  845. (', '.join(attribreprlist(self, v)), super(TSPSIPHandler,
  846. self).__repr__())
  847. class PSIPObject(object):
  848. def parse_table(self, tbl):
  849. raise NotImplementedError
  850. def repr_part(self):
  851. return []
  852. def __call__(self, psip):
  853. if psip.syntax:
  854. self.version = psip.version
  855. self.current_next = psip.current_next
  856. self.section_number = psip.section_number
  857. self.last_section_number = psip.last_section_number
  858. else:
  859. self.version = None
  860. self.current_next = None
  861. self.section_number = None
  862. self.last_section_number = None
  863. self.parse_table(psip)
  864. def __repr__(self, v=('version', 'current_next', 'section_number',
  865. 'last_section_number', )):
  866. return '<%s: %s>' % (self.__class__.__name__,
  867. ', '.join(attribreprlist(self, v) + self.repr_part()))
  868. class PAT(PSIPObject, dict):
  869. def __init__(self):
  870. '''In order to prevent confusion, you can't init w/ a packet.'''
  871. PSIPObject.__init__(self)
  872. dict.__init__(self)
  873. self.pid_dict = {}
  874. def clean_up(self):
  875. self.pid_dict = {}
  876. self.clear()
  877. def has_pid(self, pid):
  878. return pid in self.pid_dict
  879. def get_prog(self, pid):
  880. return self.pid_dict[pid]
  881. def parse_table(self, psip, s = '>HH', sl = struct.calcsize('>HH')):
  882. assert psip.table_id == 0x00
  883. for i in range(len(psip.table) / sl):
  884. prog, pid = struct.unpack(s, psip.table[i * sl:(i +
  885. 1) * sl])
  886. pid &= 0x1fff
  887. self.pid_dict[pid] = prog
  888. self[prog] = pid
  889. def repr_part(self):
  890. return [ dict.__repr__(self) ]
  891. def getaudiovideopids(pmt, lang = None):
  892. anapid = None
  893. apids = []
  894. vpids = []
  895. for i in pmt.es:
  896. cpid = i[1]
  897. j = i[2]
  898. if i[0] == 2:
  899. vpids.append(cpid)
  900. elif i[0] == 129:
  901. apids.append(cpid)
  902. elif 5 in j and i[0] != 5:
  903. assert 'AC-3' in [x[:4] for x in j[5]], (i, j)
  904. if lang is None or lang == j[10][0][0]:
  905. apids.append(cpid)
  906. else:
  907. anapid = cpid
  908. if not apids and anapid is not None:
  909. apids.append(anapid)
  910. return (apids, vpids)
  911. def iteravpids(stream, avpids):
  912. avpids = sets.ImmutableSet(avpids)
  913. for i in stream:
  914. if SimpleTSPacket(i).pid in avpids:
  915. yield i
  916. class PMT(PSIPObject, dict):
  917. def __init__(self):
  918. PSIPObject.__init__(self)
  919. dict.__init__(self)
  920. self.pcrpid = None
  921. self.es = []
  922. def clean_up(self):
  923. self.clear()
  924. del self.es[:]
  925. def __bool__(self):
  926. return len(self) or bool(self.es)
  927. def parse_table(self, psip):
  928. assert psip.table_id == 0x02
  929. tb = psip.table
  930. pcrpid = ((ord(tb[0]) & 0x1f) << 8) | ord(tb[1])
  931. self.pcrpid = pcrpid
  932. ltmp = ((ord(tb[2]) & 0xf) << 8) | ord(tb[3]) + 4
  933. self.update(getdescriptors(tb[4:ltmp]))
  934. i = ltmp
  935. es = self.es
  936. while i < len(tb):
  937. t = ord(tb[i])
  938. p = ((ord(tb[i + 1]) & 0x1f) << 8) | ord(tb[i + 2])
  939. l = ((ord(tb[i + 3]) & 0x0f) << 8) | ord(tb[i + 4])
  940. i += 5
  941. d = getdescriptors(tb[i:i + l])
  942. i += l
  943. es.append((t, p, d))
  944. def repr_part(self):
  945. return [ 'PCRpid: %d' % self.pcrpid, dict.__repr__(self),
  946. 'ES: %s' % repr(self.es) ]
  947. def channelmajorminorsort(x, y):
  948. if x['major'] != y['major']:
  949. return cmp(x['major'], y['major'])
  950. return cmp(x['minor'], y['minor'])
  951. def gpstoutc(gps, utcoff):
  952. gpstrue = gps - utcoff
  953. return gpstrue + 315990000
  954. class STT(PSIPObject):
  955. def __init__(self):
  956. '''In order to prevent confusion, you can't init w/ a packet.'''
  957. PSIPObject.__init__(self)
  958. def clean_up(self):
  959. self.utc = None
  960. self.ds_status = None
  961. self.ds_day_of_month = None
  962. self.ds_hour = None
  963. def parse_table(self, psip):
  964. assert psip.table_id == 0xcd and psip.table[0] == '\x00'
  965. system_time, gps_utc_offset, daylight_savings = \
  966. struct.unpack('>IBH', psip.table[1:8])
  967. ds_status = daylight_savings >> 15
  968. ds_day_of_month = (daylight_savings >> 8) & 0x1f
  969. ds_hour = daylight_savings & 0xff
  970. utc = gpstoutc(system_time, gps_utc_offset)
  971. self.utc = utc
  972. self.ds_status = ds_status
  973. self.ds_day_of_month = ds_day_of_month
  974. self.ds_hour = ds_hour
  975. def repr_part(self, v=('ds_status', 'ds_day_of_month', 'ds_hour', )):
  976. return [ repr(time.ctime(self.utc)), ] + attribreprlist(self, v)
  977. class MGT(list):
  978. def __init__(self, pidtable):
  979. '''In order to prevent confusion, you can't init w/ a packet.'''
  980. super(MGT, self).__init__()
  981. self.pidtable = pidtable
  982. self.watch = {}
  983. def clean_up(self):
  984. del self[:]
  985. def __call__(self, psip):
  986. assert psip.table_id == 0xc7 and psip.table[0] == '\x00'
  987. ntables = struct.unpack('>H', psip.table[1:3])[0]
  988. i = 3
  989. for foo in range(ntables):
  990. type, pid, version, nbytes, desclen = \
  991. struct.unpack('>HHBIH', psip.table[i:i + 11])
  992. i += 11
  993. pid &= 0x1fff
  994. version &= 0x1f
  995. desclen &= 0xfff
  996. desc = getdescriptors(psip.table[i:i + desclen])
  997. self.append((type, pid, version, nbytes, desc))
  998. i += desclen
  999. # start watch
  1000. if type >= 0x100 and type <= 0x17f:
  1001. if pid in self.pidtable:
  1002. # XXX - check that it's in watch
  1003. pass
  1004. else:
  1005. self.watch[type] = { 'pid': pid,
  1006. 'version': version, }
  1007. self.pidtable[pid] = TSPSIPHandler({
  1008. 0xcb: EIT() })
  1009. elif type >= 0x200 and type <= 0x27f:
  1010. if pid in self.pidtable:
  1011. # XXX - check that it's in watch
  1012. pass
  1013. else:
  1014. #print 'adding ett', pid
  1015. self.watch[type] = { 'pid': pid,
  1016. 'version': version, }
  1017. self.pidtable[pid] = TSPSIPHandler({
  1018. 0xcc: ETT() })
  1019. desclen = struct.unpack('>H', psip.table[i:i + 2])[0]
  1020. desclen &= 0xfff
  1021. desc = getdescriptors(psip.table[i:i + desclen])
  1022. self.desc = desc
  1023. #print `self`
  1024. def __repr__(self):
  1025. return '<MGT: descriptors: %s, loop: %s>' % (repr(self.desc),
  1026. list.__repr__(self))
  1027. class EIT(list):
  1028. def __init__(self):
  1029. '''In order to prevent confusion, you can't init w/ a packet.'''
  1030. super(EIT, self).__init__()
  1031. def clean_up(self):
  1032. del self[:]
  1033. def __call__(self, psip):
  1034. assert psip.table_id == 0xcb and psip.table[0] == '\x00'
  1035. ntables = ord(psip.table[1])
  1036. i = 2
  1037. for foo in range(ntables):
  1038. event_id, start_time, lochilen, lolength, titlelen = \
  1039. struct.unpack('>HIBHB', psip.table[i:i + 10])
  1040. i += 10
  1041. event_id &= 0x3fff
  1042. etm_location = (lochilen >> 4) & 0x3
  1043. length = ((lochilen & 0xf) << 16) | lolength
  1044. title = MultiStringStruct(psip.table[i:i + titlelen])
  1045. i += titlelen
  1046. desclen = struct.unpack('>H', psip.table[i:i + 2])[0]
  1047. i += 2
  1048. desclen &= 0xfff
  1049. desc = getdescriptors(psip.table[i:i + desclen])
  1050. i += desclen
  1051. # XXX - UTC offset should be what?
  1052. self.append((event_id, etm_location,
  1053. gpstoutc(start_time, 0), length, title, desc))
  1054. #print `self`
  1055. def __repr__(self):
  1056. return '<EIT: %s>' % list.__repr__(self)
  1057. class TVCT(PSIPObject, dict):
  1058. def __init__(self):
  1059. '''In order to prevent confusion, you can't init w/ a packet.'''
  1060. PSIPObject.__init__(self)
  1061. dict.__init__(self)
  1062. def clean_up(self):
  1063. self.clear()
  1064. def parse_table(self, psip):
  1065. assert psip.table_id == 0xc8
  1066. self['channels'] = []
  1067. tb = psip.table
  1068. i = ord(tb[0]) + 1
  1069. chancnt = ord(tb[i])
  1070. i += 1
  1071. for foo in range(chancnt):
  1072. shrtnm = ''.join([chr((ord(x[0]) <<
  1073. 8) | ord(x[1])) for x in [tb[i + x * 2:i + (x + 1) * 2] for
  1074. x in range(7)]]).rstrip(chr(0))
  1075. i += 7 * 2
  1076. major = (((ord(tb[i]) << 8) | ord(tb[i + 1])) >> 2) & \
  1077. 0x3ff
  1078. minor = ((ord(tb[i + 1]) & 0x3) << 8) | ord(tb[i + 2])
  1079. mode = ord(tb[i + 3])
  1080. i += 4
  1081. carrier, tsid, prog_num, flagsa, source, desc_len = \
  1082. struct.unpack('>IHHHHH', tb[i:i + 14])
  1083. i += 14
  1084. etm_loc = (flagsa & 0xc000) >> 14
  1085. access_control = bool(flagsa & 0x2000)
  1086. hidden = bool(flagsa & 0x1000)
  1087. hide_guide = bool(flagsa & 0x200)
  1088. service = flagsa & 0x3f
  1089. desc_len &= 0x3ff
  1090. descs = getdescriptors(tb[i:i + desc_len])
  1091. i += desc_len
  1092. self['channels'].append({ 'name': shrtnm,
  1093. 'major': major, 'minor': minor, 'mode': mode,
  1094. 'carrier': carrier, 'tsid': tsid,
  1095. 'prog_num': prog_num, 'source': source,
  1096. 'etm_loc': etm_loc,
  1097. 'access_control': access_control, 'hidden': hidden,
  1098. 'service': service, 'descriptors': descs })
  1099. desc_len = ((ord(tb[i]) & 0x3) << 8) | ord(tb[i + 1])
  1100. i += 2
  1101. self['descriptors'] = getdescriptors(tb[i:i + desc_len])
  1102. def repr_part(self):
  1103. return [ dict.__repr__(self), ]
  1104. class ETT(dict):
  1105. def __init__(self):
  1106. '''In order to prevent confusion, you can't init w/ a packet.'''
  1107. super(ETT, self).__init__()
  1108. def clean_up(self):
  1109. pass
  1110. def __call__(self, psip):
  1111. assert psip.table_id == 0xcc and psip.table[0] == '\x00'
  1112. id, event = struct.unpack('>HH', psip.table[1:5])
  1113. event >>= 2
  1114. desc = MultiStringStruct(psip.table[5:])
  1115. self[(id, event)] = desc
  1116. #print `self`
  1117. def __repr__(self):
  1118. return '<ETT: %s>' % dict.__repr__(self)
  1119. class TSPESHandler:
  1120. def __init__(self, cb):
  1121. self.cb = cb
  1122. self.discontinuity = True
  1123. self.complete = False
  1124. self.last_continuity = None
  1125. self.pes_len = None
  1126. def next_continuity(self, nc):
  1127. if self.last_continuity is None:
  1128. return True
  1129. return ((self.last_continuity + 1) % 16) == nc
  1130. def is_video(self):
  1131. return (self.stream_id & 0xf0) == 0xe0
  1132. def __call__(self, p):
  1133. if p.error:
  1134. #print 'got error:', `p`
  1135. return
  1136. if p.start:
  1137. if self.pes_len == 0:
  1138. assert self.is_video()
  1139. # if we were unbounded, dump the last one
  1140. if self.next_continuity(p.continuity):
  1141. self.cb(''.join(self.stored_sects))
  1142. payload = p.payload
  1143. if payload[:3] != '\x00\x00\x01':
  1144. raise ValueError('packet start code invalid')
  1145. self.stream_id = ord(payload[3])
  1146. self.pes_len = (ord(payload[4]) << 8) | ord(payload[5])
  1147. if not self.is_video():
  1148. #print 'pes', hex(self.stream_id), repr(p)
  1149. assert self.pes_len != 0
  1150. # A value of 0 indicates that the PES packet
  1151. # length is neither specified nor bounded and is
  1152. # allowed only in PES packets whose payload is a
  1153. # video elementary stream contained in Transport
  1154. # Stream packets. -- iso-13818-1 Sect. 2.4.3.7
  1155. if self.pes_len != 0:
  1156. self.pes_len += 6 # add in header
  1157. self.stored_sects = [ payload ]
  1158. self.stored_len = len(self.stored_sects[0])
  1159. self.discontinuity = False
  1160. else:
  1161. if self.discontinuity or \
  1162. not self.next_continuity(p.continuity):
  1163. self.discontinuity = True
  1164. return
  1165. self.stored_sects.append(p.payload)
  1166. self.stored_len += len(p.payload)
  1167. self.last_continuity = p.continuity
  1168. if self.stored_len < self.pes_len or self.pes_len == 0:
  1169. return
  1170. ps = ''.join(self.stored_sects)
  1171. assert self.stored_len == self.pes_len and \
  1172. self.pes_len == len(ps)
  1173. self.cb(ps)
  1174. def read_clock(buf):
  1175. assert len(buf) == 6
  1176. base = (int(ord(buf[0])) << 25) | (ord(buf[1]) << 17) | \
  1177. (ord(buf[2]) << 9) | (ord(buf[3]) << 1) | \
  1178. (ord(buf[4]) >> 7)
  1179. extension = ((ord(buf[4]) & 0x1) << 8) | ord(buf[5])
  1180. return (base, extension)
  1181. class SimpleTSPacket:
  1182. def __init__(self, p):
  1183. assert len(p) == TSPKTLEN
  1184. assert p[0] == TSSYNC
  1185. f = ord(p[1])
  1186. self.error = bool(f & 0x80)
  1187. self.start = bool(f & 0x40)
  1188. self.priority = bool(f & 0x20)
  1189. self.pid = ((f & 0x1f) << 8) + ord(p[2])
  1190. if self.pid == 0x1fff:
  1191. return
  1192. f = ord(p[3])
  1193. self.scramble = (f & 0xc0) >> 6
  1194. adapt = (f & 0x30) >> 4
  1195. self.continuity = f & 0xf
  1196. if self.error:
  1197. return
  1198. class TSPacket:
  1199. def __init__(self, *p):
  1200. assert len(p) <= 1
  1201. if len(p) == 0:
  1202. return
  1203. p = p[0]
  1204. origp = p
  1205. assert len(p) == TSPKTLEN
  1206. assert p[0] == TSSYNC
  1207. f = ord(p[1])
  1208. self.error = bool(f & 0x80)
  1209. self.start = bool(f & 0x40)
  1210. self.priority = bool(f & 0x20)
  1211. self.pid = ((f & 0x1f) << 8) + ord(p[2])
  1212. if self.pid == 0x1fff:
  1213. return
  1214. f = ord(p[3])
  1215. self.scramble = (f & 0xc0) >> 6
  1216. adapt = (f & 0x30) >> 4
  1217. self.continuity = f & 0xf
  1218. if self.error:
  1219. return
  1220. i = 4
  1221. adapt_len = ord(p[4])
  1222. # XXX - this is a large adapt, is it real?
  1223. if (adapt >= 2 and adapt_len >= 188) or self.scramble:
  1224. return
  1225. if adapt >= 2:
  1226. if adapt == 3:
  1227. pass
  1228. # see below
  1229. #assert adapt_len >= 0 and adapt_len <= 182
  1230. else:
  1231. pass
  1232. # my reading of the spec says this, but in
  1233. # practice this isn't the case
  1234. #assert adapt == 2 and adapt_len == 183
  1235. buf = p[i + 1:i + 1 + adapt_len]
  1236. #print self.error, self.start, self.priority, self.pid, self.scramble, adapt, self.continuity, adapt_len, i
  1237. #assert len(buf) == adapt_len, 'adapt: %d, lengths: %d, %d, buf[0]: %02x' % (adapt, len(buf), adapt_len, ord(buf[0]))
  1238. try:
  1239. self.decode_adaptation(buf)
  1240. except:
  1241. pass
  1242. # XXX - handle adpatation
  1243. i += 1 + adapt_len
  1244. self.payload = p[i:]
  1245. def decode_adaptation(self, adp):
  1246. if len(adp) == 0:
  1247. return
  1248. self.discontinuity_indicator = bool(ord(adp[0]) & 0x80)
  1249. self.random_access_indicator = bool(ord(adp[0]) & 0x40)
  1250. self.elementary_stream_priority = bool(ord(adp[0]) & 0x20)
  1251. PCR_flag = bool(ord(adp[0]) & 0x10)
  1252. OPCR_flag = bool(ord(adp[0]) & 0x08)
  1253. splicing_point_flag = bool(ord(adp[0]) & 0x04)
  1254. transport_private_data_flag = bool(ord(adp[0]) & 0x02)
  1255. adaptation_field_extension_flag = bool(ord(adp[0]) & 0x01)
  1256. i = 1
  1257. if PCR_flag:
  1258. self.PCR = read_clock(adp[i: i + 6])
  1259. i += 6
  1260. if OPCR_flag:
  1261. self.OPCR = read_clock(adp[i: i + 6])
  1262. i += 6
  1263. if splicing_point_flag:
  1264. self.splice_countdown = ord(adp[i])
  1265. i += 1
  1266. if transport_private_data_flag:
  1267. plen = ord(adp[i])
  1268. self.private_data = adp[i + 1: i + 1 + plen]
  1269. i += 1 + plen
  1270. if adaptation_field_extension_flag:
  1271. alen = ord(adp[i])
  1272. ltw_flag = ord(adp[i + 1]) & 0x80
  1273. piecewise_rate_flag = ord(adp[i + 1]) & 0x40
  1274. seamless_splice_flag = ord(adp[i + 1]) & 0x20
  1275. i += 2
  1276. if ltw_flag:
  1277. self.ltw_valid = bool(ord(adp[i]) & 0x80)
  1278. self.ltw_offset = ((ord(adp[i]) & 0x7f) <<
  1279. 8) | ord(adp[i + 1])
  1280. i += 2
  1281. if piecewise_rate_flag:
  1282. self.piecewise_rate = ((ord(adp[i]) & 0x3f) <<
  1283. 16) | (ord(adp[i + 1]) << 8) | \
  1284. ord(adp[i + 2])
  1285. i += 3
  1286. if seamless_splice_flag:
  1287. self.splice_type = (ord(adp[i]) & 0xf0) >> 4
  1288. self.DTS_next_AU = read_timestamp(adp[i:i + 5])
  1289. def __repr__(self):
  1290. v = ('pid', 'error', 'start', 'continuity', 'priority',
  1291. 'scramble', 'payload', 'discontinuity_indicator',
  1292. 'random_access_indicator', 'elementary_stream_priority',
  1293. 'PCR', 'OPCR', 'splice_countdown', 'private_data',
  1294. 'ltw_valid', 'ltw_offset', 'piecewise_rate', 'splice_type',
  1295. 'DTS_next_AU', )
  1296. return '<TSPacket: %s>' % ', '.join(attribreprlist(self, v))
  1297. def __str__(self):
  1298. '''We may want to save the original packet, and return it until the
  1299. data gets modified.'''
  1300. if self.error:
  1301. return '%c%s' % (TSSYNC, '\xff' * 187)
  1302. sb = (self.pid >> 8) & 0x1f
  1303. if self.error:
  1304. sb |= 0x80
  1305. if self.start:
  1306. sb |= 0x40
  1307. if self.priority:
  1308. sb |= 0x20
  1309. tb = self.pid & 0xff
  1310. fb = ((self.scramble & 0x3) << 6) | (self.continuity & 0xf)
  1311. alenstr = ''
  1312. if self.adaptation:
  1313. fb |= 1 << 5
  1314. alenstr = chr(len(self.adaptation))
  1315. if self.payload:
  1316. fb |= 1 << 4
  1317. ret = '%c%c%c%c%s%s%s' % (TSSYNC, sb, tb, fb, alenstr,
  1318. self.adaptation, self.payload)
  1319. if len(ret) != TSPKTLEN:
  1320. pass
  1321. #print >>sys.stderr, repr(self)
  1322. #print >>sys.stderr, len(self.adaptation), len(self.payload)
  1323. assert len(ret) == TSPKTLEN
  1324. return ret
  1325. class TSPStream:
  1326. '''This class takes a file object, and outputs TS packets.'''
  1327. def __init__(self, f, endpos = None):
  1328. self.f = f
  1329. self.endpos = endpos
  1330. def __iter__(self):
  1331. foundsync = False
  1332. buf = self.f.read(READBLK)
  1333. fppos = self.f.tell()
  1334. while buf:
  1335. if self.endpos is not None and \
  1336. fppos > self.endpos:
  1337. break
  1338. if not foundsync:
  1339. try:
  1340. start = buf.index(TSSYNC)
  1341. except ValueError:
  1342. buf = self.f.read(READBLK)
  1343. fppos = self.f.tell()
  1344. continue
  1345. try:
  1346. if buf[start + TSPKTLEN] == '\x47':
  1347. #if start != 0:
  1348. # print >>sys.stderr, 'sync off:', start, 'data:', repr(buf[:start])
  1349. foundsync = True
  1350. else:
  1351. #print >>sys.stderr, 'drop to sync:', start, 'data:', repr(buf[:start])
  1352. buf = buf[start + 1:]
  1353. continue
  1354. except IndexError:
  1355. nbuf = self.f.read(READBLK)
  1356. fppos = self.f.tell()
  1357. if not nbuf:
  1358. return
  1359. buf += nbuf
  1360. continue
  1361. if buf[start] != '\x47':
  1362. #print >>sys.stderr, 'sync failed'
  1363. foundsync = False
  1364. continue
  1365. t = buf[start:start + TSPKTLEN]
  1366. if len(t) != TSPKTLEN:
  1367. r = self.f.read(READBLK)
  1368. fppos = self.f.tell()
  1369. if not r:
  1370. #No more data
  1371. break
  1372. buf = buf[start:] + r
  1373. start = 0
  1374. if not buf:
  1375. buf = self.f.read(READBLK)
  1376. fppos = self.f.tell()
  1377. continue
  1378. self.itempos = fppos - len(buf)
  1379. yield t
  1380. buf = buf[start + TSPKTLEN:]
  1381. start = 0
  1382. if not buf:
  1383. buf = self.f.read(READBLK)
  1384. fppos = self.f.tell()
  1385. import getopt
  1386. import re
  1387. import sys
  1388. def usage():
  1389. print('Usage: %s -lmty <mpegtsstream>' % sys.argv[0])
  1390. print(' %s -k <pid> <mpegtsstream>' % sys.argv[0])
  1391. print(' %s -b [ -p ] <mpegtsstream>' % sys.argv[0])
  1392. print(' %s -c <channel> -o <output> <mpegtsstream>' % sys.argv[0])
  1393. print('')
  1394. print(' -l list channels')
  1395. print(' -m print PAT and PMT')
  1396. print(' -t print TVCT')
  1397. print('')
  1398. print(' -b bandwidth')
  1399. print(' -p sort by percentage')
  1400. print('')
  1401. print(' -c channel to capture')
  1402. print(' -o file to output channel')
  1403. print('')
  1404. print(' -k print PCR of pid stream')
  1405. print('')
  1406. print('Options for all:')
  1407. print(' -y file offset when done')
  1408. print(' -s <start> Starting pos')
  1409. print(' -e <end> Ending pos')
  1410. def findchannel(tvct, chan):
  1411. for i in tvct['channels']:
  1412. if isinstance(chan, int):
  1413. if i['minor'] == chan:
  1414. return i
  1415. elif isinstance(chan, tuple):
  1416. assert len(chan) == 2
  1417. if i['major'] == chan[0] and i['minor'] == chan[1]:
  1418. return i
  1419. else:
  1420. if i['name'] == chan:
  1421. return i
  1422. return None
  1423. def GetTVCT(tsstream):
  1424. listchan = True
  1425. needtvct = True
  1426. needpat = False
  1427. pat = PAT()
  1428. pmts = {}
  1429. tvct = TVCT()
  1430. psippids = { 0x00: TSPSIPHandler({ 0x00: pat }),
  1431. 0x1ffb: TSPSIPHandler({ 0xc8: tvct, }),
  1432. }
  1433. def getpmt(pid, pm = pmts, psp = psippids):
  1434. if pid not in pm:
  1435. pm[pid] = PMT()
  1436. psp[pid] = TSPSIPHandler({ 0x02: pm[pid] })
  1437. def needpmts(pm = pmts):
  1438. for i in pm.values():
  1439. if not i:
  1440. return True
  1441. return False
  1442. for i in map(TSPacket, tsstream):
  1443. try:
  1444. psippids[i.pid](i)
  1445. except ValueError:
  1446. continue
  1447. except KeyError:
  1448. pass
  1449. # XXX - we need to give up finding the TVCT after a while, and
  1450. # pretend we found it w/ some defaults. KCSM doesn't
  1451. # broadcast a TVCT.
  1452. if needtvct and tvct:
  1453. needtvct = False
  1454. needpat = True
  1455. if needpat and pat:
  1456. needpat = False
  1457. for j in pat.values():
  1458. getpmt(j)
  1459. if not (needtvct or needpat or needpmts()):
  1460. break
  1461. try:
  1462. lst = tvct['channels']
  1463. lst.sort(channelmajorminorsort)
  1464. except KeyError:
  1465. # unable to find TVCT
  1466. lst = sorted(list(pat.items()))
  1467. lst = list(map(lambda x, y: { 'name': 'PAT%d' % x[1],
  1468. 'prog_num': x[1], 'major': '?', 'minor': y}, lst,
  1469. list(range(1, len(pat) + 1))))
  1470. tvct = { 'channels': lst }
  1471. for i in lst:
  1472. if i['prog_num'] != 0:
  1473. i['PMT'] = pmts[pat[i['prog_num']]]
  1474. i['PMTpid'] = pat[i['prog_num']]
  1475. return tvct
  1476. def main():
  1477. try:
  1478. opts, args = getopt.getopt(sys.argv[1:], "bc:e:hk:lmo:pr:s:ty")
  1479. except getopt.GetoptError:
  1480. # print help information and exit:
  1481. usage()
  1482. sys.exit(2)
  1483. printbyteoffset = False
  1484. printbandwidth = False
  1485. printbandwidthpercent = False
  1486. listchan = False
  1487. channelsel = None
  1488. programsel = None
  1489. output = None
  1490. allmaps = False
  1491. printtvct = False
  1492. startpos = None
  1493. endpos = None
  1494. pcrpid = None
  1495. needtvct = False
  1496. needpat = False
  1497. channelfnd = False
  1498. needallmaps = False
  1499. cuts = False
  1500. for o, a in opts:
  1501. if o == '-b':
  1502. printbandwidth = True
  1503. elif o == "-c":
  1504. try:
  1505. channelsel = int(a)
  1506. except ValueError:
  1507. try:
  1508. channelsel = tuple(map(int,
  1509. re.split('[-.]', a, 1)))
  1510. except ValueError:
  1511. channelsel = a
  1512. if channelsel is not None:
  1513. needpat = True
  1514. needtvct = True
  1515. elif o == '-e':
  1516. endpos = int(a)
  1517. elif o == '-k':
  1518. pcrpid = int(a)
  1519. elif o == "-m":
  1520. allmaps = True
  1521. needallmaps = True
  1522. needpat = True
  1523. elif o == '-s':
  1524. startpos = int(a)
  1525. elif o == '-t':
  1526. printtvct = True
  1527. needtvct = True
  1528. elif o == "-l":
  1529. listchan = True
  1530. needpat = True
  1531. needtvct = True
  1532. elif o in ("-h", "--help"):
  1533. usage()
  1534. sys.exit()
  1535. elif o == '-o':
  1536. output = a
  1537. elif o == '-p':
  1538. printbandwidthpercent = True
  1539. elif o == '-r':
  1540. programsel = int(a)
  1541. needpat = True
  1542. elif o == '-y':
  1543. printbyteoffset = True
  1544. if len(args) != 1 or (channelsel and not output):
  1545. usage()
  1546. sys.exit()
  1547. inp = open(args[0])
  1548. if startpos is not None:
  1549. inp.seek(startpos)
  1550. s = TSPStream(inp, endpos)
  1551. pat = PAT()
  1552. pmts = {}
  1553. tvct = TVCT()
  1554. stt = STT()
  1555. def null(p):
  1556. #print 'null', repr(p)
  1557. pass
  1558. null.clean_up = lambda: None
  1559. psippids = { 0x00: TSPSIPHandler({ 0x00: pat }),
  1560. }
  1561. pidcnt = {}
  1562. mgt = MGT(psippids)
  1563. psippids[0x1ffb] = TSPSIPHandler({
  1564. #0xc7: mgt,
  1565. 0xc8: tvct,
  1566. 0xcd: stt,
  1567. })
  1568. def getpmt(pid, pm = pmts, psp = psippids):
  1569. if pid not in pm:
  1570. pm[pid] = PMT()
  1571. psp[pid] = TSPSIPHandler({ 0x02: pm[pid] })
  1572. def needpmts(pm = pmts):
  1573. for i in pm.values():
  1574. if not i:
  1575. return True
  1576. return False
  1577. lastpcr = None
  1578. lastpatpos = None
  1579. for i in map(TSPacket, s):
  1580. #if hasattr(i, 'splice_countdown') or hasattr(i, 'DTS_next_AU'):
  1581. # print 'splice_countdown:', repr(i)
  1582. #if hasattr(i, 'PCR'):
  1583. # print 'PCR:', repr(i)
  1584. #if i.pid in (48, 64, 80, 112):
  1585. # print `i`
  1586. # print s.itempos, `i`
  1587. if i.pid == 0 or lastpatpos is None:
  1588. lastpatpos = s.itempos
  1589. if pcrpid is not None and i.pid == pcrpid:
  1590. if lastpcr is not None:
  1591. # I've only seen 2703 as the largest
  1592. if i.PCR[0] - lastpcr[0] > 3000:
  1593. print(lastpatpos)
  1594. lastpcr = i.PCR
  1595. try:
  1596. psippids[i.pid](i)
  1597. except ValueError as x:
  1598. #import traceback
  1599. #print traceback.print_exc()
  1600. print('bad crc:', repr(i), file=sys.stderr)
  1601. continue
  1602. except KeyError:
  1603. pass
  1604. try:
  1605. pidcnt[i.pid] += 1
  1606. except KeyError:
  1607. pidcnt[i.pid] = 1
  1608. # XXX - we need to give up finding the TVCT after a while, and
  1609. # pretend we found it w/ some defaults. KCSM doesn't
  1610. # broadcast a TVCT.
  1611. if needtvct and tvct:
  1612. # Handle TVCT
  1613. needtvct = False
  1614. if programsel is not None and pat:
  1615. channelfnd = pat[programsel]
  1616. getpmt(channelfnd)
  1617. if channelsel is not None and pat and tvct:
  1618. channelfnd = findchannel(tvct, channelsel)
  1619. if channelfnd is None:
  1620. sys.stderr.write("Unable to find channel: %s\n"
  1621. % channelsel)
  1622. channelsel = None
  1623. else:
  1624. channelfnd = pat[channelfnd['prog_num']]
  1625. getpmt(channelfnd)
  1626. if needpat and pat:
  1627. needpat = False
  1628. for pn, j in pat.items():
  1629. if pn == 0:
  1630. # XXX - NIT
  1631. continue
  1632. if listchan or allmaps:
  1633. getpmt(j)
  1634. if needallmaps and pat and pmts:
  1635. for i in pat.values():
  1636. if i not in pmts:
  1637. break
  1638. needallmaps = False
  1639. #print `tvct`, `pat`, `pmts`
  1640. #print needtvct, needpat, needpmts(), printbandwidth, needallmaps
  1641. if not (needtvct or needpat or needpmts() or printbandwidth or
  1642. needallmaps or pcrpid):
  1643. break
  1644. if channelfnd and pmts[channelfnd]:
  1645. av = getaudiovideopids(pmts[channelfnd])
  1646. os.system("python tssel.py '%s' %s %s > '%s'" % (args[0],
  1647. channelfnd, ' '.join(map(str, itertools.chain(*av))),
  1648. output))
  1649. if allmaps:
  1650. print(repr(pat))
  1651. print(repr(pmts))
  1652. if printtvct:
  1653. print(repr(tvct))
  1654. if listchan:
  1655. #List channels
  1656. #try:
  1657. lst = tvct['channels']
  1658. lst.sort(channelmajorminorsort)
  1659. #except KeyError:
  1660. # # unable to find TVCT
  1661. # sys.stderr.write("unable to find TVCT table, faking it.\n")
  1662. # lst = pat.items()
  1663. # lst.sort()
  1664. # lst = map(lambda x, y: { 'prog_num': x[1], 'major': '?', 'minor': y}, lst, range(1, len(pat) + 1))
  1665. for i in lst:
  1666. if i['prog_num'] != 0 and i['prog_num'] != 0xffff:
  1667. #print repr(pmts[pat[i['prog_num']]])
  1668. av = getaudiovideopids(pmts[pat[i['prog_num']]])
  1669. prog_info = '\t'.join([','.join(map(str, x)) for x in av])
  1670. else:
  1671. prog_info = ''
  1672. print(('%(major)d.%(minor)d\t%(name)s\t' % i) + \
  1673. prog_info)
  1674. if printbandwidth:
  1675. totpkts = sum(pidcnt.values())
  1676. i = list(pidcnt.items())
  1677. if printbandwidthpercent:
  1678. def secondfirst(x, y):
  1679. if x[1] == y[1]:
  1680. return cmp(x[0], y[0])
  1681. return cmp(x[1], y[1])
  1682. i.sort(secondfirst)
  1683. else:
  1684. i.sort()
  1685. for pid, cnt in i:
  1686. print('%4d\t%d\t%5.2f' % (pid, cnt,
  1687. float(cnt) * 100 / totpkts))
  1688. if printbyteoffset:
  1689. print(inp.tell())
  1690. def justprint(v, p):
  1691. '''v is pid, p is the data'''
  1692. if v != 49:
  1693. return
  1694. pes = PES(p)
  1695. if pes.data[3] != '\x00':
  1696. print(repr(pes))
  1697. return
  1698. fc = findcodes(pes.data)
  1699. print('justprint', v, len(p), repr(pes), repr(pes.data[:20]), fc)
  1700. for i in [x for x in fc if x[1] == '\x00']:
  1701. print(repr(pes.data[i[0] + 3: i[0] + 7]))
  1702. if ((ord(pes.data[i[0] + 5]) & 0x38) >> 3) in (2, 3):
  1703. print('non I frame found: %d' % \
  1704. ((ord(pes.data[i[0] + 5]) & 0x38) >> 3))
  1705. if __name__ == '__main__':
  1706. if True:
  1707. main()
  1708. sys.exit(0)
  1709. if False:
  1710. ps = UnRead(sys.argv[1])
  1711. while ps:
  1712. print(repr(Pack(ps)))
  1713. sys.exit()
  1714. s = TSPStream(open(sys.argv[1]))
  1715. if False:
  1716. cleaned = open(sys.argv[2], 'w')
  1717. for j in s:
  1718. cleaned.write(str(j))
  1719. continue
  1720. sys.exit()
  1721. pids = {}
  1722. skipped = 0
  1723. cont = {}
  1724. count = 0
  1725. pat = PAT()
  1726. pmts = {}
  1727. pesstreams = {}
  1728. tvct = TVCT()
  1729. pidhandlers = { 0x00: TSPSIPHandler({ 0x00: pat }),
  1730. 0x1ffb: TSPSIPHandler({ 0xc8: tvct }),
  1731. }
  1732. first = last = None
  1733. for j in map(TSPacket, s):
  1734. count += 1
  1735. if j.pid == 8191 or (j.pid != 0 and j.pid != 48):
  1736. skipped += 1
  1737. continue
  1738. else:
  1739. #if j.pid > 4000:
  1740. #print `j`
  1741. try:
  1742. pidhandlers[j.pid](j)
  1743. except KeyError:
  1744. pass
  1745. except ValueError as x:
  1746. print('VE:', x)
  1747. #if pidhandlers[0x1ffb][0xc8]:
  1748. # print repr(pidhandlers[0x1ffb][0xc8])
  1749. # We should probably cache which ones we've added, and remove
  1750. # Ones that aren't there. Or do a clean_up callback.
  1751. for k in pidhandlers[0][0].values():
  1752. if k in pmts:
  1753. continue
  1754. pmts[k] = PMT()
  1755. pidhandlers[k] = TSPSIPHandler({ 0x02: pmts[k] })
  1756. for k in filter(lambda x: x.es, iter(pmts.values())):
  1757. #print repr(k)
  1758. for l in k.es:
  1759. if l[1] in pesstreams:
  1760. continue
  1761. print(repr(l))
  1762. pesstreams[l[1]] = TSPESHandler(lambda x, y =
  1763. l[1]: justprint(y, x))
  1764. pidhandlers[l[1]] = pesstreams[l[1]]
  1765. try:
  1766. if (cont[j.pid] + 1) % 16 != j.continuity:
  1767. pass
  1768. #print 'continuity failed'
  1769. cont[j.pid] = j.continuity
  1770. except KeyError:
  1771. cont[j.pid] = j.continuity
  1772. try:
  1773. pids[j.pid] += 1
  1774. except KeyError:
  1775. pids[j.pid] = 1
  1776. p = sorted(list(pids.items()))
  1777. print(p)
  1778. print('skipped:', skipped)
  1779. print('total:', count)