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)
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)
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)
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()
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
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()
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