def __init__(self, file: BinaryIO): self.events: Dict[int, List[int]] = {} # Remember the current file position startingPosition = file.tell() # Get file size file.seek(0, os.SEEK_END) size = file.tell() # Take note of the position of each event and its timestamp events = defaultdict(list) currentMessagePosition = 0 file.seek(0) # Register PDUs as they are parsed by the layer def registerEvent(pdu: PlayerMessagePDU): events[pdu.timestamp].append(currentMessagePosition) # The layer will take care of parsing for us player = PlayerMessageLayer() player.createObserver(onPDUReceived=registerEvent) # Parse all events in the file while file.tell() < size: data = file.read(8) player.recv(data) data = file.read(player.getDataLengthRequired()) player.recv(data) currentMessagePosition = file.tell() # Restore original file position file.seek(startingPosition) # Use relative timestamps to simplify things if len(events) == 0: self.duration = 0 else: timestamps = sorted(events.keys()) referenceTime = timestamps[0] for absoluteTimestamp in timestamps: relativeTimestamp = absoluteTimestamp - referenceTime self.events[relativeTimestamp] = events[absoluteTimestamp] self.duration = (timestamps[-1] - referenceTime) / 1000.0
class ReplayTab(BaseTab): """ Tab that displays a RDP Connection that is being replayed from a file. """ def __init__(self, fileName: str, parent: QWidget = None): """ :param fileName: name of the file to read. :param parent: parent widget. """ self.viewer = QRemoteDesktop(800, 600) super().__init__(self.viewer, parent) QApplication.instance().aboutToQuit.connect(self.onClose) self.fileName = fileName self.file = open(self.fileName, "rb") self.eventHandler = PlayerMessageHandler(self.widget, self.text) replay = Replay(self.file) self.thread = ReplayThread(replay) self.thread.eventReached.connect(self.readEvent) self.thread.timeUpdated.connect(self.onTimeUpdated) self.thread.clearNeeded.connect(self.clear) self.thread.start() self.controlBar = ReplayBar(replay.duration) self.controlBar.play.connect(self.thread.play) self.controlBar.pause.connect(self.thread.pause) self.controlBar.seek.connect(self.thread.seek) self.controlBar.speedChanged.connect(self.thread.setSpeed) self.layout().insertWidget(0, self.controlBar) self.player = PlayerMessageLayer() self.player.addObserver(self.eventHandler) def readEvent(self, position: int): """ Read an event from the file at the given position. :param position: the position of the event in the file. """ self.file.seek(position) data = self.file.read(8) self.player.recv(data) length = self.player.getDataLengthRequired() data = self.file.read(length) self.player.recv(data) def onTimeUpdated(self, currentTime: float): """ Called everytime the thread ticks. :param currentTime: the current time. """ self.controlBar.timeSlider.blockSignals(True) self.controlBar.timeSlider.setValue(int(currentTime * 1000)) self.controlBar.timeSlider.blockSignals(False) def clear(self): """ Clear the UI. """ self.viewer.clear() self.text.setText("") def onClose(self): self.thread.close() self.thread.wait()