class ProducingChannel(BaseChannel): """ Writes RTMP frames. @ivar buffer: Any data waiting to be written to the underlying stream. @type buffer: L{BufferedByteStream} @ivar acquired: Whether this channel is acquired. See L{ChannelMuxer. acquireChannel} """ def __init__(self, channelId, stream, frameSize): BaseChannel.__init__(self, channelId, stream, frameSize) self.buffer = BufferedByteStream() self.acquired = False self.callback = None def setCallback(self, cb): """ Sets the callback that will be fired once this channel has been completely encoded. """ self.callback = cb def reset(self): """ Called when the channel has completed writing the buffer. """ BaseChannel.reset(self) self.buffer.seek(0) self.buffer.truncate() self.header = None def append(self, data): """ Appends data to the buffer in preparation of encoding in RTMP. """ self.buffer.append(data) def marshallFrame(self, size): """ Writes a section of the buffer as part of the RTMP frame. """ self.stream.write(self.buffer.read(size))
class BaseStreamer(object): """ Provides all the base functionality for handling an RTMP input/output. @ivar decoder: RTMP Decoder that is fed data via L{dataReceived} """ implements(message.IMessageListener) dispatcher = MessageDispatcher @property def decoding(self): """ Whether this streamer is currently decoding RTMP message/s. If all the input buffer has been consumed, this will be C{False}. """ return getattr(self, 'decoding_task', None) is not None @property def encoding(self): """ Whether this streamer is currently encoding RTMP message/s. """ return getattr(self, 'encoding_task', None) is not None def getWriter(self): """ Returns a file like object that provides a I{write} method. This must be provided by subclasses. For example, for L{protocol.Protocol} instances this should return C{self.transport}. """ raise NotImplementedError def buildStreamManager(self): """ Returns an instance that provides L{interfaces.IStreamManager}. This must be provided by subclasses. """ raise NotImplementedError def getDispatcher(self): """ Returns an instance that will provide L{interfaces.IMessageDispatcher} """ return self.dispatcher(self) def bytesInterval(self, bytes): """ """ self.sendMessage(message.BytesRead(bytes), self.controlStream) def startStreaming(self): """ This must be called before any RTMP data is received. """ self.streamManager = self.buildStreamManager() self.controlStream = self.streamManager.getControlStream() self._decodingBuffer = BufferedByteStream() self._encodingBuffer = BufferedByteStream() self.decoder = codec.Decoder(self.getDispatcher(), self.streamManager, stream=self._decodingBuffer) self.encoder = codec.Encoder(self.getWriter(), stream=self._encodingBuffer) self.decoder_task = None self.encoder_task = None def stopStreaming(self, reason=None): """ """ self.streamManager.closeAllStreams() self._decodingBuffer.truncate() self._encodingBuffer.truncate() del self._decodingBuffer del self._encodingBuffer del self.decoder_task, self.decoder del self.encoder_task, self.encoder def dataReceived(self, data): """ Data has been received by the endpoint. """ self.decoder.send(data) if not self.decoding: self.startDecoding() def startDecoding(self): """ Called to start the decoding process. @return: A C{Deferred} which will kill the task once the decoding is done or on error. """ def cullTask(result): self.decoder_task = None return result self.decoder_task = task.coiterate(self.decoder) self.decoder_task.addBoth(cullTask) return self.decoder_task def startEncoding(self): """ Called to start asynchronously iterate the encoder. @return: A C{Deferred} which will kill the task once the encoder is done or on error will kill the connection. @todo: See _startDecoding todo. The same applies here. """ def cullTask(result): self.encoder_task = None return result self.encoder_task = task.coiterate(self.encoder) self.encoder_task.addBoth(cullTask) return self.encoder_task def sendMessage(self, msg, stream, whenDone=None): """ Sends an RTMP message to the peer. Not part of a public api, use C{stream.sendMessage} instead. @param msg: The message being sent to the peer. @type msg: L{message.IMessage} @param stream: The stream instance that is sending the message. @type stream: L{NetStream} @param whenDone: A callback fired when the message has been written to the RTMP stream. See L{BaseStream.sendMessage} """ buf = BufferedByteStream() e = self.encoder # this will probably need to be rethought as this could block for an # unacceptable amount of time. For most messages however it seems to be # fast enough and the penalty for setting up a new thread is too high. msg.encode(buf) e.send(buf.getvalue(), msg.__data_type__, stream.streamId, stream.timestamp, whenDone) if e.active and not self.encoder_task: self.startEncoding() def setFrameSize(self, size): self.sendMessage(message.FrameSize(size)) self.encoder.setFrameSize(size) def getStreamingChannel(self, stream): """ """ channel = self.encoder.acquireChannel() if channel is None: # todo: make this better raise RuntimeError('No streaming channel available') return codec.StreamingChannel(channel, stream.streamId, self.getWriter()) def onFrameSize(self, size, timestamp): """ Called when the peer sets its RTMP frame size. @param size: The new size of any RTMP frames sent from the peer. @param timestamp: Time this message was received. """ self.decoder.setFrameSize(size) def onDownstreamBandwidth(self, interval, timestamp): """ Called when the peer sends its RTMP bytes interval. @param interval: The number of bytes that must be received from the peer before sending an acknowledgement """ self.decoder.setBytesInterval(interval)
class BaseStreamer(object): """ Provides all the base functionality for handling an RTMP input/output. @ivar decoder: RTMP Decoder that is fed data via L{dataReceived} """ implements(message.IMessageListener) dispatcher = MessageDispatcher @property def decoding(self): """ Whether this streamer is currently decoding RTMP message/s. If all the input buffer has been consumed, this will be C{False}. """ return getattr(self, 'decoding_task', None) is not None @property def encoding(self): """ Whether this streamer is currently encoding RTMP message/s. """ return getattr(self, 'encoding_task', None) is not None def getWriter(self): """ Returns a file like object that provides a I{write} method. This must be provided by subclasses. For example, for L{protocol.Protocol} instances this should return C{self.transport}. """ raise NotImplementedError def buildStreamManager(self): """ Returns an instance that provides L{interfaces.IStreamManager}. This must be provided by subclasses. """ raise NotImplementedError def getDispatcher(self): """ Returns an instance that will provide L{interfaces.IMessageDispatcher} """ return self.dispatcher(self) def bytesInterval(self, bytes): """ """ self.sendMessage(message.BytesRead(bytes), self.controlStream) def startStreaming(self): """ This must be called before any RTMP data is received. """ self.streamManager = self.buildStreamManager() self.controlStream = self.streamManager.getControlStream() self._decodingBuffer = BufferedByteStream() self._encodingBuffer = BufferedByteStream() self.decoder = codec.Decoder(self.getDispatcher(), self.streamManager, stream=self._decodingBuffer) self.encoder = codec.Encoder(self.getWriter(), stream=self._encodingBuffer) self.decoder_task = None self.encoder_task = None def stopStreaming(self, reason=None): """ """ self.streamManager.closeAllStreams() self._decodingBuffer.truncate() self._encodingBuffer.truncate() del self._decodingBuffer del self._encodingBuffer del self.decoder_task, self.decoder del self.encoder_task, self.encoder def dataReceived(self, data): """ Data has been received by the endpoint. """ self.decoder.send(data) if not self.decoding: self.startDecoding() def startDecoding(self): """ Called to start the decoding process. @return: A C{Deferred} which will kill the task once the decoding is done or on error. """ def cullTask(result): self.decoder_task = None return result self.decoder_task = task.coiterate(self.decoder) self.decoder_task.addBoth(cullTask) return self.decoder_task def startEncoding(self): """ Called to start asynchronously iterate the encoder. @return: A C{Deferred} which will kill the task once the encoder is done or on error will kill the connection. @todo: See _startDecoding todo. The same applies here. """ def cullTask(result): self.encoder_task = None return result self.encoder_task = task.coiterate(self.encoder) self.encoder_task.addBoth(cullTask) return self.encoder_task def sendMessage(self, msg, stream, whenDone=None): """ Sends an RTMP message to the peer. Not part of a public api, use C{stream.sendMessage} instead. @param msg: The message being sent to the peer. @type msg: L{message.IMessage} @param stream: The stream instance that is sending the message. @type stream: L{NetStream} @param whenDone: A callback fired when the message has been written to the RTMP stream. See L{BaseStream.sendMessage} """ buf = BufferedByteStream() e = self.encoder # this will probably need to be rethought as this could block for an # unacceptable amount of time. For most messages however it seems to be # fast enough and the penalty for setting up a new thread is too high. msg.encode(buf) e.send(buf.getvalue(), msg.__data_type__, stream.streamId, stream.timestamp, whenDone) if e.active and not self.encoder_task: self.startEncoding() def setFrameSize(self, size): self.sendMessage(message.FrameSize(size), self.controlStream) self.encoder.setFrameSize(size) def getStreamingChannel(self, stream): """ """ return codec.StreamingChannel(self.encoder, stream.streamId, self.getWriter()) def onFrameSize(self, size, timestamp): """ Called when the peer sets its RTMP frame size. @param size: The new size of any RTMP frames sent from the peer. @param timestamp: Time this message was received. """ self.decoder.setFrameSize(size) def onAbort(self, channelId, timestamp): """ Called to abort a channel currently be decoded. """ self.decoder.abort(channelId) def onDownstreamBandwidth(self, interval, timestamp): """ Called when the peer sends its RTMP bytes interval. @param interval: The number of bytes that must be received from the peer before sending an acknowledgement """ self.decoder.setBytesInterval(interval)
class Codec(object): """ Generic channels and frame operations. @ivar stream: The underlying buffer containing the raw bytes. @type stream: L{BufferedByteStream} @ivar channels: A L{dict} of L{BaseChannel} objects that are handling data. @ivar frameSize: The maximum size for an individual frame. Read-only, use L{setFrameSize} instead. """ def __init__(self): self.buffer = BufferedByteStream() self.channels = {} self.frameSize = FRAME_SIZE self.bytes = 0 def setFrameSize(self, size): """ Set the size of the next frame to be handled. """ self.frameSize = size for channel in self.channels.values(): channel.setFrameSize(size) def buildChannel(self, channelId): """ Called to build a channel suitable for use with this codec. Must be implemented by subclasses. """ raise NotImplementedError def getChannel(self, channelId): """ Returns a channel based on channelId. If the channel doesn't exist, then one is created. @param channelId: Index for the channel to retrieve. @type channelId: C{int} @rtype: L{Channel} """ channel = self.channels.get(channelId, None) if channel is not None: return channel if channelId > MAX_CHANNELS: raise IndexError('Attempted to get channelId %d which is > %d' % ( channelId, MAX_CHANNELS)) channel = self.buildChannel(channelId) self.channels[channelId] = channel channel.reset() return channel def clear(self): """ Clears the underlying buffer. """ self.buffer.consume() self.buffer.truncate()