diff --git a/mpegtsmod.py b/mpegtsmod.py new file mode 100644 index 0000000..ff5a9de --- /dev/null +++ b/mpegtsmod.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# Copyright 2006 John-Mark Gurney +'''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) diff --git a/pymediaserv b/pymediaserv index 17bd2fb..8b8983e 100755 --- a/pymediaserv +++ b/pymediaserv @@ -25,7 +25,7 @@ def tryloadmodule(mod): pass # ZipStorage w/ tar support should be last as it will gobble up empty files. -modules = [ 'dvd', 'shoutcast', 'ZipStorage' ] +modules = [ 'dvd', 'mpegtsmod', 'shoutcast', 'ZipStorage' ] modmap = {} for i in modules: modmap[i] = tryloadmodule(i)