# -*- Mode: Python -*-

import struct
import sys

def as_binary (n, width):
    r = []
    bit = 1 << width
    while width:
        width -= 1
        bit >>= 1
        if n & bit:
            r.append ('1')
        else:
            r.append ('0')
    return ''.join (r)

#print as_binary (get_bits (0xf00f, 4, 8), 16)
#print as_binary (get_bits (0x0ff0, 4, 8), 16)
def get_bits (x, start, nbits):
    r = 0
    for i in range (nbits):
        if x & (1<<(start+i)):
            r |= 1<<i
    return r

def grok_pcr (s):
    # wtf? 42 bits of timestamp?
    # one source leads me to believe the actual format is
    # 33:6:9 - where the six bits in the middle are reserved.
    # this seems to match my sample TS, which has the six bits
    # always set to one, and a fixed pattern in the lower nine.
    n = 0
    for ch in s:
        n <<= 8
        n |= ord (ch)
    # roll off the bottom nine+6 bits
    n >>= 9+6
    return n

def progressive_divide (n, parts):
    result = []
    for part in parts:
        n, rem = divmod (n, part)
        result.append (rem)
    result.append (n)
    return result

last_pcr = 0

def parse_adaptation (p):
    global last_pcr
    flen = ord(p[0])
    x = ord(p[1])
    disc = get_bits (x, 7, 1)
    rand = get_bits (x, 6, 1)
    esp  = get_bits (x, 5, 1)
    pcr  = get_bits (x, 4, 1)
    opcr = get_bits (x, 3, 1)
    splc = get_bits (x, 2, 1)
    tpd  = get_bits (x, 1, 1)
    afe  = get_bits (x, 0, 1)
    if tpd:
        sys.stderr.write ('> %s\n' %  (pretty (p)))
    if pcr:
        pcr = grok_pcr (p[2:8])
        # convert to hh:mm:ss
        sec = pcr / 90000       # 90kHz clock
        ss, mm, hh, dd = progressive_divide (sec, (60, 60, 24))
        if dd:
            sys.stderr.write ('%d %02d:%02d:%02d\n' % (dd, hh, mm, ss))
        if sec < last_pcr:
            ss, mm, hh, dd = progressive_divide (last_pcr, (60, 60, 24))
            sys.stderr.write ('? %d %02d:%02d:%02d\n' % (dd, hh, mm, ss))
        last_pcr = sec
        #sys.stderr.write (as_binary (pcr, 34))
        #import pdb; pdb.set_trace()
    if opcr:
        opcr = grok_pcr (p[8:14])
        sys.stderr.write ('  ' + as_binary (opcr, 34) + '\n')
    return locals()

#unsigned char SonyA1::year() { return BCD( GetBits(352, 8) ); }
#unsigned char SonyA1::month() { return BCD( GetBits(347, 5) ); }
#unsigned char SonyA1::day() { return BCD( GetBits(338, 6) ); }
#unsigned char SonyA1::hour() { return BCD( GetBits(386, 6) ); }
#unsigned char SonyA1::minute() { return BCD( GetBits(377, 7) ); }
#unsigned char SonyA1::second() { return BCD( GetBits(369, 7) ); }
#
#unsigned char SonyA1::timecode_hour() { return BCD( GetBits(322, 6) ); }
#unsigned char SonyA1::timecode_minute() { return BCD( GetBits(313, 7) ); }
#unsigned char SonyA1::timecode_second() { return BCD( GetBits(305, 7) ); }
#unsigned char SonyA1::timecode_frame() { return BCD( GetBits(298, 6) ); }
#bool SonyA1::scene_start() { return !GetBits(394, 1); }

# ^ 8,5,6

def decode_pat (p):
    if ord(p[1]) in (0x70, 0x73):
        import pdb; pdb.set_trace()

def pretty (p):
    return ''.join ('%02x' % ord(ch) for ch in p)

def parse_ts (f, files):
    pause_mode = False
    while 1:
        header = f.read (4)
        if not header:
            break
        elif header[0] != 'G':
            break
        else:
            byte23 = struct.unpack ('>h', header[1:3])[0]
            #print '%16s' % (as_binary (byte23, 16),)
            tei = get_bits (byte23, 15, 1)
            pus = get_bits (byte23, 14, 1)
            tp  = get_bits (byte23, 13, 1)
            pid = get_bits (byte23, 0, 13)
            #print '%s %s %s %2x' % (tei, pus, tp, pid)
            byte4 = ord (header[3])
            #print 'byte4', as_binary (byte4, 8)
            scramble = get_bits (byte4, 6, 2)
            adapt = get_bits (byte4, 5, 1)
            pde = get_bits (byte4, 4, 1)
            continuity = get_bits (byte4, 0, 4)
            #print '%s %s %s %d' % (scramble, adapt, pde, continuity)
            #if adapt:
            #    raise ValueError ("adaptation field not handled yet")
            #elif not pde:
            #    raise ValueError ("no data??")
            #else:
            packet = f.read (184)

            if pid == 2065:
                sys.stderr.write ('%d %r\n' % (adapt, pretty (packet[63:],)))

            if False and adapt:
                #sys.stderr.write ('[%5d] ' % (pid,))
                parse_adaptation (packet)
            else:
                #sys.stderr.write ('.')
                pass
            if 0:
                if not files.has_key (pid):
                    files[pid] = open ('%d.ts' % (pid,), 'wb')
                #files[pid].write (header)
                files[pid].write (packet)
                if pus and packet[0] == '\xa0':
                    info = packet[-23:]
                    sys.stderr.write ('[%5d] %s\n' % (pid, pretty (info)))
                    #sys.stderr.write ('[%5d] %r\n' % (pid, info))
    return files

