You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							108 lines
						
					
					
						
							2.9 KiB
						
					
					
				
			
		
		
	
	
							108 lines
						
					
					
						
							2.9 KiB
						
					
					
				#!/usr/bin/env python3
 | 
						|
 | 
						|
import argparse
 | 
						|
import bisect
 | 
						|
import select
 | 
						|
import sys
 | 
						|
import termios
 | 
						|
import time
 | 
						|
import tty
 | 
						|
from collections import defaultdict
 | 
						|
 | 
						|
import cereal.messaging as messaging
 | 
						|
from openpilot.tools.lib.framereader import FrameReader
 | 
						|
from openpilot.tools.lib.logreader import LogReader
 | 
						|
from openpilot.tools.lib.openpilotci import get_url
 | 
						|
 | 
						|
IGNORE = ['initData', 'sentinel']
 | 
						|
 | 
						|
 | 
						|
def input_ready():
 | 
						|
  return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
 | 
						|
 | 
						|
 | 
						|
def replay(route, segment, loop):
 | 
						|
  route = route.replace('|', '/')
 | 
						|
 | 
						|
  lr = LogReader(get_url(route, segment))
 | 
						|
  fr = FrameReader(get_url(route, segment, "fcamera"), readahead=True)
 | 
						|
 | 
						|
  # Build mapping from frameId to segmentId from roadEncodeIdx, type == fullHEVC
 | 
						|
  msgs = [m for m in lr if m.which() not in IGNORE]
 | 
						|
  msgs = sorted(msgs, key=lambda m: m.logMonoTime)
 | 
						|
  times = [m.logMonoTime for m in msgs]
 | 
						|
  frame_idx = {m.roadEncodeIdx.frameId: m.roadEncodeIdx.segmentId for m in msgs if m.which() == 'roadEncodeIdx' and m.roadEncodeIdx.type == 'fullHEVC'}
 | 
						|
 | 
						|
  socks = {}
 | 
						|
  lag = 0.0
 | 
						|
  i = 0
 | 
						|
  max_i = len(msgs) - 2
 | 
						|
 | 
						|
  while True:
 | 
						|
    msg = msgs[i].as_builder()
 | 
						|
    next_msg = msgs[i + 1]
 | 
						|
 | 
						|
    start_time = time.time()
 | 
						|
    w = msg.which()
 | 
						|
 | 
						|
    if w == 'roadCameraState':
 | 
						|
      try:
 | 
						|
        img = fr.get(frame_idx[msg.roadCameraState.frameId], pix_fmt="rgb24")
 | 
						|
        img = img[0][:, :, ::-1]  # Convert RGB to BGR, which is what the camera outputs
 | 
						|
        msg.roadCameraState.image = img.flatten().tobytes()
 | 
						|
      except (KeyError, ValueError):
 | 
						|
        pass
 | 
						|
 | 
						|
    if w not in socks:
 | 
						|
      socks[w] = messaging.pub_sock(w)
 | 
						|
 | 
						|
    try:
 | 
						|
      if socks[w]:
 | 
						|
        socks[w].send(msg.to_bytes())
 | 
						|
    except messaging.messaging_pyx.MultiplePublishersError:
 | 
						|
      socks[w] = None
 | 
						|
 | 
						|
    lag += (next_msg.logMonoTime - msg.logMonoTime) / 1e9
 | 
						|
    lag -= time.time() - start_time
 | 
						|
 | 
						|
    dt = max(lag, 0.0)
 | 
						|
    lag -= dt
 | 
						|
    time.sleep(dt)
 | 
						|
 | 
						|
    if lag < -1.0 and i % 1000 == 0:
 | 
						|
      print(f"{-lag:.2f} s behind")
 | 
						|
 | 
						|
    if input_ready():
 | 
						|
      key = sys.stdin.read(1)
 | 
						|
 | 
						|
      # Handle pause
 | 
						|
      if key == " ":
 | 
						|
        while True:
 | 
						|
          if input_ready() and sys.stdin.read(1) == " ":
 | 
						|
            break
 | 
						|
          time.sleep(0.01)
 | 
						|
 | 
						|
      # Handle seek
 | 
						|
      dt = defaultdict(int, s=10, S=-10)[key]
 | 
						|
      new_time = msgs[i].logMonoTime + dt * 1e9
 | 
						|
      i = bisect.bisect_left(times, new_time)
 | 
						|
 | 
						|
    i = (i + 1) % max_i if loop else min(i + 1, max_i)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
  parser = argparse.ArgumentParser()
 | 
						|
  parser.add_argument("--loop", action='store_true')
 | 
						|
  parser.add_argument("route")
 | 
						|
  parser.add_argument("segment")
 | 
						|
  args = parser.parse_args()
 | 
						|
 | 
						|
  orig_settings = termios.tcgetattr(sys.stdin)
 | 
						|
  tty.setcbreak(sys.stdin)
 | 
						|
 | 
						|
  try:
 | 
						|
    replay(args.route, args.segment, args.loop)
 | 
						|
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
 | 
						|
  except Exception:
 | 
						|
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
 | 
						|
    raise
 | 
						|
 |