109 lines
2.9 KiB
Python
109 lines
2.9 KiB
Python
#!/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
|