Package flumotion :: Package component :: Package plugs :: Package hlsmultibitrate :: Module multibitrate
[hide private]

Source Code for Module flumotion.component.plugs.hlsmultibitrate.multibitrate

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  from twisted.web.resource import Resource 
 19  from twisted.web.static import Data 
 20   
 21  from flumotion.common import log 
 22  from flumotion.common.errors import ComponentStartError 
 23  from flumotion.component.misc.httpserver.httpserver import HTTPFileStreamer 
 24  from flumotion.component.plugs.base import ComponentPlug 
 25   
 26  __version__ = "$Rev$" 
 27   
 28   
 29  HEADER = "#EXTM3U" 
 30   
 31  ENTRY_TEMPLATE = \ 
 32  """ 
 33  #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%(bitrate)d 
 34  %(stream-url)s""" 
 35   
 36  CONTENT_TYPE = "application/vnd.apple.mpegurl" 
 37  IPAD_TARGET = 600000 
 38  IPHONE_TARGET = 200000 
 39  AUDIO_TARGET = 96000 
 40   
 41   
42 -class PlaylistResource(Resource):
43 """I am a resource for m3u8 playlists, rendering multibitrate playlists 44 based on the user-agent, so that IPad clients gets a variant playlist in 45 which the first element correspond to the higher bitrate, whilst IPhone 46 ones receive a playlist where the first element has a lower bitrate. 47 """ 48
49 - def __init__(self, entries, target_bitrate=None, min_bitrate=AUDIO_TARGET):
50 Resource.__init__(self) 51 52 self._entries = entries 53 self._target_bitrate = target_bitrate 54 self._min_bitrate = min_bitrate
55
56 - def render_GET(self, request):
57 playlist = [HEADER] 58 59 # If the target bitrate was not set, sort by user-agent 60 agent = request.getHeader('User-Agent') 61 if agent and not self._target_bitrate: 62 if 'ipad' in agent.lower(): 63 self._target_bitrate = IPAD_TARGET 64 else: 65 self._target_bitrate = IPHONE_TARGET 66 67 # Sort playlist entries by proximity to the target bitrate 68 self._entries.sort(key=lambda x: abs(x['bitrate'] - 69 self._target_bitrate)) 70 71 # Check first entry's bitrate for the minimum bitrate as we might not 72 # want to have audio streams in the first position. 73 if self._entries[0]['bitrate'] < self._min_bitrate: 74 self._entries.append(self._entries.pop(0)) 75 76 for entry in self._entries: 77 playlist.append(ENTRY_TEMPLATE % entry) 78 79 request.setHeader('Content-type', CONTENT_TYPE) 80 return "\n".join(playlist)
81 82
83 -class MultibiratePlaylistPlug(ComponentPlug):
84 """I am a component plug for a http-server which plugs in a 85 http resource containing a HLS variant playlist. 86 """ 87
88 - def start(self, component):
89 """ 90 @type component: L{HTTPFileStreamer} 91 """ 92 if not isinstance(component, HTTPFileStreamer): 93 raise ComponentStartError( 94 "A MultibitratePlug %s must be plugged into a " 95 " HTTPFileStreamer component, not a %s" % ( 96 self, component.__class__.__name__)) 97 log.debug('multibitrate', 'Attaching to %r' % (component, )) 98 99 props = self.args['properties'] 100 resource = Resource() 101 playlist = PlaylistResource(props.get('playlist-entry', []), 102 props.get('target-bitrate', None)) 103 104 resource.putChild(props.get('playlist-name', 'main.m3u8'), playlist) 105 component.setRootResource(resource)
106 107
108 -def test():
109 import sys 110 from twisted.internet import reactor 111 from twisted.python.log import startLogging 112 from twisted.web.server import Site 113 startLogging(sys.stderr) 114 115 properties = { 116 'playlist-entry': [ 117 {'stream-url': 118 'http://example.com/iphone/low/stream.m3u8', 119 'bitrate': 100000}, 120 {'stream-url': 121 'http://example.com/iphone/medium/stream.m3u8', 122 'bitrate': 200000}, 123 {'stream-url': 124 'http://example.com/iphone/high/stream.m3u8', 125 'bitrate': 400000}, 126 ]} 127 128 root = Resource() 129 mount_point = Resource() 130 playlist = PlaylistResource(properties['playlist-entry']) 131 root.putChild('test', mount_point) 132 mount_point.putChild('main.m3u8', playlist) 133 site = Site(root) 134 135 reactor.listenTCP(8080, site) 136 reactor.run()
137 138 if __name__ == "__main__": 139 test() 140