Subliminal update

This commit is contained in:
Ruud
2012-04-08 17:35:39 +02:00
parent 5321037f08
commit 148e1940ac
85 changed files with 4375 additions and 3420 deletions
+89 -91
View File
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# enzyme - Video metadata parser
# Copyright (C) 2011 Antoine Bertin <diaoulael@gmail.com>
# Copyright (C) 2003-2006 Thomas Schueppel <stain@acm.org>
# Copyright (C) 2003-2006 Dirk Meyer <dischi@freevo.org>
# Copyright 2011-2012 Antoine Bertin <diaoulael@gmail.com>
# Copyright 2003-2006 Thomas Schueppel <stain@acm.org>
# Copyright 2003-2006 Dirk Meyer <dischi@freevo.org>
#
# This file is part of enzyme.
#
@@ -17,16 +17,14 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# along with enzyme. If not, see <http://www.gnu.org/licenses/>.
__all__ = ['Parser']
import os
import struct
import logging
import stat
from exceptions import *
from exceptions import ParseError
import core
# get logging object
@@ -49,7 +47,7 @@ START_CODE = {
0xB7 : 'sequence end',
0xB8 : 'group of pictures',
}
for i in range(0x01,0xAF):
for i in range(0x01, 0xAF):
START_CODE[i] = 'slice_start_code'
##------------------------------------------------------------------------
@@ -96,14 +94,14 @@ TS_SYNC = 0x47
##------------------------------------------------------------------------
FRAME_RATE = [
0,
24000.0/1001, ## 3-2 pulldown NTSC (CPB/Main Level)
24, ## Film (CPB/Main Level)
25, ## PAL/SECAM or 625/60 video
30000.0/1001, ## NTSC (CPB/Main Level)
30, ## drop-frame NTSC or component 525/60 (CPB/Main Level)
50, ## double-rate PAL
60000.0/1001, ## double-rate NTSC
60, ## double-rate, drop-frame NTSC/component 525/60 video
24000.0 / 1001, ## 3-2 pulldown NTSC (CPB/Main Level)
24, ## Film (CPB/Main Level)
25, ## PAL/SECAM or 625/60 video
30000.0 / 1001, ## NTSC (CPB/Main Level)
30, ## drop-frame NTSC or component 525/60 (CPB/Main Level)
50, ## double-rate PAL
60000.0 / 1001, ## double-rate NTSC
60, ## double-rate, drop-frame NTSC/component 525/60 video
]
##------------------------------------------------------------------------
@@ -114,9 +112,9 @@ FRAME_RATE = [
## it, a stream that doesn't adhere to one of these aspect ratios is
## technically considered non-compliant.
##------------------------------------------------------------------------
ASPECT_RATIO = ( None, # Forbidden
1.0, # 1/1 (VGA)
4.0 / 3, # 4/3 (TV)
ASPECT_RATIO = (None, # Forbidden
1.0, # 1/1 (VGA)
4.0 / 3, # 4/3 (TV)
16.0 / 9, # 16/9 (Widescreen)
2.21 # (Cinema)
)
@@ -131,7 +129,7 @@ class MPEG(core.AVContainer):
no additional metadata like title, etc; only codecs, length and
resolution is reported back.
"""
def __init__(self,file):
def __init__(self, file):
core.AVContainer.__init__(self)
self.sequence_header_offset = 0
self.mpeg_version = 2
@@ -190,29 +188,29 @@ class MPEG(core.AVContainer):
a.codec = ac
def dxy(self,file):
def dxy(self, file):
"""
get width and height of the video
"""
file.seek(self.sequence_header_offset+4,0)
file.seek(self.sequence_header_offset + 4, 0)
v = file.read(4)
x = struct.unpack('>H',v[:2])[0] >> 4
y = struct.unpack('>H',v[1:3])[0] & 0x0FFF
return (x,y)
x = struct.unpack('>H', v[:2])[0] >> 4
y = struct.unpack('>H', v[1:3])[0] & 0x0FFF
return (x, y)
def framerate_aspect(self,file):
def framerate_aspect(self, file):
"""
read framerate and aspect ratio
"""
file.seek(self.sequence_header_offset+7,0)
v = struct.unpack( '>B', file.read(1) )[0]
file.seek(self.sequence_header_offset + 7, 0)
v = struct.unpack('>B', file.read(1))[0]
try:
fps = FRAME_RATE[v&0xf]
fps = FRAME_RATE[v & 0xf]
except IndexError:
fps = None
if v>>4 < len(ASPECT_RATIO):
aspect = ASPECT_RATIO[v>>4]
if v >> 4 < len(ASPECT_RATIO):
aspect = ASPECT_RATIO[v >> 4]
else:
aspect = None
return (fps, aspect)
@@ -238,18 +236,18 @@ class MPEG(core.AVContainer):
if pos == -1 or len(buffer) - pos < 5:
buffer = buffer[-10:]
continue
ext = (ord(buffer[pos+4]) >> 4)
ext = (ord(buffer[pos + 4]) >> 4)
if ext == 8:
pass
elif ext == 1:
if (ord(buffer[pos+5]) >> 3) & 1:
if (ord(buffer[pos + 5]) >> 3) & 1:
self._set('progressive', True)
else:
self._set('interlaced', True)
return True
else:
log.debug('ext', ext)
buffer = buffer[pos+4:]
log.debug(u'ext: %r' % ext)
buffer = buffer[pos + 4:]
return False
@@ -292,12 +290,12 @@ class MPEG(core.AVContainer):
## Some parts in the code are based on mpgtx (mpgtx.sf.net)
def bitrate(self,file):
def bitrate(self, file):
"""
read the bitrate (most of the time broken)
"""
file.seek(self.sequence_header_offset+8,0)
t,b = struct.unpack( '>HB', file.read(3) )
file.seek(self.sequence_header_offset + 8, 0)
t, b = struct.unpack('>HB', file.read(3))
vrate = t << 2 | b >> 6
return vrate * 400
@@ -309,9 +307,9 @@ class MPEG(core.AVContainer):
if len(buffer) < 6:
return None
highbit = (ord(buffer[0])&0x20)>>5
highbit = (ord(buffer[0]) & 0x20) >> 5
low4Bytes= ((long(ord(buffer[0])) & 0x18) >> 3) << 30
low4Bytes = ((long(ord(buffer[0])) & 0x18) >> 3) << 30
low4Bytes |= (ord(buffer[0]) & 0x03) << 28
low4Bytes |= ord(buffer[1]) << 20
low4Bytes |= (ord(buffer[2]) & 0xF8) << 12
@@ -319,10 +317,10 @@ class MPEG(core.AVContainer):
low4Bytes |= ord(buffer[3]) << 5
low4Bytes |= (ord(buffer[4])) >> 3
sys_clock_ref=(ord(buffer[4]) & 0x3) << 7
sys_clock_ref|=(ord(buffer[5]) >> 1)
sys_clock_ref = (ord(buffer[4]) & 0x3) << 7
sys_clock_ref |= (ord(buffer[5]) >> 1)
return (long(highbit * (1<<16) * (1<<16)) + low4Bytes) / 90000
return (long(highbit * (1 << 16) * (1 << 16)) + low4Bytes) / 90000
def ReadSCRMpeg1(self, buffer):
@@ -340,7 +338,7 @@ class MPEG(core.AVContainer):
low4Bytes |= ord(buffer[3]) << 7;
low4Bytes |= ord(buffer[4]) >> 1;
return (long(highbit) * (1<<16) * (1<<16) + low4Bytes) / 90000;
return (long(highbit) * (1 << 16) * (1 << 16) + low4Bytes) / 90000;
def ReadPTS(self, buffer):
@@ -350,7 +348,7 @@ class MPEG(core.AVContainer):
high = ((ord(buffer[0]) & 0xF) >> 1)
med = (ord(buffer[1]) << 7) + (ord(buffer[2]) >> 1)
low = (ord(buffer[3]) << 7) + (ord(buffer[4]) >> 1)
return ((long(high) << 30 ) + (med << 15) + low) / 90000
return ((long(high) << 30) + (med << 15) + low) / 90000
def ReadHeader(self, buffer, offset):
@@ -358,25 +356,25 @@ class MPEG(core.AVContainer):
Handle MPEG header in buffer on position offset
Return None on error, new offset or 0 if the new offset can't be scanned
"""
if buffer[offset:offset+3] != '\x00\x00\x01':
if buffer[offset:offset + 3] != '\x00\x00\x01':
return None
id = ord(buffer[offset+3])
id = ord(buffer[offset + 3])
if id == PADDING_PKT:
return offset + (ord(buffer[offset+4]) << 8) + \
ord(buffer[offset+5]) + 6
return offset + (ord(buffer[offset + 4]) << 8) + \
ord(buffer[offset + 5]) + 6
if id == PACK_PKT:
if ord(buffer[offset+4]) & 0xF0 == 0x20:
if ord(buffer[offset + 4]) & 0xF0 == 0x20:
self.type = 'MPEG-1 Video'
self.get_time = self.ReadSCRMpeg1
self.mpeg_version = 1
return offset + 12
elif (ord(buffer[offset+4]) & 0xC0) == 0x40:
elif (ord(buffer[offset + 4]) & 0xC0) == 0x40:
self.type = 'MPEG-2 Video'
self.get_time = self.ReadSCRMpeg2
return offset + (ord(buffer[offset+13]) & 0x07) + 14
return offset + (ord(buffer[offset + 13]) & 0x07) + 14
else:
# I have no idea what just happened, but for some DVB
# recordings done with mencoder this points to a
@@ -412,10 +410,10 @@ class MPEG(core.AVContainer):
if id in [PRIVATE_STREAM1, PRIVATE_STREAM2]:
# private stream. we don't know, but maybe we can guess later
add = ord(buffer[offset+8])
add = ord(buffer[offset + 8])
# if (ord(buffer[offset+6]) & 4) or 1:
# id = ord(buffer[offset+10+add])
if buffer[offset+11+add:offset+15+add].find('\x0b\x77') != -1:
if buffer[offset + 11 + add:offset + 15 + add].find('\x0b\x77') != -1:
# AC3 stream
for a in self.audio:
if a.id == id:
@@ -442,18 +440,18 @@ class MPEG(core.AVContainer):
This MPEG starts with a sequence of 0x00 followed by a PACK Header
http://dvd.sourceforge.net/dvdinfo/packhdr.html
"""
file.seek(0,0)
file.seek(0, 0)
buffer = file.read(10000)
offset = 0
# seek until the 0 byte stop
while offset < len(buffer)-100 and buffer[offset] == '\0':
while offset < len(buffer) - 100 and buffer[offset] == '\0':
offset += 1
offset -= 2
# test for mpeg header 0x00 0x00 0x01
header = '\x00\x00\x01%s' % chr(PACK_PKT)
if offset < 0 or not buffer[offset:offset+4] == header:
if offset < 0 or not buffer[offset:offset + 4] == header:
if not force:
return 0
# brute force and try to find the pack header in the first
@@ -470,9 +468,9 @@ class MPEG(core.AVContainer):
self.ReadHeader(buffer, offset)
# store first timestamp
self.start = self.get_time(buffer[offset+4:])
self.start = self.get_time(buffer[offset + 4:])
while len(buffer) > offset + 1000 and \
buffer[offset:offset+3] == '\x00\x00\x01':
buffer[offset:offset + 3] == '\x00\x00\x01':
# read the mpeg header
new_offset = self.ReadHeader(buffer, offset)
@@ -486,12 +484,12 @@ class MPEG(core.AVContainer):
# skip padding 0 before a new header
while len(buffer) > offset + 10 and \
not ord(buffer[offset+2]):
not ord(buffer[offset + 2]):
offset += 1
else:
# seek to new header by brute force
offset += buffer[offset+4:].find('\x00\x00\x01') + 4
offset += buffer[offset + 4:].find('\x00\x00\x01') + 4
# fill in values for support functions:
self.__seek_size__ = 1000000
@@ -555,15 +553,15 @@ class MPEG(core.AVContainer):
self.video[-1]._set('id', id)
# new mpeg starting
if buffer[header_length+9:header_length+13] == \
if buffer[header_length + 9:header_length + 13] == \
'\x00\x00\x01\xB3' and not self.sequence_header_offset:
# yes, remember offset for later use
self.sequence_header_offset = offset + header_length+9
self.sequence_header_offset = offset + header_length + 9
elif ord(buffer[3]) == 189 or ord(buffer[3]) == 191:
# private stream. we don't know, but maybe we can guess later
id = id or ord(buffer[3]) & 0xF
if align and \
buffer[header_length+9:header_length+11] == '\x0b\x77':
buffer[header_length + 9:header_length + 11] == '\x0b\x77':
# AC3 stream
for a in self.audio:
if a.id == id:
@@ -581,7 +579,7 @@ class MPEG(core.AVContainer):
if ptsdts and ptsdts == ord(buffer[9]) >> 4:
if ord(buffer[9]) >> 4 != ptsdts:
log.warning('WARNING: bad PTS/DTS, please contact us')
log.warning(u'WARNING: bad PTS/DTS, please contact us')
return packet_length, None
# timestamp = self.ReadPTS(buffer[9:14])
@@ -595,8 +593,8 @@ class MPEG(core.AVContainer):
def isPES(self, file):
log.info('trying mpeg-pes scan')
file.seek(0,0)
log.info(u'trying mpeg-pes scan')
file.seek(0, 0)
buffer = file.read(3)
# header (also valid for all mpegs)
@@ -613,7 +611,7 @@ class MPEG(core.AVContainer):
return 0
if timestamp != None and not hasattr(self, 'start'):
self.get_time = self.ReadPTS
bpos = buffer[offset+timestamp:offset+timestamp+5]
bpos = buffer[offset + timestamp:offset + timestamp + 5]
self.start = self.get_time(bpos)
if self.sequence_header_offset and hasattr(self, 'start'):
# we have all informations we need
@@ -702,13 +700,13 @@ class MPEG(core.AVContainer):
# Transport Stream ===============================================
def isTS(self, file):
file.seek(0,0)
file.seek(0, 0)
buffer = file.read(TS_PACKET_LENGTH * 2)
c = 0
while c + TS_PACKET_LENGTH < len(buffer):
if ord(buffer[c]) == ord(buffer[c+TS_PACKET_LENGTH]) == TS_SYNC:
if ord(buffer[c]) == ord(buffer[c + TS_PACKET_LENGTH]) == TS_SYNC:
break
c += 1
else:
@@ -718,7 +716,7 @@ class MPEG(core.AVContainer):
self.type = 'MPEG-TS'
while c + TS_PACKET_LENGTH < len(buffer):
start = ord(buffer[c+1]) & 0x40
start = ord(buffer[c + 1]) & 0x40
# maybe load more into the buffer
if c + 2 * TS_PACKET_LENGTH > len(buffer) and c < 500000:
buffer += file.read(10000)
@@ -728,30 +726,30 @@ class MPEG(core.AVContainer):
c += TS_PACKET_LENGTH
continue
tsid = ((ord(buffer[c+1]) & 0x3F) << 8) + ord(buffer[c+2])
adapt = (ord(buffer[c+3]) & 0x30) >> 4
tsid = ((ord(buffer[c + 1]) & 0x3F) << 8) + ord(buffer[c + 2])
adapt = (ord(buffer[c + 3]) & 0x30) >> 4
offset = 4
if adapt & 0x02:
# meta info present, skip it for now
adapt_len = ord(buffer[c+offset])
adapt_len = ord(buffer[c + offset])
offset += adapt_len + 1
if not ord(buffer[c+1]) & 0x40:
if not ord(buffer[c + 1]) & 0x40:
# no new pes or psi in stream payload starting
pass
elif adapt & 0x01:
# PES
timestamp = self.ReadPESHeader(c+offset, buffer[c+offset:],
timestamp = self.ReadPESHeader(c + offset, buffer[c + offset:],
tsid)[1]
if timestamp != None:
if not hasattr(self, 'start'):
self.get_time = self.ReadPTS
timestamp = c + offset + timestamp
self.start = self.get_time(buffer[timestamp:timestamp+5])
self.start = self.get_time(buffer[timestamp:timestamp + 5])
elif not hasattr(self, 'audio_ok'):
timestamp = c + offset + timestamp
start = self.get_time(buffer[timestamp:timestamp+5])
start = self.get_time(buffer[timestamp:timestamp + 5])
if start is not None and self.start is not None and \
abs(start - self.start) < 10:
# looks ok
@@ -759,7 +757,7 @@ class MPEG(core.AVContainer):
else:
# timestamp broken
del self.start
log.warning('Timestamp error, correcting')
log.warning(u'Timestamp error, correcting')
if hasattr(self, 'start') and self.start and \
self.sequence_header_offset and self.video and self.audio:
@@ -786,31 +784,31 @@ class MPEG(core.AVContainer):
c = 0
while c + TS_PACKET_LENGTH < len(buffer):
if ord(buffer[c]) == ord(buffer[c+TS_PACKET_LENGTH]) == TS_SYNC:
if ord(buffer[c]) == ord(buffer[c + TS_PACKET_LENGTH]) == TS_SYNC:
break
c += 1
else:
return None
while c + TS_PACKET_LENGTH < len(buffer):
start = ord(buffer[c+1]) & 0x40
start = ord(buffer[c + 1]) & 0x40
if not start:
c += TS_PACKET_LENGTH
continue
tsid = ((ord(buffer[c+1]) & 0x3F) << 8) + ord(buffer[c+2])
adapt = (ord(buffer[c+3]) & 0x30) >> 4
tsid = ((ord(buffer[c + 1]) & 0x3F) << 8) + ord(buffer[c + 2])
adapt = (ord(buffer[c + 3]) & 0x30) >> 4
offset = 4
if adapt & 0x02:
# meta info present, skip it for now
offset += ord(buffer[c+offset]) + 1
offset += ord(buffer[c + offset]) + 1
if adapt & 0x01:
timestamp = self.ReadPESHeader(c+offset, buffer[c+offset:], tsid)[1]
timestamp = self.ReadPESHeader(c + offset, buffer[c + offset:], tsid)[1]
if timestamp is None:
# this should not happen
log.error('bad TS')
log.error(u'bad TS')
return None
return c + offset + timestamp
c += TS_PACKET_LENGTH
@@ -841,7 +839,7 @@ class MPEG(core.AVContainer):
if pos == None:
break
end = self.get_time(buffer[pos:]) or end
buffer = buffer[pos+100:]
buffer = buffer[pos + 100:]
file.close()
return end
@@ -855,7 +853,7 @@ class MPEG(core.AVContainer):
if end == None or self.start == None:
return None
if self.start > end:
return int(((long(1) << 33) - 1 ) / 90000) - self.start + end
return int(((long(1) << 33) - 1) / 90000) - self.start + end
return end - self.start
@@ -897,7 +895,7 @@ class MPEG(core.AVContainer):
return 0
file = open(self.filename)
log.debug('scanning file...')
log.debug(u'scanning file...')
while 1:
file.seek(self.__seek_size__ * 10, 1)
buffer = file.read(self.__sample_size__)
@@ -906,10 +904,10 @@ class MPEG(core.AVContainer):
pos = self.__search__(buffer)
if pos == None:
continue
log.debug('buffer position: %s' % self.get_time(buffer[pos:]))
log.debug(u'buffer position: %r' % self.get_time(buffer[pos:]))
file.close()
log.debug('done scanning file')
log.debug(u'done scanning file')
Parser = MPEG