예제 #1
0
    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()
예제 #2
0
파일: __init__.py 프로젝트: njoyce/rtmpy
    def sendMessage(self, msg, stream, whenDone=None):
        """
        Queues L{msg} for encoding into RTMP and being spit out to L{output}.

        This is a low level api and is not for public consumption. Use
        L{interfaces.IStream.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{interfaces.IStream}
        @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._encoding_task:
            self.startEncoding()
예제 #3
0
    def __init__(self, encoder, streamId, output):
        self.encoder = encoder

        self.channel = self.encoder.acquireChannel()

        if self.channel is None:
            # todo: make this better
            raise RuntimeError('No streaming channel available')


        self.type = None
        self.streamId = streamId
        self.output = output
        self.stream = BufferedByteStream()

        self._lastHeader = None
        self._oldStream = self.channel.stream
        self.channel.stream = self.stream

        h = header.Header(self.channel.channelId)

        # encode a continuation header for speed
        header.encode(self.stream, h, h)

        self._continuationHeader = self.stream.getvalue()
        self.stream.consume()
예제 #4
0
파일: __init__.py 프로젝트: Flumotion/rtmpy
    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()
예제 #5
0
    def test_assemble(self):
        for fixture in self.data:
            buf = BufferedByteStream()
            a = RTMPAssembler(128, buf)

            for packet in fixture['packets']:
                a.push_packet(packet)

            buf.seek(0, 0)

            self.failUnlessEqual(struct.pack("B" * len(fixture['data']), *fixture['data']), buf.read())
예제 #6
0
파일: assembly.py 프로젝트: longde123/fmspy
    def __init__(self, chunkSize):
        """
        Constructor.

        @param chunkSize: initial size of chunk
        @type chunkSize: C{int}
        """
        self.lastHeaders = {}
        self.pool = {}
        self.chunkSize = chunkSize
        self.buffer = BufferedByteStream()
예제 #7
0
파일: base.py 프로젝트: longde123/fmspy
    def connectionMade(self):
        """
        Successfully connected to peer.
        """
        self.input = RTMPDisassembler(constants.DEFAULT_CHUNK_SIZE)
        self.output = RTMPAssembler(constants.DEFAULT_CHUNK_SIZE,
                                    self.transport)

        self.state = self.State.HANDSHAKE_SEND
        self.handshakeTimeout = reactor.callLater(
            config.getint('RTMP', 'handshakeTimeout'), self._handshakeTimedout)
        self.handshakeBuf = BufferedByteStream()
        self._beginHandshake()
예제 #8
0
    def test_assemble_chunks(self):
        chunkSizes = (32, 64, 128, 256)
        header = RTMPHeader(object_id=2, timestamp=9504486, length=10, type=0x04, stream_id=0)

        for chunkSize in chunkSizes:
            for l in xrange(1, 258):
                data = ''.join([chr(random.randint(0, 255)) for x in xrange(l)])

                buf = BufferedByteStream()
                a = RTMPAssembler(chunkSize, buf)
                a.push_packet(DataPacket(header=header, data=data))

                buf.seek(0, 0)
                self.failUnlessEqual(''.join(self.gen_packet("\x02\x91\x06\xe6\x00\x00\x01\x04\x00\x00\x00\x00", ["\xc2"], data, l, chunkSize)), buf.read())
예제 #9
0
파일: __init__.py 프로젝트: jasimmk/rtmpy
    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
예제 #10
0
파일: util.py 프로젝트: njoyce/rtmpy
    class _BackRelay(protocol.ProcessProtocol):
        def __init__(self, deferred):
            self.deferred = deferred
            self.s = BufferedByteStream()

        def errReceived(self, text):
            self.deferred.errback(failure.Failure(IOError()))
            self.deferred = None
            self.transport.loseConnection()

        def outReceived(self, text):
            self.s.write(text)

        def processEnded(self, reason):
            if self.deferred is not None:
                result = self.s.getvalue()
                self.deferred.callback(result)
예제 #11
0
파일: util.py 프로젝트: vanzhiganov/rtmpy
    class _BackRelay(protocol.ProcessProtocol):
        def __init__(self, deferred):
            self.deferred = deferred
            self.s = BufferedByteStream()

        def errReceived(self, text):
            self.deferred.errback(failure.Failure(IOError()))
            self.deferred = None
            self.transport.loseConnection()

        def outReceived(self, text):
            self.s.write(text)

        def processEnded(self, reason):
            if self.deferred is not None:
                result = self.s.getvalue()
                self.deferred.callback(result)
예제 #12
0
 def test_read_short(self):
     for fixture in self.data:
         for l in xrange(len(fixture[0])-1):
             try:
                 RTMPHeader.read(BufferedByteStream(fixture[0][0:l]))
                 self.fail()
             except NeedBytes, (bytes,):
                 self.failUnlessEqual(len(fixture[0])-l if l != 0 else 1, bytes)
예제 #13
0
    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
예제 #14
0
파일: assembly.py 프로젝트: longde123/fmspy
    def disassemble(self):
        """
        Disassemble L{buffer} into packets.

        Returns first decoded packet or None, if no packet could
        be decoded at the moment.

        @return: decoded packet
        @rtype: L{Packet}
        """
        self.buffer.seek(0)

        while self.buffer.remaining() > 0:
            try:
                # try to parse header from stream
                header = RTMPHeader.read(self.buffer)
            except NeedBytes, (bytes, ):
                # not enough bytes, return what we've already parsed
                return None

            # fill header with extra data from previous headers received
            # with same object_id
            header.fill(self.lastHeaders.get(header.object_id, RTMPHeader()))

            # get buffer for data of this packet
            buf = self.pool.get(header.object_id, BufferedByteStream())

            # this chunk size is minimum of regular chunk size in this
            # disassembler and what we have left here
            thisChunk = min(header.length - len(buf), self.chunkSize)
            if self.buffer.remaining() < thisChunk:
                # we have not enough bytes to read this chunk of data
                return None

            # we got complete chunk
            buf.write(self.buffer.read(thisChunk))

            # store packet header for this object_id
            self.lastHeaders[header.object_id] = header

            # skip data left in input buffer
            self.buffer.consume()

            # this chunk completes full packet?
            if len(buf) < header.length:
                # no, store buffer for further chunks
                self.pool[header.object_id] = buf
            else:
                # parse packet from header and data
                buf.seek(0, 0)

                # delete stored data for this packet
                if header.object_id in self.pool:
                    del self.pool[header.object_id]

                return self._decode_packet(header, buf)
예제 #15
0
파일: packets.py 프로젝트: longde123/fmspy
    def write(self):
        """
        Encode packet into bytes.

        @return: representation of packet
        @rtype: C{str}
        """
        buf = BufferedByteStream()
        buf.write_ushort(self.event)

        for val in self.data:
            buf.write_ulong(val)

        self.header.length = len(buf)
        buf.seek(0, 0)
        return buf.read()
예제 #16
0
    def setUp(self):
        self.patch(codec, 'ChannelDemuxer', MockChannelDemuxer)

        self.dispatcher = DispatchTester(self)
        self.stream_factory = MockStreamFactory(self)
        self.decoder = codec.Decoder(self.dispatcher,
                                     self.stream_factory,
                                     stream=BufferedByteStream())

        self.expected_streams = None
        self.streams = {}
예제 #17
0
파일: assembly.py 프로젝트: smira/fmspy
    def __init__(self, chunkSize):
        """
        Constructor.

        @param chunkSize: initial size of chunk
        @type chunkSize: C{int}
        """
        self.lastHeaders = {}
        self.pool = {}
        self.chunkSize = chunkSize
        self.buffer = BufferedByteStream()
예제 #18
0
파일: base.py 프로젝트: Xkeeper/fmspy
    def connectionMade(self):
        """
        Successfully connected to peer.
        """
        self.input = RTMPDisassembler(constants.DEFAULT_CHUNK_SIZE)
        self.output = RTMPAssembler(constants.DEFAULT_CHUNK_SIZE, self.transport)

        self.state = self.State.HANDSHAKE_SEND
        self.handshakeTimeout = reactor.callLater(config.getint('RTMP', 'handshakeTimeout'), self._handshakeTimedout)
        self.handshakeBuf = BufferedByteStream()
        self._beginHandshake()
예제 #19
0
    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)
예제 #20
0
파일: __init__.py 프로젝트: njoyce/rtmpy
    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()
예제 #21
0
    def dispatchMessage(self, stream, datatype, timestamp, data):
        """
        Called when the RTMP decoder has read a complete RTMP message.

        @param stream: The L{Stream} to receive this message.
        @param datatype: The RTMP datatype for the message.
        @param timestamp: The absolute timestamp this message was received.
        @param data: The raw data for the message.
        """
        m = message.classByType(datatype)()

        m.decode(BufferedByteStream(data))
        m.dispatch(stream, timestamp)
예제 #22
0
파일: packets.py 프로젝트: smira/fmspy
    def write(self):
        """
        Encode packet into bytes.

        @return: representation of packet
        @rtype: C{str}
        """
        buf = BufferedByteStream()
        buf.write_ushort(self.event)

        for val in self.data:
            buf.write_ulong(val)

        self.header.length = len(buf)
        buf.seek(0, 0)
        return buf.read()
예제 #23
0
    def dispatchMessage(self, stream, datatype, timestamp, data):
        p = Packet(self.type,
                   streamId=stream.streamId,
                   datatype=datatype,
                   timestamp=timestamp)

        self.observer.messageStart(p)

        e = message.classByType(datatype)()

        e.decode(BufferedByteStream(data))

        e.dispatch(stream, timestamp)

        self.observer.messageComplete(p)
예제 #24
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))
예제 #25
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))
예제 #26
0
파일: codec.py 프로젝트: jasimmk/rtmpy
    def __init__(self, channel, streamId, output):
        self.type = None
        self.channel = channel
        self.streamId = streamId
        self.output = output
        self.stream = BufferedByteStream()

        self._lastHeader = None
        self._oldStream = channel.stream
        channel.stream = self.stream

        h = header.Header(channel.channelId)

        # encode a continuation header for speed
        header.encode(self.stream, h, h)

        self._continuationHeader = self.stream.getvalue()
        self.stream.consume()
예제 #27
0
class BytesReadTestCase(unittest.TestCase):
    """
    Test case for L{fmspy.rtmp.packets.BytesRead}.
    """

    data = [
        (
            {
                'header':
                RTMPHeader(object_id=2,
                           timestamp=0,
                           length=4,
                           type=0x03,
                           stream_id=0L),
                'buf':
                BufferedByteStream('\x00\x00\x00\x89'),
            },
            BytesRead(bytes=137,
                      header=RTMPHeader(object_id=2,
                                        timestamp=0,
                                        length=4,
                                        type=0x03,
                                        stream_id=0L)),
        ),
    ]

    def test_eq(self):
        self.failUnlessEqual(
            BytesRead(bytes=5, header=RTMPHeader(object_id=3)),
            BytesRead(bytes=5, header=RTMPHeader(object_id=3)))
        self.failIfEqual(BytesRead(bytes=5, header=RTMPHeader(object_id=4)),
                         BytesRead(bytes=5, header=RTMPHeader(object_id=3)))
        self.failIfEqual(BytesRead(bytes=6, header=RTMPHeader(object_id=3)),
                         BytesRead(bytes=5, header=RTMPHeader(object_id=3)))

    def test_read(self):
        for fixture in self.data:
            fixture[0]['buf'].seek(0)
            self.failUnlessEqual(fixture[1], BytesRead.read(**fixture[0]))

    def test_write(self):
        for fixture in self.data:
            fixture[0]['buf'].seek(0)
            self.failUnlessEqual(fixture[0]['buf'].read(), fixture[1].write())
예제 #28
0
    def pyamfEncode(self, obj, amf3=False, use_proxies=False):
        if amf3 is True:
            context = pyamf.get_context(pyamf.AMF3)
        else:
            context = pyamf.get_context(pyamf.AMF0)

        stream = BufferedByteStream()

        if amf3 is True:
            pyamf_encoder = pyamf.get_encoder(pyamf.AMF3,
                                              stream=stream,
                                              context=context)
        else:
            pyamf_encoder = pyamf.get_encoder(pyamf.AMF0,
                                              stream=stream,
                                              context=context)

        pyamf_encoder.writeElement(obj)
        return pyamf_encoder.stream.getvalue()
예제 #29
0
파일: handshake.py 프로젝트: Arlex/rtmpy
    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)
예제 #30
0
파일: base.py 프로젝트: Xkeeper/fmspy
class RTMPBaseProtocol(protocol.Protocol):
    """
    Basis RTMP protocol implementation.

    @ivar state: internal state of protocol
    @ivar input: input packet disassebmbler 
    @type input: L{RTMPDisassembler}
    @ivar output: output packet assembler
    @type output: L{RTMPAssembler}
    @ivar handshakeBuf: buffer, holding input data during handshake
    @type handshakeBuf: C{BufferedByteStream}
    """

    class State:
        CONNECTING = 'connecting'
        """
        Connection in progress
        """
        HANDSHAKE_SEND = 'handshake-send'
        """
        Handshake, 1st phase.
        """
        HANDSHAKE_VERIFY = 'handshake-verify'
        """
        Handshake, 2nd phase.
        """
        RUNNING = 'running'
        """
        Usual state of protocol: receiving-sending RTMP packets.
        """

    def __init__(self):
        """
        Constructor.
        """
        self.state = self.State.CONNECTING
        self.handshakeTimeout = None

    def connectionMade(self):
        """
        Successfully connected to peer.
        """
        self.input = RTMPDisassembler(constants.DEFAULT_CHUNK_SIZE)
        self.output = RTMPAssembler(constants.DEFAULT_CHUNK_SIZE, self.transport)

        self.state = self.State.HANDSHAKE_SEND
        self.handshakeTimeout = reactor.callLater(config.getint('RTMP', 'handshakeTimeout'), self._handshakeTimedout)
        self.handshakeBuf = BufferedByteStream()
        self._beginHandshake()

    def _beginHandshake(self):
        """
        Begin handshake procedures.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeSendReceived(self):
        """
        Data received in HANDSHAKE_SEND state.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeVerifyReceived(self):
        """
        Data received in HANDSHAKE_VERIFY state.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeComplete(self):
        """
        Handshake complete, clear timeouts.
        """
        if self.handshakeTimeout is not None:
            self.handshakeTimeout.cancel()
            self.handshakeTimeout = None
        self.state = self.State.RUNNING
        self._regularInput(self.handshakeBuf.read())
        del self.handshakeBuf

    def _handshakeTimedout(self):
        """
        Handshake not completed in timeout.
        """
        self.handshakeTimeout = None
        self.transport.loseConnection()

    def connectionLost(self, reason):
        """
        Connection with peer was lost for some reason.
        """
        if self.handshakeTimeout is not None:
            self.handshakeTimeout.cancel()
            self.handshakeTimeout = None

    def _regularInput(self, data):
        """
        Regular RTMP dataflow: stream of RTMP packets.

        Some bytes (L{data}) was received.

        @param data: bytes received
        @type data: C{str}
        """
        self.input.push_data(data)

        while True:
            packet = self.input.disassemble()
            if packet is None:
                return

            self._handlePacket(packet)

    def dataReceived(self, data):
        """
        Some data was received from peer.

        @param data: bytes received
        @type data: C{str}
        """
        if self.state == self.State.RUNNING:
            self._regularInput(data)
        else: # handshake
            self.handshakeBuf.seek(0, 2)
            self.handshakeBuf.write(data)
            if self.state == self.State.HANDSHAKE_SEND:
                self._handshakeSendReceived()
            elif self.state == self.State.HANDSHAKE_VERIFY:
                self._handshakeVerifyReceived()

    def _handlePacket(self, packet):
        """
        Dispatch received packet to some handler.

        @param packet: packet
        @type packet: L{Packet}
        """
        log.msg("<- %r" % packet)
        handler = 'handle' + packet.__class__.__name__
        try:
            getattr(self, handler)(packet)
        except AttributeError:
            log.msg("Unhandled packet: %r" % packet)

    def pushPacket(self, packet):
        """
        Push outgoing RTMP packet.

        @param packet: outgoing packet
        @type packet: L{Packet}.
        """
        log.msg("-> %r" % packet)
        self.output.push_packet(packet)
