# Copyright (C) 2024 Jason Anderson, Lunatixz # # # This file is part of PseudoTV Live. # # PseudoTV Live is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # PseudoTV Live is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PseudoTV Live. If not, see . from globals import * class TSPacket: def __init__(self): self.pid = 0 self.errorbit = 1 self.pesstartbit = 0 self.adaption = 1 self.adaptiondata = '' self.pesdata = '' class TSParser: def __init__(self): self.monitor = MONITOR() def determineLength(self, filename: str) -> int and float: log("TSParser: determineLength %s"%filename) self.pid = -1 try: self.File = FileAccess.open(filename, "rb", None) except: log("TSParser: Unable to open the file") return 0 self.filesize = self.getFileSize() self.packetLength = self.findPacketLength() if self.packetLength <= 0: return 0 start = self.getStartTime() log('TSParser: Start %s'%(start)) end = self.getEndTime() log('TSParser: End - %s'%(end)) if end > start: dur = int((end - start) / 90000) else: dur = 0 self.File.close() log("TSParser: Duration is %s"%(dur)) return dur def findPacketLength(self): log('TSParser: findPacketLength') maxbytes = 600 start = 0 self.packetLength = 0 while not self.monitor.abortRequested() and maxbytes > 0: maxbytes -= 1 try: data = self.File.readBytes(1) data = struct.unpack('B', data) if data[0] == 71: if start > 0: end = self.File.tell() break else: start = self.File.tell() # A minimum of 188, so skip the rest self.File.seek(187, 1) except: log('TSParser: Exception in findPacketLength') return if (start > 0) and (end > start): log('TSParser: Packet Length: %s'%(end - start)) return (end - start) return def getFileSize(self): size = 0 try: pos = self.File.tell() self.File.seek(0, 2) size = self.File.tell() self.File.seek(pos, 0) except: pass return size def getStartTime(self): # A reasonably high number of retries in case the PES starts in the middle # and is it's maximum length maxpackets = 12000 log('TSParser: getStartTime') try: self.File.seek(0, 0) except: return 0 while not self.monitor.abortRequested() and maxpackets > 0: packet = self.readTSPacket() maxpackets -= 1 if packet == None: return 0 if packet.errorbit == 0 and packet.pesstartbit == 1: ret = self.getPTS(packet) if ret > 0: self.pid = packet.pid log('TSParser: PID: %s'%(self.pid)) return ret return 0 def getEndTime(self): log('TSParser: getEndTime') packetcount = int(self.filesize / self.packetLength) try: self.File.seek((packetcount * self.packetLength)- self.packetLength, 0) except: return 0 maxpackets = 12000 while not self.monitor.abortRequested() and maxpackets > 0: packet = self.readTSPacket() maxpackets -= 1 if packet == None: log('TSParser: getEndTime got a null packet') return 0 if packet.errorbit == 0 and packet.pesstartbit == 1 and packet.pid == self.pid: ret = self.getPTS(packet) if ret > 0: log('TSParser: getEndTime returning time') return ret else: try: self.File.seek(-1 * (self.packetLength * 2), 1) except: log('TSParser: exception') return 0 log('TSParser: getEndTime no found end time') return 0 def getPTS(self, packet): timestamp = 0 log('TSParser: getPTS') try: data = struct.unpack('19B', packet.pesdata[:19]) # start code if data[0] == 0 and data[1] == 0 and data[2] == 1: # cant be a navigation packet if data[3] != 190 and data[3] != 191: offset = 0 if (data[9] >> 4) == 3: offset = 5 # a little dangerous...ignoring the LSB of the timestamp timestamp = ((data[9 + offset] >> 1) & 7) << 30 timestamp = timestamp | (data[10 + offset] << 22) timestamp = timestamp | ((data[11 + offset] >> 1) << 15) timestamp = timestamp | (data[12 + offset] << 7) timestamp = timestamp | (data[13 + offset] >> 1) return timestamp except: log('TSParser: exception in getPTS') pass log('TSParser: getPTS returning 0') return 0 def readTSPacket(self): packet = TSPacket() pos = 0 try: data = self.File.readBytes(4) pos = 4 data = struct.unpack('4B', data) if data[0] == 71: packet.pid = (data[1] & 31) << 8 packet.pid = packet.pid | data[2] # skip tables and null packets if packet.pid < 21 or packet.pid == 8191: self.File.seek(self.packetLength - 4, 1) else: packet.adaption = (data[3] >> 4) & 3 packet.errorbit = data[1] >> 7 packet.pesstartbit = (data[1] >> 6) & 1 if packet.adaption > 1: data = self.File.readBytes(1) length = struct.unpack('B', data)[0] if length > 0: data = self.File.readBytes(length) else: length = 0 pos += length + 1 if pos < 188: # read the PES data packet.pesdata = self.File.readBytes(self.packetLength - pos) except: log('TSParser: readTSPacket exception') return None return packet