A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
4.4 KiB

  1. #!/usr/bin/env python
  2. # Copyright 2006 John-Mark Gurney <gurney_j@resnet.uoregon.edu>
  3. '''MPEG-TS Handling'''
  4. __version__ = '$Change$'
  5. # $Id$
  6. default_audio_lang = 'eng'
  7. import os
  8. import sets
  9. import sys
  10. mpegtspath = '/Users/jgurney/p4/bktrau/info'
  11. if mpegtspath not in sys.path:
  12. sys.path.append(mpegtspath)
  13. import mpegts
  14. from DIDLLite import StorageFolder, VideoItem, Resource
  15. from FSStorage import FSObject, registerklassfun
  16. from twisted.python import log, threadable
  17. from twisted.spread import pb
  18. from twisted.web import resource, server
  19. class _LimitedFile(file):
  20. def __init__(self, *args, **kwargs):
  21. self.__size = kwargs['size']
  22. del kwargs['size']
  23. file.__init__(self, *args, **kwargs)
  24. def remain(self):
  25. pos = self.tell()
  26. if pos > self.__size:
  27. return 0
  28. return self.__size - pos
  29. def read(self, size=-1):
  30. if size < 0:
  31. return file.read(self, self.remain())
  32. return file.read(self, min(size, self.remain()))
  33. def _gennameindexes(chan):
  34. ret = []
  35. d = {}
  36. for i in chan:
  37. t = '%s %s.%s' % (i['name'], i['major'], i['minor'])
  38. ret.append(t)
  39. d[t] = i
  40. return ret, d
  41. class MPEGTSTransfer(pb.Viewable):
  42. def __init__(self, iterable, request):
  43. self.iter = iter(iterable)
  44. self.request = request
  45. request.registerProducer(self, 0)
  46. def resumeProducing(self):
  47. if not self.request:
  48. return
  49. # get data and write to request.
  50. try:
  51. data = self.iter.next()
  52. if data:
  53. # this .write will spin the reactor, calling
  54. # .doWrite and then .resumeProducing again, so
  55. # be prepared for a re-entrant call
  56. self.request.write(data)
  57. except StopIteration:
  58. if self.request:
  59. self.request.unregisterProducer()
  60. self.request.finish()
  61. self.request = None
  62. def pauseProducing(self):
  63. pass
  64. def stopProducing(self):
  65. # close zipfile
  66. self.request = None
  67. # Remotely relay producer interface.
  68. def view_resumeProducing(self, issuer):
  69. self.resumeProducing()
  70. def view_pauseProducing(self, issuer):
  71. self.pauseProducing()
  72. def view_stopProducing(self, issuer):
  73. self.stopProducing()
  74. synchronized = ['resumeProducing', 'stopProducing']
  75. threadable.synchronize(MPEGTSTransfer)
  76. class MPEGTSResource(resource.Resource):
  77. isLeaf = True
  78. def __init__(self, iter_):
  79. resource.Resource.__init__(self)
  80. self.iter = iter_
  81. def render(self, request):
  82. request.setHeader('content-type', 'video/mpeg')
  83. if request.method == 'HEAD':
  84. return ''
  85. # return data
  86. MPEGTSTransfer(self.iter, request)
  87. # and make sure the connection doesn't get closed
  88. return server.NOT_DONE_YET
  89. class MPEGTS(FSObject, VideoItem):
  90. def __init__(self, *args, **kwargs):
  91. path = kwargs['path']
  92. del kwargs['path']
  93. self.tvct = kwargs['tvct']
  94. del kwargs['tvct']
  95. log.msg('MPEGTS tvct:', self.tvct)
  96. kwargs['content'] = MPEGTSResource(
  97. mpegts.iteravpids(mpegts.TSPStream(open(path)),
  98. sum(mpegts.getaudiovideopids(self.tvct['PMT']), [])))
  99. VideoItem.__init__(self, *args, **kwargs)
  100. FSObject.__init__(self, path)
  101. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  102. self.res = Resource(self.url, 'http-get:*:video/mpeg:*')
  103. def doUpdate(self):
  104. pass
  105. class MultiMPEGTS(FSObject, StorageFolder):
  106. def __init__(self, *args, **kwargs):
  107. path = kwargs['path']
  108. del kwargs['path']
  109. StorageFolder.__init__(self, *args, **kwargs)
  110. FSObject.__init__(self, path)
  111. # mapping from path to objectID
  112. self.pathObjmap = {}
  113. def doUpdate(self):
  114. f = mpegts.TSPStream(_LimitedFile(self.FSpath,
  115. size= 2*1024*1024))
  116. self.tvct = mpegts.GetTVCT(f)
  117. doupdate = False
  118. origchildren, toindex = _gennameindexes(self.tvct['channels'])
  119. children = sets.Set(origchildren)
  120. for i in self.pathObjmap.keys():
  121. if i not in children:
  122. doupdate = True
  123. # delete
  124. self.cd.delItem(self.pathObjmap[i])
  125. del self.pathObjmap[i]
  126. for i in origchildren:
  127. if i in self.pathObjmap:
  128. continue
  129. # new object
  130. self.pathObjmap[i] = self.cd.addItem(self.id, MPEGTS,
  131. i, path = self.FSpath, tvct = toindex[i])
  132. doupdate = True
  133. if doupdate:
  134. StorageFolder.doUpdate(self)
  135. def detectmpegts(path, fobj):
  136. try:
  137. f = mpegts.TSPStream(_LimitedFile(path, size= 2*1024*1024))
  138. tvct = mpegts.GetTVCT(f)
  139. except:
  140. import traceback
  141. traceback.print_exc(file=log.logfile)
  142. raise
  143. log.msg('tvct:', tvct)
  144. if len(tvct['channels']) == 1:
  145. return None, None
  146. return MPEGTS, { 'path': path, 'tvct': tvct['channels'][0] }
  147. elif len(tvct['channels']) > 1:
  148. return MultiMPEGTS, { 'path': path }
  149. return None, None
  150. registerklassfun(detectmpegts)