Package flumotion :: Package component :: Package consumers :: Package hlsstreamer :: Module hlsstreamer
[hide private]

Source Code for Module flumotion.component.consumers.hlsstreamer.hlsstreamer

  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  import gst 
 19  import urlparse 
 20   
 21  from twisted.internet import reactor 
 22   
 23  from flumotion.common import gstreamer 
 24  from flumotion.common.i18n import gettexter 
 25  from flumotion.component.base import http 
 26  from flumotion.component.component import moods 
 27  from flumotion.component.common.streamer.fragmentedstreamer import\ 
 28          FragmentedStreamer, Stats 
 29  from flumotion.component.consumers.hlsstreamer.resources import \ 
 30          HTTPLiveStreamingResource 
 31  from flumotion.component.consumers.hlsstreamer.hlsring import HLSRing 
 32  from flumotion.component.consumers.hlsstreamer import hlssink 
 33   
 34  __all__ = ['HLSStreamer'] 
 35  __version__ = "" 
 36  T_ = gettexter() 
 37   
 38   
39 -class HLSStreamer(FragmentedStreamer, Stats):
40 DEFAULT_SESSION_TIMEOUT = 30 41 DEFAULT_FRAGMENT_PREFIX = 'fragment' 42 DEFAULT_MAIN_PLAYLIST = 'main.m3u8' 43 DEFAULT_STREAM_PLAYLIST = 'stream.m3u8' 44 DEFAULT_STREAM_BITRATE = 300000 45 DEFAULT_KEYFRAMES_PER_SEGMENT = 10 46 47 logCategory = 'hls-streamer' 48
49 - def init(self):
50 self.debug("HTTP live streamer initialising") 51 self.hlsring = None
52
53 - def get_mime(self):
54 return 'video/webm'
55
56 - def get_pipeline_string(self, properties):
57 # Check of the hlssink is available or use the python one 58 if not gstreamer.element_factory_exists('hlssink'): 59 hlssink.register() 60 return "hlssink name=sink sync=false"
61
63 self.httpauth = http.HTTPAuthentication(self) 64 self.resource = HTTPLiveStreamingResource(self, self.httpauth, 65 self.secret_key, self.session_timeout)
66
67 - def getRing(self):
68 return self.hlsring
69
70 - def configure_pipeline(self, pipeline, props):
71 self.hlsring = HLSRing( 72 props.get('main-playlist', self.DEFAULT_MAIN_PLAYLIST), 73 props.get('stream-playlist', self.DEFAULT_STREAM_PLAYLIST), 74 props.get('stream-bitrate', self.DEFAULT_STREAM_BITRATE), 75 self.description, 76 props.get('fragment-prefix', self.DEFAULT_FRAGMENT_PREFIX), 77 props.get('new-fragment-tolerance', 0), 78 props.get('max-window', self.DEFAULT_MAX_WINDOW), 79 props.get('max-extra-buffers', None), 80 props.get('key-rotation', 0), 81 props.get('keys-uri', None)) 82 83 # Call the base class after initializing the ring and getting 84 # the secret key and the session timeout 85 FragmentedStreamer.configure_pipeline(self, pipeline, props) 86 87 self.hls_url = props.get('hls-url', None) 88 if self.hls_url: 89 if not self.hls_url.endswith('/'): 90 self.hls_url += '/' 91 if self.mountPoint.startswith('/'): 92 mp = self.mountPoint[1:] 93 else: 94 mp = self.mountPoint 95 self.hls_url = urlparse.urljoin(self.hls_url, mp) 96 else: 97 self.hls_url = self.getUrl() 98 99 self.hlsring.setHostname(self.hls_url) 100 self.soft_restart()
101
102 - def soft_restart(self):
103 """Stops serving fragments, resets the playlist and starts 104 waiting for new segments to become happy again 105 """ 106 self.info("Soft restart, resetting playlist and waiting to fill " 107 "the initial fragments window") 108 self._ready = False 109 self._fragmentsCount = 0 110 self._last_index = 0 111 self.hlsring.reset()
112
113 - def _configure_sink(self):
114 self.sink.set_property('write-to-disk', False) 115 self.sink.set_property('playlist-max-window', 5)
116
117 - def _connect_sink_signals(self):
118 FragmentedStreamer._connect_sink_signals(self) 119 self.sink.connect("new-fragment", self._new_fragment)
120
121 - def _process_fragment(self, fragment):
122 self._fragmentsCount = self._fragmentsCount + 1 123 124 # Wait hls-min-window fragments to set the component 'happy' 125 if self._fragmentsCount == self._minWindow: 126 self.info("%d fragments received. Changing mood to 'happy'", 127 self._fragmentsCount) 128 self.setMood(moods.happy) 129 self._ready = True 130 131 b = fragment.get_property('buffer') 132 index = fragment.get_property('index') 133 duration = fragment.get_property('duration') 134 135 if index < self._last_index: 136 self.warning("Found a discontinuity last index is %s but current " 137 "one is %s", self._last_index, index) 138 self.soft_restart() 139 140 fragName = self.hlsring.addFragment(b.data, index, 141 round(duration / float(gst.SECOND))) 142 self.info('Added fragment "%s", index=%s, duration=%s', 143 fragName, index, gst.TIME_ARGS(duration))
144 145 ### START OF THREAD-AWARE CODE (called from non-reactor threads) 146
147 - def _new_fragment(self, hlssink):
148 self.log("hlsink created a new fragment") 149 try: 150 fragment = hlssink.get_property('fragment') 151 except: 152 fragment = hlssink.emit('pull-fragment') 153 reactor.callFromThread(self._process_fragment, fragment)
154 155 ### END OF THREAD-AWARE CODE 156