예제 #1
0
파일: codec.py 프로젝트: Flumotion/rtmpy
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))
예제 #2
0
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))
예제 #3
0
파일: __init__.py 프로젝트: njoyce/rtmpy
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
예제 #4
0
파일: __init__.py 프로젝트: Flumotion/rtmpy
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)
예제 #5
0
파일: handshake.py 프로젝트: Arlex/rtmpy
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):
        """
예제 #6
0
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)
예제 #7
0
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):
        """