|
|
@@ -0,0 +1,196 @@ |
|
|
|
#!/usr/bin/env python |
|
|
|
# Copyright 2006 John-Mark Gurney <gurney_j@resnet.uoregon.edu> |
|
|
|
'''MPEG-TS Handling''' |
|
|
|
|
|
|
|
__version__ = '$Change$' |
|
|
|
# $Id$ |
|
|
|
|
|
|
|
default_audio_lang = 'eng' |
|
|
|
|
|
|
|
import os |
|
|
|
import sets |
|
|
|
|
|
|
|
import sys |
|
|
|
mpegtspath = '/Users/jgurney/p4/bktrau/info' |
|
|
|
if mpegtspath not in sys.path: |
|
|
|
sys.path.append(mpegtspath) |
|
|
|
import mpegts |
|
|
|
|
|
|
|
from DIDLLite import StorageFolder, VideoItem, Resource |
|
|
|
from FSStorage import FSObject, registerklassfun |
|
|
|
|
|
|
|
from twisted.python import log, threadable |
|
|
|
from twisted.spread import pb |
|
|
|
from twisted.web import resource, server |
|
|
|
|
|
|
|
class _LimitedFile(file): |
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
self.__size = kwargs['size'] |
|
|
|
del kwargs['size'] |
|
|
|
file.__init__(self, *args, **kwargs) |
|
|
|
|
|
|
|
def remain(self): |
|
|
|
pos = self.tell() |
|
|
|
if pos > self.__size: |
|
|
|
return 0 |
|
|
|
return self.__size - pos |
|
|
|
|
|
|
|
def read(self, size=-1): |
|
|
|
if size < 0: |
|
|
|
return file.read(self, self.remain()) |
|
|
|
|
|
|
|
return file.read(self, min(size, self.remain())) |
|
|
|
|
|
|
|
def _gennameindexes(chan): |
|
|
|
ret = [] |
|
|
|
d = {} |
|
|
|
|
|
|
|
for i in chan: |
|
|
|
t = '%s %s.%s' % (i['name'], i['major'], i['minor']) |
|
|
|
ret.append(t) |
|
|
|
d[t] = i |
|
|
|
|
|
|
|
return ret, d |
|
|
|
|
|
|
|
class MPEGTSTransfer(pb.Viewable): |
|
|
|
def __init__(self, iterable, request): |
|
|
|
self.iter = iter(iterable) |
|
|
|
self.request = request |
|
|
|
request.registerProducer(self, 0) |
|
|
|
|
|
|
|
def resumeProducing(self): |
|
|
|
if not self.request: |
|
|
|
return |
|
|
|
# get data and write to request. |
|
|
|
try: |
|
|
|
data = self.iter.next() |
|
|
|
if data: |
|
|
|
# this .write will spin the reactor, calling |
|
|
|
# .doWrite and then .resumeProducing again, so |
|
|
|
# be prepared for a re-entrant call |
|
|
|
self.request.write(data) |
|
|
|
except StopIteration: |
|
|
|
if self.request: |
|
|
|
self.request.unregisterProducer() |
|
|
|
self.request.finish() |
|
|
|
self.request = None |
|
|
|
|
|
|
|
def pauseProducing(self): |
|
|
|
pass |
|
|
|
|
|
|
|
def stopProducing(self): |
|
|
|
# close zipfile |
|
|
|
self.request = None |
|
|
|
|
|
|
|
# Remotely relay producer interface. |
|
|
|
|
|
|
|
def view_resumeProducing(self, issuer): |
|
|
|
self.resumeProducing() |
|
|
|
|
|
|
|
def view_pauseProducing(self, issuer): |
|
|
|
self.pauseProducing() |
|
|
|
|
|
|
|
def view_stopProducing(self, issuer): |
|
|
|
self.stopProducing() |
|
|
|
|
|
|
|
synchronized = ['resumeProducing', 'stopProducing'] |
|
|
|
|
|
|
|
threadable.synchronize(MPEGTSTransfer) |
|
|
|
|
|
|
|
class MPEGTSResource(resource.Resource): |
|
|
|
isLeaf = True |
|
|
|
|
|
|
|
def __init__(self, iter_): |
|
|
|
resource.Resource.__init__(self) |
|
|
|
|
|
|
|
self.iter = iter_ |
|
|
|
|
|
|
|
def render(self, request): |
|
|
|
request.setHeader('content-type', 'video/mpeg') |
|
|
|
|
|
|
|
if request.method == 'HEAD': |
|
|
|
return '' |
|
|
|
|
|
|
|
# return data |
|
|
|
MPEGTSTransfer(self.iter, request) |
|
|
|
# and make sure the connection doesn't get closed |
|
|
|
return server.NOT_DONE_YET |
|
|
|
|
|
|
|
class MPEGTS(FSObject, VideoItem): |
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
path = kwargs['path'] |
|
|
|
del kwargs['path'] |
|
|
|
self.tvct = kwargs['tvct'] |
|
|
|
del kwargs['tvct'] |
|
|
|
|
|
|
|
log.msg('MPEGTS tvct:', self.tvct) |
|
|
|
|
|
|
|
kwargs['content'] = MPEGTSResource( |
|
|
|
mpegts.iteravpids(mpegts.TSPStream(open(path)), |
|
|
|
sum(mpegts.getaudiovideopids(self.tvct['PMT']), []))) |
|
|
|
VideoItem.__init__(self, *args, **kwargs) |
|
|
|
FSObject.__init__(self, path) |
|
|
|
|
|
|
|
self.url = '%s/%s' % (self.cd.urlbase, self.id) |
|
|
|
self.res = Resource(self.url, 'http-get:*:video/mpeg:*') |
|
|
|
|
|
|
|
def doUpdate(self): |
|
|
|
pass |
|
|
|
|
|
|
|
class MultiMPEGTS(FSObject, StorageFolder): |
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
path = kwargs['path'] |
|
|
|
del kwargs['path'] |
|
|
|
|
|
|
|
StorageFolder.__init__(self, *args, **kwargs) |
|
|
|
FSObject.__init__(self, path) |
|
|
|
|
|
|
|
# mapping from path to objectID |
|
|
|
self.pathObjmap = {} |
|
|
|
|
|
|
|
def doUpdate(self): |
|
|
|
f = mpegts.TSPStream(_LimitedFile(self.FSpath, |
|
|
|
size= 2*1024*1024)) |
|
|
|
self.tvct = mpegts.GetTVCT(f) |
|
|
|
|
|
|
|
doupdate = False |
|
|
|
origchildren, toindex = _gennameindexes(self.tvct['channels']) |
|
|
|
children = sets.Set(origchildren) |
|
|
|
for i in self.pathObjmap.keys(): |
|
|
|
if i not in children: |
|
|
|
doupdate = True |
|
|
|
# delete |
|
|
|
self.cd.delItem(self.pathObjmap[i]) |
|
|
|
del self.pathObjmap[i] |
|
|
|
|
|
|
|
for i in origchildren: |
|
|
|
if i in self.pathObjmap: |
|
|
|
continue |
|
|
|
|
|
|
|
# new object |
|
|
|
self.pathObjmap[i] = self.cd.addItem(self.id, MPEGTS, |
|
|
|
i, path = self.FSpath, tvct = toindex[i]) |
|
|
|
doupdate = True |
|
|
|
|
|
|
|
if doupdate: |
|
|
|
StorageFolder.doUpdate(self) |
|
|
|
|
|
|
|
def detectmpegts(path, fobj): |
|
|
|
try: |
|
|
|
f = mpegts.TSPStream(_LimitedFile(path, size= 2*1024*1024)) |
|
|
|
tvct = mpegts.GetTVCT(f) |
|
|
|
except: |
|
|
|
import traceback |
|
|
|
traceback.print_exc(file=log.logfile) |
|
|
|
raise |
|
|
|
|
|
|
|
log.msg('tvct:', tvct) |
|
|
|
if len(tvct['channels']) == 1: |
|
|
|
return None, None |
|
|
|
return MPEGTS, { 'path': path, 'tvct': tvct['channels'][0] } |
|
|
|
elif len(tvct['channels']) > 1: |
|
|
|
return MultiMPEGTS, { 'path': path } |
|
|
|
|
|
|
|
return None, None |
|
|
|
|
|
|
|
registerklassfun(detectmpegts) |