from struct import pack, unpack
import io
import os.path
import sys
LEN = 24064
PACKET_SIZE = 188
def WriteBufInternal(f, sc, tm):
f.write(pack('>QQ', sc, tm))
def FrameGenerator(ts, packetoffset=0, maxlen=-1):
s = io.open(ts, 'rb', buffering=LEN)
buf = bytearray(PACKET_SIZE)
streamtype = -1
pid = -1
while len(s.peek())>=PACKET_SIZE: #Skip if not a whole packet left
while ord(s.peek()[0]) != 0x47: #Sync stream
s.read(1)
print("Skipped 1 byte")
filepos = s.tell()
if maxlen >= 0 and filepos > maxlen: #Limit reading for debugging purposes
break
if s.readinto(buf) != PACKET_SIZE: #If not enough data quit
s.close()
break
if not buf[3]&0x10: #Skip if there is no payload
continue
pos = buf[4] + 5 if buf[3]&0x20 else 4 #Skip adaption field
if pos > PACKET_SIZE: #Skip large adaption field
continue
tpid = ((buf[1]&0x1F) << 8) | buf[2] #Read current packet pid
if (not (buf[pos] or buf[pos+1] or not buf[pos+2]&0x01)
and buf[pos+3]&0xf0 == 0xe0
and buf[1]&0x040): #Find video pid
pid = tpid
elif pid>=0 and pid != tpid: #Wrong pid
continue
pts = -1
if buf[1]&0x40: #Pusi
if buf[pos] or buf[pos+1] or not buf[pos+2]&0x01:
print("Broken startcode")
continue
if buf[pos+7]&0x80: #PTS present?
pts = ((buf[pos+9]&0xE) << 29 | (buf[pos+10]&0xFF) << 22 |
(buf[pos+11]&0xFE) << 14 | (buf[pos+12]&0xFF) << 7 |
(buf[pos+13]&0xFE) >> 1)
pos = buf[pos+8] + 9
while pos < PACKET_SIZE - 4:
if not (buf[pos] or buf[pos+1] or not buf[pos+2]&0x01):
sc = buf[pos+3]
if streamtype < 0: #Stream type is unknown
if sc in [0x00, 0xb3, 0xb8]:
streamtype = 0
print("Detected MPEG2 stream type")
elif sc in [0x09]:
streamtype = 1
print("Detected H264 stream type")
else:
pos += 1
continue
if streamtype == 0: #MPEG2
if sc in [0x00, 0xb3, 0xb8]: #Picture, sequence, group start code
retpos = retpts = retdat = retpos2 = -1
if sc == 0xb3 and pts >=0 : #Sequence header
retpos = filepos
retpts = pts
if pos < PACKET_SIZE - 6:
retdat = sc | buf[pos+4] << 8 | buf[pos+5] << 16
if pts >= 0:
retdat |= (pts << 31) | 0x1000000;
retpos2 = filepos + pos
yield (retpts, retpos, retdat, retpos2)
elif streamtype == 1:
if sc == 0x09:
retpos = retpts = retdat = retpos2 = -1
retdat = sc | (buf[pos+4] << 8)
if pts >= 0:
retdat |= (pts << 31) | 0x1000000
retpos2 = filepos + pos
if (buf[pos+4]&0x60) == 0:
if pts >= 0:
retpos = filepos
retpts = pts
yield (retpts, retpos, retdat, retpos2)
pos += 1
def ProcessScAp(ts, maxlen=-1):
scf = open(ts+'.sc', 'wb')
apf = open(ts+'.ap', 'wb')
filesize = os.path.getsize(ts)
lastprogress = -1
for (pts, pos, dat, pos2) in FrameGenerator(ts, maxlen=maxlen):
curpos = max(pos, pos2)
if curpos >= 0:
progress = curpos*100/filesize
if progress > lastprogress:
print("{0}%".format(progress))
lastprogress = progress
if pts >= 0 and pos >= 0:
WriteBufInternal(apf, pos, pts)
if dat >= 0 and pos2 >= 0:
WriteBufInternal(scf, pos2, dat)
scf.close()
apf.close()
Friday, January 4, 2013
A python .ap and .sc generator for enigma2 recordings
In my quest to make a transcoder for recordings on my enigma2 satellite receiver I've made an .ap and .sc file generator in python. The .ap and .sc files are used for more accurate skipping in the recording when viewing on the receiver. As input and inspiration I used ProcessApSc plugin by Michel Hartman and also the OpenPli source for pvrparse.cpp.
Subscribe to:
Post Comments (Atom)
Hello,
ReplyDeleteI have a vuduo also so i would like to use your script :) but how?