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 BaseNegotiator(object): """ Base functionality for negotiating an RTMP handshake. Call L{start} to begin negotiations. @ivar observer: An observer for handshake negotiations. @type observer: L{IHandshakeObserver} @ivar started: Whether negotiations have begun. @type started: C{bool} @ivar _buffer: Any data that has been received but not yet been consumed. @type _buffer: L{BufferedByteStream} """ started = False nearRequest = None nearResponse = None farRequest = None farResponse = None protocolVersion = 3 farProtocolVersion = None def __init__(self, observer, output): self.observer = observer self.output = output def start(self, uptime=0, version=0): """ Called to start the handshaking negotiations. """ if self.started: raise AlreadyStarted('Handshake negotiator cannot be restarted') self.started = True self.uptime = uptime self.version = version self._buffer = BufferedByteStream() def readPacket(self): if self._buffer.remaining() < HANDSHAKE_LENGTH: # we're expecting more data return packet = self._buffer.read(HANDSHAKE_LENGTH) self._buffer.consume() return packet def dataReceived(self, data): """ Called when handshake data has been received. """ if not self.started: raise HandshakeError('Data received, but negotiator not started') self._buffer.append(data) if self.farProtocolVersion is None: self.farProtocolVersion = self._buffer.read_uchar() packet = self.readPacket() if not packet: return if not self.farRequest: self.farRequest = self.buildFarRequest() self.farRequest.decode(packet) self.farRequestReceived(self.farRequest) packet = self.readPacket() if not packet: return if not self.farResponse: self.farResponse = self.buildFarResponse() self.farResponse.decode(packet) self.farResponseReceived(self.farResponse) def buildFarRequest(self): """ """ return RequestPacket() def buildFarResponse(self): """ """ return ResponsePacket() def buildNearRequest(self): """ """ p = RequestPacket() p.uptime = self.uptime p.version = self.version return p def buildNearResponse(self): """ """ return ResponsePacket() def farRequestReceived(self, request): """ Called when request packet has been received from the peer. """ raise NotImplementedError def farResponseReceived(self, response): """ Called when response packet has been received from the peer. """ raise NotImplementedError
class StateEngine(BaseStreamer): """ There are three stages to the protocol negotiations before RTMP message streaming can begin. Stage 1 - Version negotiation:: The first byte in any RTMP connection is the I{protocol version} byte. This allows endpoints to negotiate which version of the protocol to proceed with. Valid values appear to be:: - C{0x03}: plain old RTMP this is the baseline protocol that all implementations should default to. - C{0x06}: Used to signify an RTMPE connection. There is another (C{0x08}) but not documented at this time because how/why it is used is unclear. L{versionSuccess} must be called to move on to stage 2. Stage 2 - Handshake negotiations:: The next 1536 * 2 bytes is handshaking data. This part is delegated to a handshake negotiator, which is based on the protocol version. L{handshakeSuccess} must be called to move on to stage 3. Stage 3 - RTMP Message streaming:: Some docstring here. @ivar state: The state of the protocol. """ STATE_VERSION = 'version' STATE_HANDSHAKE = 'handshake' STATE_STREAM = 'stream' state = None protocolVersion = 3 def connectionMade(self): """ Called when this a connection has been made. """ self.state = self.STATE_VERSION self.startVersioning() def connectionLost(self, reason): """ Called when the connection has been lost. @param reason: The reason for the disconnection """ if self.state == self.STATE_VERSION: self.stopVersioning(reason) elif self.state == self.STATE_HANDSHAKE: self.stopHandshaking(reason) elif self.state == self.STATE_STREAM: self.stopStreaming(reason) def dataReceived(self, data): """ """ if self.state == self.STATE_VERSION: self.version_dataReceived(data) elif self.state == self.STATE_HANDSHAKE: self.handshake_dataReceived(data) elif self.state == self.STATE_STREAM: BaseStreamer.dataReceived(self, data) else: raise RuntimeError('Invalid state!') def startVersioning(self): """ Start protocol version negotiations. """ self.buffer = BufferedByteStream() def stopVersioning(self, reason=None): """ Stop protocol version negotiations. @param reason: A L{failure.Failure} object if protocol version negotiations failed. C{None} means success. """ del self.buffer def version_dataReceived(self, data): """ """ if not data: return self.buffer.append(data) self.peerProtocolVersion = self.buffer.read_uchar() self.versionReceived(self.peerProtocolVersion) def versionReceived(self, version): """ Called when the peers' protocol version has been received. The default behaviour is to accept any known version. It is the responsibility for any overriding subclass to call L{versionSuccess} for negotiations to proceed. """ if version == self.protocolVersion: self.versionSuccess() return raise UnknownProtocolVersion( 'Unhandled protocol version %d' % (version,)) def versionSuccess(self): """ Protocol version negotiations have been successful, now on to handshaking. """ try: data = self.buffer.read() except IOError: data = None self.stopVersioning() self.state = self.STATE_HANDSHAKE self.startHandshaking() if data: # any data that was left over from version negotiations is # artificially re-inserted back into the protocol because the # `state` has changed. self.dataReceived(data) def buildHandshakeNegotiator(self): """ """ raise NotImplementedError def startHandshaking(self): """ """ self.handshaker = self.buildHandshakeNegotiator() # TODO: apply uptime, version to the handshaker instead of 0, 0 self.handshaker.start(0, 0) def stopHandshaking(self, reason=None): """ """ del self.handshaker def handshake_dataReceived(self, data): """ """ self.handshaker.dataReceived(data) def handshakeSuccess(self, data): """ Handshaking was successful, streaming now commences. """ #data = self.handshaker.getRemainingData() self.stopHandshaking() self.state = self.STATE_STREAM self.startStreaming() if data: self.dataReceived(data) def startStreaming(self): """ Because Python is awesome we can short circuit checking state each time L{dataReceived} is called. """ self.dataReceived = lambda x: BaseStreamer.dataReceived(self, x) return BaseStreamer.startStreaming(self)
class BaseNegotiator(object): """ Base functionality for negotiating an RTMP handshake. @ivar observer: An observer for handshake negotiations. @type observer: L{IHandshakeObserver} @ivar buffer: Any data that has not yet been consumed. @type buffer: L{BufferedByteStream} @ivar started: Determines whether negotiations have already begun. @type started: C{bool} @ivar my_syn: The initial handshake packet that will be sent by this negotiator. @type my_syn: L{Packet} @ivar my_ack: The handshake packet that will be sent after the peer has sent its syn. @ivar peer_syn: The initial L{Packet} received from the peer. @ivar peer_ack: The L{Packet} received in acknowledgement of my syn. @ivar peer_version: The handshake version that the peer understands. """ implements(IHandshakeNegotiator) def __init__(self, observer, transport): self.observer = observer self.transport = transport self.started = False def start(self, uptime=None, version=None): """ Called to start the handshaking negotiations. """ if self.started: raise HandshakeError('Handshake negotiator cannot be restarted') self.started = True self.buffer = BufferedByteStream() self.peer_version = None self.my_syn = Packet(uptime, version) self.my_ack = None self.peer_syn = None self.peer_ack = None self.buildSynPayload(self.my_syn) self._writePacket(self.my_syn) def getPeerPacket(self): """ Attempts to decode a L{Packet} from the buffer. If there is not enough data in the buffer then C{None} is returned. """ if self.buffer.remaining() < HANDSHAKE_LENGTH: # we're expecting more data return packet = Packet() packet.decode(self.buffer) return packet def _writePacket(self, packet, stream=None): stream = stream or BufferedByteStream() packet.encode(stream) self.transport.write(stream.getvalue()) def dataReceived(self, data): """ Called when handshake data has been received. If an error occurs whilst negotiating the handshake then C{self.observer.handshakeFailure} will be called, citing the reason. 3 stages of data are received. The handshake version, the syn packet and then the ack packet. """ if not self.started: raise HandshakeError('Data was received, but negotiator was ' 'not started') self.buffer.append(data) self._process() def _process(self): if not self.peer_syn: self.peer_syn = self.getPeerPacket() if not self.peer_syn: return self.buffer.consume() self.synReceived() if not self.peer_ack: self.peer_ack = self.getPeerPacket() if not self.peer_ack: return self.buffer.consume() self.ackReceived() # if we get here then a successful handshake has been negotiated. # inform the observer accordingly self.observer.handshakeSuccess(self.buffer.getvalue()) def writeAck(self): """ Writes L{self.my_ack} to the observer. """ self._writePacket(self.my_ack) def buildSynPayload(self, packet): """ Called to build the syn packet, based on the state of the negotiations. """ raise NotImplementedError def buildAckPayload(self, packet): """ Called to build the ack packet, based on the state of the negotiations. """ raise NotImplementedError def synReceived(self): """ Called when the peers syn packet has been received. Use this function to do any validation/verification. """ def ackReceived(self): """
class StateEngine(BaseStreamer): """ There are three stages to the protocol negotiations before RTMP message streaming can begin. Stage 1 - Version negotiation:: The first byte in any RTMP connection is the I{protocol version} byte. This allows endpoints to negotiate which version of the protocol to proceed with. Valid values appear to be:: - C{0x03}: plain old RTMP this is the baseline protocol that all implementations should default to. - C{0x06}: Used to signify an RTMPE connection. There is another (C{0x08}) but not documented at this time because how/why it is used is unclear. L{versionSuccess} must be called to move on to stage 2. Stage 2 - Handshake negotiations:: The next 1536 * 2 bytes is handshaking data. This part is delegated to a handshake negotiator, which is based on the protocol version. L{handshakeSuccess} must be called to move on to stage 3. Stage 3 - RTMP Message streaming:: Some docstring here. @ivar state: The state of the protocol. """ STATE_VERSION = 'version' STATE_HANDSHAKE = 'handshake' STATE_STREAM = 'stream' state = None protocolVersion = 3 def connectionMade(self): """ Called when this a connection has been made. """ self.state = self.STATE_VERSION self.startVersioning() def connectionLost(self, reason): """ Called when the connection has been lost. @param reason: The reason for the disconnection """ if self.state == self.STATE_VERSION: self.stopVersioning(reason) elif self.state == self.STATE_HANDSHAKE: self.stopHandshaking(reason) elif self.state == self.STATE_STREAM: self.stopStreaming(reason) def dataReceived(self, data): """ """ if self.state == self.STATE_VERSION: self.version_dataReceived(data) elif self.state == self.STATE_HANDSHAKE: self.handshake_dataReceived(data) elif self.state == self.STATE_STREAM: BaseStreamer.dataReceived(self, data) else: raise RuntimeError('Invalid state!') def startVersioning(self): """ Start protocol version negotiations. """ self.buffer = BufferedByteStream() def stopVersioning(self, reason=None): """ Stop protocol version negotiations. @param reason: A L{failure.Failure} object if protocol version negotiations failed. C{None} means success. """ del self.buffer def version_dataReceived(self, data): """ """ if not data: return self.buffer.append(data) self.peerProtocolVersion = self.buffer.read_uchar() self.versionReceived(self.peerProtocolVersion) def versionReceived(self, version): """ Called when the peers' protocol version has been received. The default behaviour is to accept any known version. It is the responsibility for any overriding subclass to call L{versionSuccess} for negotiations to proceed. """ if version == self.protocolVersion: self.versionSuccess() return raise UnknownProtocolVersion('Unhandled protocol version %d' % (version, )) def versionSuccess(self): """ Protocol version negotiations have been successful, now on to handshaking. """ try: data = self.buffer.read() except IOError: data = None self.stopVersioning() self.state = self.STATE_HANDSHAKE self.startHandshaking() if data: # any data that was left over from version negotiations is # artificially re-inserted back into the protocol because the # `state` has changed. self.dataReceived(data) def buildHandshakeNegotiator(self): """ """ raise NotImplementedError def startHandshaking(self): """ """ self.handshaker = self.buildHandshakeNegotiator() # TODO: apply uptime, version to the handshaker instead of 0, 0 self.handshaker.start(0, 0) def stopHandshaking(self, reason=None): """ """ del self.handshaker def handshake_dataReceived(self, data): """ """ self.handshaker.dataReceived(data) def handshakeSuccess(self, data): """ Handshaking was successful, streaming now commences. """ #data = self.handshaker.getRemainingData() self.stopHandshaking() self.state = self.STATE_STREAM self.startStreaming() if data: self.dataReceived(data) def startStreaming(self): """ Because Python is awesome we can short circuit checking state each time L{dataReceived} is called. """ self.dataReceived = lambda x: BaseStreamer.dataReceived(self, x) return BaseStreamer.startStreaming(self)