def print_pcr (pcr):
    sec = pcr / 90000       # 90kHz clock
    ss, mm, hh, dd = progressive_divide (sec, (60, 60, 24))
    sys.stderr.write ('%d %02d:%02d:%02d\n' % (dd, hh, mm, ss))

def yrgl (s):
    return ''.join ([as_binary (ord(ch),8) for ch in s])

MB = 1024 * 1024
def parse_ts2 (f):
    last = 400
    n = 0
    while 1:
        packet = f.read (188)
        n += 188
        if not packet:
            break
        elif packet[0] != 'G':
            break
        else:
            byte23 = struct.unpack ('>h', packet[1:3])[0]
            #pus = get_bits (byte23, 14, 1)
            pus = byte23 & 16384
            pid = byte23 & 0x1fff
            if pid == 2065: # and packet[4] == '\xe0':
                info2 = packet[109:109+3]
                if info2 != last:
                    sys.stderr.write ('%5d %2x %s %r\n' % (n/MB, ord(packet[4]), pretty(info2), pretty (packet[63:],)))
                last = info2
            if False and pus and packet[4] == '\xa0':
                info = packet[-23:]
                #if info[-1] == '\x00':
                    #sys.stderr.write ('%5d %s\n' % (n/MB, pretty (info)))
                sys.stderr.write ('%5d %s\n' % (n/MB, yrgl (info[8:16])))

# 11:34:51 => 11:35:03
# d1b4d130 => 84b5d130
#
# 11010001 10110100 11010001 0011 0000
#   5   1     3  4     1   1
# 10000100 10110101 11010001 0011 0000
#   0   4     3  5     1   1              
#
#
def bcd (ch, bits0, bits1):
    return ((ord(ch)>>4) & bits0) * 10 + (ord(ch) & bits1)

def grok_hms (hms):
    ss = bcd (hms[0], 0x7, 0xf)
    mm = bcd (hms[1], 0x7, 0xf)
    hh = bcd (hms[2], 0x3, 0xf)
    return hh, mm, ss

# d4e706
# 11010100 11100111 00000110
#   dd        mm       yy 
#   14        07       06
# xx001111 xxx23333 44445555
#

def grok_ymd (ymd):
    dd = bcd (ymd[0], 3, 15)
    mm = bcd (ymd[1], 1, 15)
    yy = bcd (ymd[2], 15, 15)
    return 2000 + yy, mm, dd

MB = 1024 * 1024
def parse_ts3 (f):
    last = (None, None)
    n = 0
    num = 0
    fo = None
    while 1:
        packet = f.read (188)
        n += 188
        if not packet:
            break
        elif packet[0] != 'G':
            break
        else:
            byte23 = struct.unpack ('>h', packet[1:3])[0]
            #pus = get_bits (byte23, 14, 1)
            pus = byte23 & 16384
            pid = byte23 & 0x1fff
            if pid == 2065:
                byte4 = ord (packet[3])
                adapt = get_bits (byte4, 5, 1)

                ymd = packet[109:112]
                hms = packet[113:117]
                if True: #info2 != last[0]:
                    sys.stderr.write ('%5d %d %s %r\n' % (n/MB, adapt, grok_ymd(ymd), grok_hms(hms)))
                    #fo = open ('clip_%03d.%s.m2t' % (num,pretty(info2)), 'wb')
                    num += 1
                last = ymd, packet
            if pus and packet[4] == '\xa0':
                info = packet[-23:]
                if info[-1] == '\x00':
                    sys.stderr.write ('%5d %s\n' % (n/MB, pretty (info)))
                    sys.stderr.write ('%5d %s\n' % (n/MB, pretty (last[1][63:])))
            #if fo:
            #    fo.write (packet)

if __name__ == '__main__':
    if 0:
        files = {}
        try:
            parse_ts (open (sys.argv[1], 'rb'), files)
        finally:
            for pid, file in files.iteritems():
                file.close()
    else:
        parse_ts3 (open (sys.argv[1], 'rb'))