예제 #31
0
파일: __init__.py 프로젝트: Flumotion/rtmpy
 def startVersioning(self):
     """
     Start protocol version negotiations.
     """
     self.buffer = BufferedByteStream()
예제 #32
0
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)
예제 #33
0
파일: __init__.py 프로젝트: njoyce/rtmpy
 def __init__(self):
     self.buffer = BufferedByteStream()
예제 #34
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):
        """
예제 #35
0
class InvokeTestCase(unittest.TestCase):
    """
    Test case for L{fmspy.rtmp.packets.Invoke}.
    """

    data = [
        ({
            'header':
            RTMPHeader(object_id=3,
                       timestamp=0,
                       length=235,
                       type=0x14,
                       stream_id=0L),
            'buf':
            BufferedByteStream(
                '\x02\x00\x07connect\x00?\xf0\x00\x00\x00\x00\x00\x00\x03\x00\x03app\x02\x00\x04echo\x00\x08flashVer\x02\x00\rLNX 10,0,20,7\x00\x06swfUrl\x06\x00\x05tcUrl\x02\x00\x15rtmp://localhost/echo\x00\x04fpad\x01\x00\x00\x0ccapabilities\x00@.\x00\x00\x00\x00\x00\x00\x00\x0baudioCodecs\x00@\xa8\xee\x00\x00\x00\x00\x00\x00\x0bvideoCodecs\x00@o\x80\x00\x00\x00\x00\x00\x00\rvideoFunction\x00?\xf0\x00\x00\x00\x00\x00\x00\x00\x07pageUrl\x06\x00\x0eobjectEncoding\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t'
            ),
        },
         Invoke(name=u'connect',
                argv=({
                    'videoCodecs': 252,
                    'audioCodecs': 3191,
                    'flashVer': u'LNX 10,0,20,7',
                    'app': u'echo',
                    'tcUrl': u'rtmp://localhost/echo',
                    'videoFunction': 1,
                    'capabilities': 15,
                    'pageUrl': pyamf.Undefined,
                    'fpad': False,
                    'swfUrl': pyamf.Undefined,
                    'objectEncoding': 0
                }, ),
                id=1,
                header=RTMPHeader(object_id=3,
                                  timestamp=0,
                                  length=235,
                                  type=0x14,
                                  stream_id=0L)), False),
        ({
            'header':
            RTMPHeader(object_id=3,
                       timestamp=0,
                       length=0,
                       type=0x14,
                       stream_id=0L),
            'buf':
            BufferedByteStream(
                '\x02\x00\x07destroy\x00@@\x80\x00\x00\x00\x00\x00\x03\x00\x0bvideoCodecs\x00@o\x80\x00\x00\x00\x00\x00\x00\x00\t'
            ),
        },
         Invoke(name=u'destroy',
                argv=({
                    'videoCodecs': 252
                }, ),
                id=33,
                header=RTMPHeader(object_id=3,
                                  timestamp=0,
                                  length=0,
                                  type=0x14,
                                  stream_id=0L)), True),
    ]

    def test_eq(self):
        self.failUnlessEqual(
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)),
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)))
        self.failIfEqual(
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)),
            Invoke(name='b', argv=(), id=35.0, header=RTMPHeader(object_id=3)))
        self.failIfEqual(
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)),
            Invoke(name='a',
                   argv=('a'),
                   id=35.0,
                   header=RTMPHeader(object_id=3)))
        self.failIfEqual(
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)),
            Invoke(name='a', argv=(), id=36.0, header=RTMPHeader(object_id=3)))
        self.failIfEqual(
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=3)),
            Invoke(name='a', argv=(), id=35.0, header=RTMPHeader(object_id=4)))

    def test_repr(self):
        self.failUnlessEqual(
            "<Invoke(name=u'destroy', argv=({'videoCodecs': 252},), id=33, header=<RTMPHeader(object_id=3, timestamp=0, length=0, type=0x14, stream_id=0L)>)>",
            repr(
                Invoke(name=u'destroy',
                       argv=({
                           'videoCodecs': 252
                       }, ),
                       id=33,
                       header=RTMPHeader(object_id=3,
                                         timestamp=0,
                                         length=0,
                                         type=0x14,
                                         stream_id=0L))))

    def test_read(self):
        for fixture in self.data:
            fixture[0]['buf'].seek(0)
            self.failUnlessEqual(fixture[1], Invoke.read(**fixture[0]))

    def test_write(self):
        for fixture in self.data:
            if not fixture[2]:
                continue
            fixture[0]['buf'].seek(0)
            self.failUnlessEqual(fixture[0]['buf'].read(), fixture[1].write())
예제 #36
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)
예제 #37
0
파일: codec.py 프로젝트: jasimmk/rtmpy
    def __init__(self, channelId, stream, frameSize):
        BaseChannel.__init__(self, channelId, stream, frameSize)

        self.buffer = BufferedByteStream()
        self.acquired = False
예제 #38
0
class StreamingChannel(object):
    """
    """


    def __init__(self, encoder, streamId, output):
        self.encoder = encoder

        self.channel = self.encoder.acquireChannel()

        if self.channel is None:
            # todo: make this better
            raise RuntimeError('No streaming channel available')


        self.type = None
        self.streamId = streamId
        self.output = output
        self.stream = BufferedByteStream()

        self._lastHeader = None
        self._oldStream = self.channel.stream
        self.channel.stream = self.stream

        h = header.Header(self.channel.channelId)

        # encode a continuation header for speed
        header.encode(self.stream, h, h)

        self._continuationHeader = self.stream.getvalue()
        self.stream.consume()


    def __del__(self):
        try:
            self.channel.stream = self._oldStream
        except:
            pass


    def setType(self, type):
        self.type = type


    def sendData(self, data, timestamp):
        c = self.channel

        if timestamp < c.timestamp:
            relTimestamp = timestamp
        else:
            relTimestamp = timestamp - c.timestamp

        h = header.Header(c.channelId, relTimestamp, self.type, len(data), self.streamId)

        if self._lastHeader is None:
            h.full = True

        c.setHeader(h)
        c.append(data)

        header.encode(self.stream, h, self._lastHeader)
        self._lastHeader = h

        c.marshallOneFrame()

        while not c.complete():
            self.stream.write(self._continuationHeader)
            c.marshallOneFrame()

        c.reset()
        s = self.stream.getvalue()
        self.output.write(s)
        self.encoder.bytes += len(s)

        self.stream.consume()
예제 #39
0
 def test_read(self):
     for fixture in self.data:
         self.failUnlessEqual(fixture[1], RTMPHeader.read(BufferedByteStream(fixture[0])))
예제 #40
0
파일: header.py 프로젝트: smira/fmspy
    def write(self, previous=None):
        """
        Write (encoder) header to byte string.

        @param previous: previous header (used to compress header)
        @type previous: L{RTMPHeader}
        @return: encoded header
        @rtype: C{str}
        """
        if previous is None:
            diff = 3
        else:
            diff = self.diff(previous)

        first = self.object_id & 0x3F | ((diff ^ 3) << 6)

        if diff == 0:
            return chr(first)

        buf = BufferedByteStream()
        buf.write_uchar(first)

        buf.write_24bit_uint(self.timestamp)

        if diff > 1:
            buf.write_24bit_uint(self.length)
            buf.write_uchar(self.type)

            if diff > 2:
                buf.write_ulong(self.stream_id)

        buf.seek(0, 0)
        return buf.read()
예제 #41
0
파일: assembly.py 프로젝트: smira/fmspy
class RTMPDisassembler(object):
    """
    Disassembling bytestream into RTMP packets.

    RTMP stream slices packets into chunks of L{chunkSize}. This class
    processes incoming stream of RTMP protocol data (after initial handshake)
    and decodes RTMP packets.

    Communication goes independently for each object_id. Last received
    headers are stored for each object_id in L{lastHeaders}. L{pool} holds
    incomplete packet contents also for each object_id.

    @ivar lastHeaders: last received header for object_id
    @type lastHeaders: C{dict}, object_id -> L{RTMPHeader}
    @ivar pool: incomplete packet data for object_id
    @type pool: C{dict}, object_id -> L{BufferedByteStream}
    @ivar chunkSize: size of chunk for this stream
    @type chunkSize: C{int}
    @ivar buffer: incoming buffer with data received from protocol
    @type buffer: L{BufferedByteStream}
    """

    def __init__(self, chunkSize):
        """
        Constructor.

        @param chunkSize: initial size of chunk
        @type chunkSize: C{int}
        """
        self.lastHeaders = {}
        self.pool = {}
        self.chunkSize = chunkSize
        self.buffer = BufferedByteStream()

    def push_data(self, data):
        """
        Push more incoming data.

        @param data: data received
        @type data: C{str}
        """
        self.buffer.seek(0, 2)
        self.buffer.write(data)

        return self

    def disassemble(self):
        """
        Disassemble L{buffer} into packets.

        Returns first decoded packet or None, if no packet could
        be decoded at the moment.

        @return: decoded packet
        @rtype: L{Packet}
        """
        self.buffer.seek(0)

        while self.buffer.remaining() > 0:
            try:
                # try to parse header from stream
                header = RTMPHeader.read(self.buffer)
            except NeedBytes, (bytes,):
                # not enough bytes, return what we've already parsed
                return None

            # fill header with extra data from previous headers received
            # with same object_id
            header.fill(self.lastHeaders.get(header.object_id, RTMPHeader()))

            # get buffer for data of this packet
            buf = self.pool.get(header.object_id, BufferedByteStream())

            # this chunk size is minimum of regular chunk size in this
            # disassembler and what we have left here
            thisChunk = min(header.length - len(buf), self.chunkSize)
            if self.buffer.remaining() < thisChunk:
                # we have not enough bytes to read this chunk of data
                return None

            # we got complete chunk
            buf.write(self.buffer.read(thisChunk))

            # store packet header for this object_id
            self.lastHeaders[header.object_id] = header

            # skip data left in input buffer
            self.buffer.consume()

            # this chunk completes full packet?
            if len(buf) < header.length:
                # no, store buffer for further chunks
                self.pool[header.object_id] = buf
            else:
                # parse packet from header and data
                buf.seek(0, 0)

                # delete stored data for this packet
                if header.object_id in self.pool:
                    del self.pool[header.object_id]

                return self._decode_packet(header, buf)

        return None
예제 #42
0
파일: header.py 프로젝트: longde123/fmspy
    def write(self, previous=None):
        """
        Write (encoder) header to byte string.

        @param previous: previous header (used to compress header)
        @type previous: L{RTMPHeader}
        @return: encoded header
        @rtype: C{str}
        """
        if previous is None:
            diff = 3
        else:
            diff = self.diff(previous)

        first = self.object_id & 0x3f | ((diff ^ 3) << 6)

        if diff == 0:
            return chr(first)

        buf = BufferedByteStream()
        buf.write_uchar(first)

        buf.write_24bit_uint(self.timestamp)

        if diff > 1:
            buf.write_24bit_uint(self.length)
            buf.write_uchar(self.type)

            if diff > 2:
                buf.write_ulong(self.stream_id)

        buf.seek(0, 0)
        return buf.read()
예제 #43
0
    def __init__(self, channelId, stream, frameSize):
        BaseChannel.__init__(self, channelId, stream, frameSize)

        self.buffer = BufferedByteStream()
        self.acquired = False
        self.callback = None
예제 #44
0
파일: base.py 프로젝트: longde123/fmspy
class RTMPBaseProtocol(protocol.Protocol):
    """
    Basis RTMP protocol implementation.

    @ivar state: internal state of protocol
    @ivar input: input packet disassebmbler 
    @type input: L{RTMPDisassembler}
    @ivar output: output packet assembler
    @type output: L{RTMPAssembler}
    @ivar handshakeBuf: buffer, holding input data during handshake
    @type handshakeBuf: C{BufferedByteStream}
    """
    class State:
        CONNECTING = 'connecting'
        """
        Connection in progress
        """
        HANDSHAKE_SEND = 'handshake-send'
        """
        Handshake, 1st phase.
        """
        HANDSHAKE_VERIFY = 'handshake-verify'
        """
        Handshake, 2nd phase.
        """
        RUNNING = 'running'
        """
        Usual state of protocol: receiving-sending RTMP packets.
        """

    def __init__(self):
        """
        Constructor.
        """
        self.state = self.State.CONNECTING
        self.handshakeTimeout = None

    def connectionMade(self):
        """
        Successfully connected to peer.
        """
        self.input = RTMPDisassembler(constants.DEFAULT_CHUNK_SIZE)
        self.output = RTMPAssembler(constants.DEFAULT_CHUNK_SIZE,
                                    self.transport)

        self.state = self.State.HANDSHAKE_SEND
        self.handshakeTimeout = reactor.callLater(
            config.getint('RTMP', 'handshakeTimeout'), self._handshakeTimedout)
        self.handshakeBuf = BufferedByteStream()
        self._beginHandshake()

    def _beginHandshake(self):
        """
        Begin handshake procedures.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeSendReceived(self):
        """
        Data received in HANDSHAKE_SEND state.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeVerifyReceived(self):
        """
        Data received in HANDSHAKE_VERIFY state.

        Implemented in descendants.
        """
        raise NotImplementedError

    def _handshakeComplete(self):
        """
        Handshake complete, clear timeouts.
        """
        if self.handshakeTimeout is not None:
            self.handshakeTimeout.cancel()
            self.handshakeTimeout = None
        self.state = self.State.RUNNING
        self._regularInput(self.handshakeBuf.read())
        del self.handshakeBuf

    def _handshakeTimedout(self):
        """
        Handshake not completed in timeout.
        """
        self.handshakeTimeout = None
        self.transport.loseConnection()

    def connectionLost(self, reason):
        """
        Connection with peer was lost for some reason.
        """
        if self.handshakeTimeout is not None:
            self.handshakeTimeout.cancel()
            self.handshakeTimeout = None

    def _regularInput(self, data):
        """
        Regular RTMP dataflow: stream of RTMP packets.

        Some bytes (L{data}) was received.

        @param data: bytes received
        @type data: C{str}
        """
        self.input.push_data(data)

        while True:
            packet = self.input.disassemble()
            if packet is None:
                return

            self._handlePacket(packet)

    def dataReceived(self, data):
        """
        Some data was received from peer.

        @param data: bytes received
        @type data: C{str}
        """
        if self.state == self.State.RUNNING:
            self._regularInput(data)
        else:  # handshake
            self.handshakeBuf.seek(0, 2)
            self.handshakeBuf.write(data)
            if self.state == self.State.HANDSHAKE_SEND:
                self._handshakeSendReceived()
            elif self.state == self.State.HANDSHAKE_VERIFY:
                self._handshakeVerifyReceived()

    def _handlePacket(self, packet):
        """
        Dispatch received packet to some handler.

        @param packet: packet
        @type packet: L{Packet}
        """
        log.msg("<- %r" % packet)
        handler = 'handle' + packet.__class__.__name__
        try:
            getattr(self, handler)(packet)
        except AttributeError:
            log.msg("Unhandled packet: %r" % packet)

    def pushPacket(self, packet):
        """
        Push outgoing RTMP packet.

        @param packet: outgoing packet
        @type packet: L{Packet}.
        """
        log.msg("-> %r" % packet)
        self.output.push_packet(packet)
예제 #45
0
파일: util.py 프로젝트: njoyce/rtmpy
 def __init__(self, deferred):
     self.deferred = deferred
     self.s = BufferedByteStream()
예제 #46
0
 def setUp(self):
     self.buffer = BufferedByteStream()
     self.listener = MockMessageListener()
예제 #47
0
파일: codec.py 프로젝트: njoyce/rtmpy
    def __init__(self):
        self.buffer = BufferedByteStream()

        self.channels = {}
        self.frameSize = FRAME_SIZE
        self.bytes = 0
예제 #48
0
파일: codec.py 프로젝트: jasimmk/rtmpy
class StreamingChannel(object):
    """
    """


    def __init__(self, channel, streamId, output):
        self.type = None
        self.channel = channel
        self.streamId = streamId
        self.output = output
        self.stream = BufferedByteStream()

        self._lastHeader = None
        self._oldStream = channel.stream
        channel.stream = self.stream

        h = header.Header(channel.channelId)

        # encode a continuation header for speed
        header.encode(self.stream, h, h)

        self._continuationHeader = self.stream.getvalue()
        self.stream.consume()


    def __del__(self):
        try:
            self.channel.stream = self._oldStream
        except:
            pass


    def setType(self, type):
        self.type = type


    def sendData(self, data, timestamp):
        c = self.channel

        if timestamp < c.timestamp:
            relTimestamp = timestamp
        else:
            relTimestamp = timestamp - c.timestamp

        h = header.Header(c.channelId, relTimestamp, self.type, len(data), self.streamId)

        if self._lastHeader is None:
            h.full = True

        c.setHeader(h)
        c.append(data)

        header.encode(self.stream, h, self._lastHeader)
        self._lastHeader = h

        c.marshallOneFrame()

        while not c.complete():
            self.stream.write(self._continuationHeader)
            c.marshallOneFrame()

        c.reset()
        self.output.write(self.stream.getvalue())
        self.stream.consume()
예제 #49
0
 def setUp(self):
     self.output = BufferedByteStream()
     self.encoder = codec.Encoder(self.output)
예제 #50
0
 def loads(data):
     """returns a list of messages"""
     stream = BufferedByteStream(data)
     result = list(pyamf.decode(stream=stream, encoding=3))
     stream.close()
     return result
예제 #51
0
파일: __init__.py 프로젝트: Flumotion/rtmpy
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)
예제 #52
0
파일: codec.py 프로젝트: njoyce/rtmpy
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()
예제 #53
0
    def __init__(self, stream=None):
        self.stream = stream or BufferedByteStream()

        self.channels = {}
        self.frameSize = FRAME_SIZE
        self.bytes = 0
예제 #54
0
파일: __init__.py 프로젝트: njoyce/rtmpy
class Packet(object):
    """
    """


    format = None
    challengeKey = None


    def __init__(self):
        self.buffer = BufferedByteStream()


    def computeOffset(self, start, modulus, increment):
        """
        An offset is 4 consecutive bytes encoded at C{start}.

        s = sum of bytes

        offset = (s % modulus) + increment
        """
        self.buffer.seek(start)

        offset = (
            self.buffer.read_uchar() +
            self.buffer.read_uchar() +
            self.buffer.read_uchar() +
            self.buffer.read_uchar())

        offset %= modulus
        offset += increment

        return offset


    def getDigestAndPayload(self, offset, length):
        """
        Returns the C{digest} and C{payload} components.
        """
        self.buffer.seek(0)

        payload = self.buffer.read(offset)
        digest = self.buffer.read(length)
        payload += self.buffer.read()

        return digest, payload


    def setFormat(self, format):
        self.format = format


    def setChallengeKey(self, key):
        self.challengeKey = key


    def setChallengeDigest(self, digest):
        self.challengeDigest = digest


    def decode(self, data):
        """
        Decodes the data bytes into this packet.
        """
        raise NotImplementedError
예제 #55
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)
예제 #56
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
예제 #57
0
 def startVersioning(self):
     """
     Start protocol version negotiations.
     """
     self.buffer = BufferedByteStream()
예제 #58
0
파일: util.py 프로젝트: sasqwatch/deblaze
 def setUp(self):
     self.context = pyamf.get_context(self.amf_version)
     self.stream = BufferedByteStream()
예제 #59
0
파일: util.py 프로젝트: vanzhiganov/rtmpy
 def __init__(self, deferred):
     self.deferred = deferred
     self.s = BufferedByteStream()