Package flumotion :: Package component :: Package consumers :: Package icystreamer :: Module icymux
[hide private]

Source Code for Module flumotion.component.consumers.icystreamer.icymux

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_ts_segmenter -*- 
  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   
 19  import gst 
 20  import gobject 
 21  import time 
 22   
 23   
24 -class IcyMux(gst.Element):
25 ''' 26 I mux the metadata with title changes into audio stream. 27 ''' 28 29 _DEFAULT_FRAMESIZE = 256 30 _DEFAULT_NUMFRAMES = 16 31 _MAX_INT = int(1 << 31 - 1) 32 33 34 __gproperties__ = { 35 'frame-size': (int, 36 'size of the frame in bytes', 37 'The size in bytes of the frame', 38 1, _MAX_INT, _DEFAULT_FRAMESIZE, 39 gobject.PARAM_READWRITE), 40 'num-frames': (int, 41 'number of frames per data block', 42 'The number of frames per data block', 43 1, _MAX_INT, _DEFAULT_NUMFRAMES, 44 gobject.PARAM_READWRITE), 45 'icy-metaint': (int, 46 'number of bytes per data block', 47 'The length of data block', 48 1, _MAX_INT, _DEFAULT_FRAMESIZE * _DEFAULT_NUMFRAMES, 49 gobject.PARAM_READABLE), 50 'iradio-title': (str, 51 'title of currently playing song', 52 'Title of currently playing song', 53 None, gobject.PARAM_READABLE), 54 'iradio-timestamp': (int, 55 'last title timestamp', 56 'Epoch time of last title change', 57 -_MAX_INT, _MAX_INT, -1, gobject.PARAM_READABLE)} 58 59 __gsignals__ = {"broadcast-title": (gobject.SIGNAL_RUN_LAST,\ 60 gobject.TYPE_NONE, [])} 61 62 __gstdetails__ = ('IcyMux', 'Codec/Muxer', 63 'Icy format muxer', 64 'Flumotion Dev Team') 65 66 _sinkpadtemplate = gst.PadTemplate("sink", 67 gst.PAD_SINK, 68 gst.PAD_ALWAYS, 69 gst.caps_from_string("audio/mpeg;application/ogg")) 70 71 _srcpadtemplate = gst.PadTemplate("src", 72 gst.PAD_SRC, 73 gst.PAD_ALWAYS, 74 gst.caps_from_string("application/x-icy, " +\ 75 "metadata-interval= (int)[0, MAX]")) 76
77 - def __init__(self):
78 gst.Element.__init__(self) 79 80 self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink") 81 self.sinkpad.set_chain_function(self.chainfunc) 82 self.add_pad(self.sinkpad) 83 self.sinkpad.add_event_probe(self._tag_event_cb) 84 85 self.srcpad = gst.Pad(self._srcpadtemplate, "src") 86 self.add_pad(self.srcpad) 87 88 self._frameSize = self._DEFAULT_FRAMESIZE 89 self._numFrames = self._DEFAULT_NUMFRAMES 90 self._recountMetaint() 91 92 self.connect('broadcast-title', self._broadcast_title_handler) 93 94 self._reset()
95
96 - def _reset(self):
97 self.adapter = gst.Adapter() 98 self._frameCount = 0 99 self._shouldOutputMetadata = False 100 self._lastTitle = None 101 self._lastTitleTimestamp = -1
102
103 - def _broadcast_title_handler(self, object):
104 self.debug("Will broadcast title.") 105 self._shouldOutputMetadata = True
106
107 - def _tag_event_cb(self, pad, event):
108 self.debug("Received event %r" % event) 109 if event.type == gst.EVENT_TAG: 110 struc = event.get_structure() 111 if 'title' in struc.keys(): 112 self._lastTitle = struc['title'] 113 self._lastTitleTimestamp = int(time.time()) 114 self.debug("Stored title: %r on timestamp %r" %\ 115 (self._lastTitle, self._lastTitleTimestamp)) 116 self._shouldOutputMetadata = True 117 return True
118
119 - def _recountMetaint(self):
120 self._icyMetaint = self._frameSize * self._numFrames 121 self.debug("Metaint recount: %d" % self._icyMetaint)
122
123 - def do_get_property(self, property):
124 if property.name == "frame-size": 125 return self._frameSize 126 elif property.name == "num-frames": 127 return self._numFrames 128 elif property.name == "icy-metaint": 129 return self._icyMetaint 130 elif property.name == "iradio-title": 131 return self._lastTitle 132 elif property.name == 'iradio-timestamp': 133 return self._lastTitleTimestamp 134 else: 135 raise AttributeError('unknown property %s' % property.name)
136
137 - def do_set_property(self, property, value):
138 if property.name == "frame-size": 139 self.debug("Setting frame-size to %s" % value) 140 self._frameSize = int(value) 141 self._recountMetaint() 142 elif property.name == "num-frames": 143 self.debug("Setting num-frames to %s" % value) 144 self._numFrames = int(value) 145 self._recountMetaint() 146 elif property.name == "icy-metaint": 147 raise AttributeError("readonly property %s" % property.name) 148 else: 149 raise AttributeError('unknown property %s' % property.name)
150
151 - def do_change_state(self, transition):
152 if transition == gst.STATE_CHANGE_PAUSED_TO_READY: 153 self._reset() 154 return gst.Element.do_change_state(self, transition)
155
156 - def chainfunc(self, pad, buffer):
157 self.adapter.push(buffer) 158 while self.adapter.available() >= self._frameSize: 159 frame = self.adapter.take_buffer(self._frameSize) 160 self._setCapsAndFlags(frame) 161 if self._frameCount == 0: 162 #mark as key frame 163 frame.flag_unset(gst.BUFFER_FLAG_DELTA_UNIT) 164 self.log('marked as keyframe') 165 166 self.srcpad.push(frame) 167 self.log('Pushed frame of size %d' % frame.size) 168 self._frameCount += 1 169 170 if self._frameCount == self._numFrames: 171 self.outputMetadata() 172 self._frameCount = 0 173 return gst.FLOW_OK
174
175 - def _getTitleForMetadata(self):
176 if self._shouldOutputMetadata: 177 self.info("Will output title: %r" % self._lastTitle) 178 self._shouldOutputMetadata = False 179 return self._lastTitle 180 else: 181 return None
182
183 - def outputMetadata(self):
184 buf = MetadataBuffer(title=self._getTitleForMetadata()) 185 self._setCapsAndFlags(buf) 186 self.srcpad.push(buf) 187 self.log('Pushed metadata')
188
189 - def _setCapsAndFlags(self, buf):
190 buf.set_caps(gst.caps_from_string("application/x-icy, " +\ 191 "metadata-interval=%d" % self._icyMetaint)) 192 buf.flag_set(gst.BUFFER_FLAG_DELTA_UNIT)
193 194 195 gst.element_register(IcyMux, "icymux") 196 197
198 -class MetadataBuffer(gst.Buffer):
199
200 - def __init__(self, title=None):
201 self.title = title 202 203 payload = "" 204 if title: 205 title = title.encode("utf-8", "replace") 206 payload = "StreamTitle='%s';" % title 207 if not (len(payload) % 16 == 0): 208 toAdd = 16 - (len(payload) % 16) 209 payload = payload + "\0" * toAdd 210 gst.Buffer.__init__(self, chr(len(payload) / 16) + payload)
211