def test_decode_amf0(self): stream = util.BufferedByteStream( '\x11\n\x07Cflex.messaging.io.ArrayCollection\t\x03\x01\x06\teggs') decoder = amf0.Decoder(stream) x = decoder.readElement() self.assertEqual(x.__class__, flex.ArrayCollection) self.assertEqual(x, ['eggs'])
def next(self): """ Read one RTMP message from the stream and return it. """ if self.stream.at_eof(): raise StopIteration # Read the message into body_stream. The message may span a number of # chunks (each one with its own header). message_body = [] msg_body_len = 0 _header = header.decode(self.stream) log.debug('header %s' % _header) # FIXME: this should really be implemented inside header_decode if _header.data_type == rtmp_type.DT_NONE: _header = self.prv_header self.prv_header = _header while True: # NOTE: this whole loop section needs to be looked at. read_bytes = min(_header.body_length - msg_body_len, self.chunk_size) message_body.append(self.stream.read(read_bytes)) msg_body_len += read_bytes if msg_body_len >= _header.body_length: break next_header = header.decode(self.stream) # WORKAROUND: even though the RTMP specification states that the # extended timestamp field DOES NOT follow type 3 chunks, it seems # that Flash player 10.1.85.3 and Flash Media Server 3.0.2.217 send # and expect this field here. if _header.timestamp >= 0x00ffffff: self.stream.read_ulong() assert next_header.stream_id == -1, (_header, next_header) assert next_header.data_type == -1, (_header, next_header) assert next_header.timestamp == -1, (_header, next_header) assert next_header.body_length == -1, (_header, next_header) assert _header.body_length == msg_body_len, (_header, msg_body_len) body_stream = pyamf.util.BufferedByteStream(''.join(message_body)) # Decode the message based on the datatype present in the header ret = {'msg': _header.data_type} if ret['msg'] == rtmp_type.DT_NONE: log.warning('WARNING: message with datatype None received: %s' % _header) return self.next() elif ret['msg'] == rtmp_type.DT_USER_CONTROL: ret['event_type'] = body_stream.read_ushort() ret['event_data'] = body_stream.read() elif ret['msg'] == rtmp_type.DT_WINDOW_ACK_SIZE: ret['window_ack_size'] = body_stream.read_ulong() elif ret['msg'] == rtmp_type.DT_SET_PEER_BANDWIDTH: ret['window_ack_size'] = body_stream.read_ulong() ret['limit_type'] = body_stream.read_uchar() elif ret['msg'] == rtmp_type.DT_SHARED_OBJECT: decoder = amf0.Decoder(body_stream) obj_name = decoder.readString() curr_version = body_stream.read_ulong() flags = body_stream.read(8) # A shared object message may contain a number of events. events = [] while not body_stream.at_eof(): event = self.read_shared_object_event(body_stream, decoder) events.append(event) ret['obj_name'] = obj_name ret['curr_version'] = curr_version ret['flags'] = flags ret['events'] = events elif ret['msg'] == rtmp_type.DT_AMF3_SHARED_OBJECT: decoder = amf3.Decoder(body_stream) obj_name = decoder.readString() curr_version = body_stream.read_ulong() flags = body_stream.read(8) # A shared object message may contain a number of events. events = [] while not body_stream.at_eof(): event = self.read_shared_object_event(body_stream, decoder) events.append(event) ret['obj_name'] = obj_name ret['curr_version'] = curr_version ret['flags'] = flags ret['events'] = events elif ret['msg'] == rtmp_type.DT_COMMAND: decoder = amf0.Decoder(body_stream) commands = [] while not body_stream.at_eof(): commands.append(decoder.readElement()) ret['command'] = commands elif ret['msg'] == rtmp_type.DT_AMF3_COMMAND: decoder = amf3.Decoder(body_stream) commands = [] while not body_stream.at_eof(): commands.append(decoder.readElement()) ret['command'] = commands elif ret['msg'] == rtmp_type.DT_SET_CHUNK_SIZE: ret['chunk_size'] = body_stream.read_ulong() else: assert False, _header log.debug('recv %r', ret) return ret
def setUp(self): ClassCacheClearingTestCase.setUp(self) self.buf = util.BufferedByteStream() self.decoder = amf0.Decoder(self.buf) self.context = self.decoder.context