Files
DevOps/Kodi/Lenovo/addons/plugin.video.pseudotv.live/resources/lib/parsers/AVIParser.py

255 lines
7.3 KiB
Python

# 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 <http://www.gnu.org/licenses/>.
from globals import *
class AVIChunk:
def __init__(self):
self.empty()
def empty(self):
self.size = 0
self.fourcc = ''
self.datatype = 1
self.chunk = ''
def read(self, thefile):
data = thefile.readBytes(4)
try: self.size = struct.unpack('<i', data)[0]
except: self.size = 0
# Putting an upper limit on the chunk size, in case the file is corrupt
if self.size > 0 and self.size < 10000:
self.chunk = thefile.readBytes(self.size)
else:
self.chunk = ''
self.size = 0
class AVIList:
def __init__(self):
self.empty()
def empty(self):
self.size = 0
self.fourcc = ''
self.datatype = 2
def read(self, thefile):
data = thefile.readBytes(4)
self.size = struct.unpack('<i', data)[0]
try: self.size = struct.unpack('<i', data)[0]
except: self.size = 0
self.fourcc = thefile.read(4)
class AVIHeader:
def __init__(self):
self.empty()
def empty(self):
self.dwMicroSecPerFrame = 0
self.dwMaxBytesPerSec = 0
self.dwPaddingGranularity = 0
self.dwFlags = 0
self.dwTotalFrames = 0
self.dwInitialFrames = 0
self.dwStreams = 0
self.dwSuggestedBufferSize = 0
self.dwWidth = 0
self.dwHeight = 0
class AVIStreamHeader:
def __init__(self):
self.empty()
def empty(self):
self.fccType = ''
self.fccHandler = ''
self.dwFlags = 0
self.wPriority = 0
self.wLanguage = 0
self.dwInitialFrame = 0
self.dwScale = 0
self.dwRate = 0
self.dwStart = 0
self.dwLength = 0
self.dwSuggestedBuffer = 0
self.dwQuality = 0
self.dwSampleSize = 0
self.rcFrame = ''
class AVIParser:
def __init__(self):
self.Header = AVIHeader()
self.StreamHeader = AVIStreamHeader()
def determineLength(self, filename: str) -> int and float:
log("AVIParser: determineLength %s"%filename)
try: self.File = FileAccess.open(filename, "rb", None)
except:
log("AVIParser: Unable to open the file")
return 0
dur = int(self.readHeader())
self.File.close()
log('AVIParser: Duration is %s'%(dur))
return dur
def readHeader(self):
# AVI Chunk
data = self.getChunkOrList()
if data.datatype != 2:
log("AVIParser: Not an avi")
return 0
#todo fix
if data.fourcc[0:4] != "AVI ":
log("AVIParser: Wrong FourCC")
return 0
# Header List
data = self.getChunkOrList()
if data.fourcc != "hdrl":
log("AVIParser: Header not found")
return 0
# Header chunk
data = self.getChunkOrList()
if data.fourcc != 'avih':
log('Header chunk not found')
return 0
self.parseHeader(data)
# Stream list
data = self.getChunkOrList()
if self.Header.dwStreams > 10:
self.Header.dwStreams = 10
for i in range(self.Header.dwStreams):
if data.datatype != 2:
log("AVIParser: Unable to find streams")
return 0
listsize = data.size
# Stream chunk number 1, the stream header
data = self.getChunkOrList()
if data.datatype != 1:
log("AVIParser: Broken stream header")
return 0
self.StreamHeader.empty()
self.parseStreamHeader(data)
# If this is the video header, determine the duration
if self.StreamHeader.fccType == 'vids':
return self.getStreamDuration()
# If this isn't the video header, skip through the rest of these
# stream chunks
try:
if listsize - data.size - 12 > 0:
self.File.seek(listsize - data.size - 12, 1)
data = self.getChunkOrList()
except:
log("AVIParser: Unable to seek")
log("AVIParser: Video stream not found")
return 0
def getStreamDuration(self):
try:
return int(self.StreamHeader.dwLength / (float(self.StreamHeader.dwRate) / float(self.StreamHeader.dwScale)))
except:
return 0
def parseHeader(self, data):
try:
header = struct.unpack('<iiiiiiiiiiiiii', data.chunk)
self.Header.dwMicroSecPerFrame = header[0]
self.Header.dwMaxBytesPerSec = header[1]
self.Header.dwPaddingGranularity = header[2]
self.Header.dwFlags = header[3]
self.Header.dwTotalFrames = header[4]
self.Header.dwInitialFrames = header[5]
self.Header.dwStreams = header[6]
self.Header.dwSuggestedBufferSize = header[7]
self.Header.dwWidth = header[8]
self.Header.dwHeight = header[9]
except:
self.Header.empty()
log("AVIParser: Unable to parse the header")
def parseStreamHeader(self, data):
try:
self.StreamHeader.fccType = data.chunk[0:4].decode('utf-8')
self.StreamHeader.fccHandler = data.chunk[4:8].decode('utf-8')
header = struct.unpack('<ihhiiiiiiiid', data.chunk[8:])
self.StreamHeader.dwFlags = header[0]
self.StreamHeader.wPriority = header[1]
self.StreamHeader.wLanguage = header[2]
self.StreamHeader.dwInitialFrame = header[3]
self.StreamHeader.dwScale = header[4]
self.StreamHeader.dwRate = header[5]
self.StreamHeader.dwStart = header[6]
self.StreamHeader.dwLength = header[7]
self.StreamHeader.dwSuggestedBuffer = header[8]
self.StreamHeader.dwQuality = header[9]
self.StreamHeader.dwSampleSize = header[10]
self.StreamHeader.rcFrame = ''
except:
self.StreamHeader.empty()
log("AVIParser: Error reading stream header")
def getChunkOrList(self):
try: data = self.File.readBytes(4).decode('utf-8')
except: data = self.File.read(4)
if data == "RIFF" or data == "LIST":
dataclass = AVIList()
elif len(data) == 0:
dataclass = AVIChunk()
dataclass.datatype = 3
else:
dataclass = AVIChunk()
dataclass.fourcc = data
# Fill in the chunk or list info
dataclass.read(self.File)
return dataclass