1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#!/usr/bin/env python
# $Id: oggtail.py,v 1.1 2001/05/02 01:49:11 andrew Exp $
# Send to stdout an ogg stream composed of the last PERCENT percent of
# file OGGFILE.
#
# example: oggtail.py --percent=PERCENT OGGFILE
#
# (PERCENT is rounded down to the nearest page. Cutting at the
# nearest packet would be more complicated.)
# First version by Mike Coleman <mkc@mathdogs.com>, April 2001
# TODO: This still doesn't work quite right. 'ogg123' complains with
# a warning if we pipe oggtail's output directly into it, so probably
# there's a minor error in the stream formatting somewhere.
import getopt
import ogg
import sys
_debug = 0
def usage():
sys.exit("usage: oggtail.py [--debug] --percent=PERCENT OGGFILE")
def debug(s):
if _debug:
sys.stderr.write(s)
sys.stderr.write('\n')
def copy_packets(from_, to_, outstream=None, count=None, start_pageno=0, start_granulepos=0):
"""
Copy 'count' packets from file 'from_' to file 'to_'. If 'count'
is None, copy the rest of 'from_'. Use 'start_granulepos' as the
initial granulepos tag for pages. Bytes will be skipped in
'from_' to sync, as needed, and copying will stop if a chain
boundary is encountered.
"""
# XXX: warn on desync, except at beginning?
copied = 0
written = 0
pageno = start_pageno
granule_offset = None
instream = None
# get it? insync? get it? i'm so funny you can't stand it!
insync = ogg.OggSyncState()
while count == None or copied < count:
b = from_.read(65536) # size doesn't really matter
if not b:
break
insync.bytesin(b)
skipped = 1
while skipped != 0:
skipped, page = insync.pageseek()
if skipped > 0:
if instream and page.serialno() != serialno:
# we hit a chain boundary
break
if not instream:
serialno = page.serialno()
instream = ogg.OggStreamState(serialno)
outstream = ogg.OggStreamState(serialno)
page.pageno = pageno
debug('*** %s' % page.pageno())
pageno = pageno + 1
instream.pagein(page)
while count == None or copied < count:
p = instream.packetout()
if not p:
break
if p.granulepos != -1:
if granule_offset == None:
granule_offset = p.granulepos
p.granulepos = p.granulepos - granule_offset
debug('copied %s' % p)
outstream.packetin(p)
copied = copied + 1
while 1:
pg = outstream.pageout()
if not pg:
break
debug('writing %s' % pg)
# FIX: should check success of write
written = written + pg.writeout(to_)
elif skipped < 0:
print 'skipped', -skipped, 'bytes'
pg = outstream.flush()
if pg:
written = written + pg.writeout(to_)
return (written, pageno - start_pageno, outstream)
opts, pargs = getopt.getopt(sys.argv[1:], '', ['debug', 'percent='])
print sys.argv, opts, pargs
if len(opts) < 1 or len(pargs) != 1:
usage()
for o, a in opts:
print o, a
if o == '--percent':
try:
percent = float(a)
except ValueError:
usage()
elif o == '--debug':
_debug = 1
else:
usage()
if not 0.0 <= percent <= 100.0:
usage()
file = pargs[0]
f = open(file, 'rb')
f.seek(0, 2)
f_size = f.tell()
f.seek(0)
# first copy the three header packets
n, p, os = copy_packets(f, sys.stdout, None, 3)
debug('copied header: %d bytes, %d pages' % (n, p))
debug(str(os))
# then seek to percent and copy out the rest
f.seek(int((100.0 - percent) / 100.0 * f_size))
n, p, os = copy_packets(f, sys.stdout, os)
debug('copied tail: %d bytes, %d pages' % (n, p))
debug(str(os))
|