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