tools/Rerun: Add video logging features (#32810)
	
		
	
				
					
				
			* working
* multiprocessing
* fix that
* print services
* all services + fix
* less verbose
* start readme
* segment range
* cleanup
* update readme + fix bug in 'all'
* cleanup + update readme
* update readme
* cleanup
* cleanup
* rm frame_iter
* cleanup
* staticmethod
* proc kill
* split files
* fix range with hevc vids
* update reamde + add prompt
* readme
* readme
* readme
old-commit-hash: 5e0aff92ae
			
			
				vw-mqb-aeb
			
			
		
							parent
							
								
									8a16d47e7e
								
							
						
					
					
						commit
						41ee057acd
					
				
				 3 changed files with 299 additions and 64 deletions
			
			
		| @ -0,0 +1,57 @@ | |||||||
|  | # Rerun | ||||||
|  | Rerun is a tool to quickly visualize time series data. It supports all openpilot logs , both the `logMessages` and video logs. | ||||||
|  | 
 | ||||||
|  | [Instructions](https://rerun.io/docs/reference/viewer/overview) for navigation within the Rerun Viewer. | ||||||
|  | 
 | ||||||
|  | ## Usage | ||||||
|  | ``` | ||||||
|  | usage: run.py [-h] [--demo] [--qcam] [--fcam] [--ecam] [--dcam] [--print_services] [--services [SERVICES ...]] [route_or_segment_name] | ||||||
|  | 
 | ||||||
|  | A helper to run rerun on openpilot routes | ||||||
|  | 
 | ||||||
|  | options: | ||||||
|  |   -h, --help                  show this help message and exit | ||||||
|  |   --demo                      Use the demo route instead of providing one (default: False) | ||||||
|  |   --qcam                      Log decimated driving camera (default: False) | ||||||
|  |   --fcam                      Log driving camera (default: False) | ||||||
|  |   --ecam                      Log wide camera (default: False) | ||||||
|  |   --dcam                      Log driver monitoring camera (default: False) | ||||||
|  |   --print_services            List out openpilot services (default: False) | ||||||
|  |   --services [SERVICES ...]   Specify openpilot services that will be logged. No service will be logged if not specified. | ||||||
|  |                               To log all services include 'all' as one of your services (default: []) | ||||||
|  |   --route [ROUTE]             The route or segment name to plot (default: None) | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Examples using route name to observe accelerometer and qcamera: | ||||||
|  | 
 | ||||||
|  | `./run.py --services accelerometer --qcam --route "a2a0ccea32023010/2023-07-27--13-01-19"` | ||||||
|  | 
 | ||||||
|  | Examples using segment range (more on [SegmentRange](https://github.com/commaai/openpilot/tree/master/tools/lib)): | ||||||
|  | 
 | ||||||
|  | `./run.py --qcam --route "a2a0ccea32023010/2023-07-27--13-01-19/2:4"` | ||||||
|  | 
 | ||||||
|  | ## Cautions: | ||||||
|  | - You can specify `--services all` to visualize all `logMessage`, but it will draw a lot of memory usage and take a long time to log all messages. Rerun isn't ready for logging big number of data. | ||||||
|  | 
 | ||||||
|  | - Logging hevc videos (`--fcam`, `--ecam`, and `--dcam`)  are expensive, and it's recommended to use `--qcam` for optimized performance. If possible, limiting your route to a few segments using `SegmentRange` will speed up logging and reduce memory usage | ||||||
|  | 
 | ||||||
|  | This example draws 13GB of memory: | ||||||
|  | 
 | ||||||
|  | `./run.py --services accelerometer --qcam --route "a2a0ccea32023010/2023-07-27--13-01-19"` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Openpilot services | ||||||
|  | To list all openpilot services: | ||||||
|  | 
 | ||||||
|  | `./run.py --print_services` | ||||||
|  | 
 | ||||||
|  | Examples including openpilot services: | ||||||
|  | 
 | ||||||
|  | `./run.py --services accelerometer cameraodometry --route "a2a0ccea32023010/2023-07-27--13-01-19/0/q"` | ||||||
|  | 
 | ||||||
|  | Examples including all services: | ||||||
|  | 
 | ||||||
|  | `./run.py --services all --route "a2a0ccea32023010/2023-07-27--13-01-19/0/q"` | ||||||
|  | 
 | ||||||
|  | ## Demo | ||||||
|  | `./run.py --services accelerometer carcontrol caroutput --qcam --demo` | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | import tqdm | ||||||
|  | import subprocess | ||||||
|  | import multiprocessing | ||||||
|  | from enum import StrEnum | ||||||
|  | from functools import partial | ||||||
|  | from collections import namedtuple | ||||||
|  | 
 | ||||||
|  | from openpilot.tools.lib.framereader import ffprobe | ||||||
|  | 
 | ||||||
|  | CameraConfig = namedtuple("CameraConfig", ["qcam", "fcam", "ecam", "dcam"]) | ||||||
|  | 
 | ||||||
|  | class CameraType(StrEnum): | ||||||
|  |   qcam = "qcamera" | ||||||
|  |   fcam = "fcamera" | ||||||
|  |   ecam = "ecamera" | ||||||
|  |   dcam = "dcamera" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def probe_packet_info(camera_path): | ||||||
|  |   args = ["ffprobe", "-v", "quiet", "-show_packets", "-probesize", "10M", camera_path] | ||||||
|  |   dat = subprocess.check_output(args) | ||||||
|  |   dat = dat.decode().split() | ||||||
|  |   return dat | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class _FrameReader: | ||||||
|  |   def __init__(self, camera_path, segment, h, w, start_time): | ||||||
|  |     self.camera_path = camera_path | ||||||
|  |     self.segment = segment | ||||||
|  |     self.h = h | ||||||
|  |     self.w = w | ||||||
|  |     self.start_time = start_time | ||||||
|  | 
 | ||||||
|  |     self.ts = self._get_ts() | ||||||
|  | 
 | ||||||
|  |   def _read_stream_nv12(self): | ||||||
|  |     frame_sz = self.w * self.h * 3 // 2 | ||||||
|  |     proc = subprocess.Popen( | ||||||
|  |              ["ffmpeg", "-v", "quiet", "-i", self.camera_path, "-f", "rawvideo", "-pix_fmt", "nv12", "-"], | ||||||
|  |              stdout=subprocess.PIPE, | ||||||
|  |              stderr=subprocess.DEVNULL | ||||||
|  |            ) | ||||||
|  |     try: | ||||||
|  |       while True: | ||||||
|  |         dat = proc.stdout.read(frame_sz) | ||||||
|  |         if len(dat) == 0: | ||||||
|  |           break | ||||||
|  |         yield dat | ||||||
|  |     finally: | ||||||
|  |       proc.kill() | ||||||
|  | 
 | ||||||
|  |   def _get_ts(self): | ||||||
|  |     dat = probe_packet_info(self.camera_path) | ||||||
|  |     try: | ||||||
|  |       ret = [float(d.split('=')[1]) for d in dat if d.startswith("pts_time=")] | ||||||
|  |     except ValueError: | ||||||
|  |       # pts_times aren't available. Infer timestamps from duration_times | ||||||
|  |       ret = [d for d in dat if d.startswith("duration_time")] | ||||||
|  |       ret = [float(d.split('=')[1])*(i+1)+(self.segment*60)+self.start_time for i, d in enumerate(ret)] | ||||||
|  |     return ret | ||||||
|  | 
 | ||||||
|  |   def __iter__(self): | ||||||
|  |     for i, frame in enumerate(self._read_stream_nv12()): | ||||||
|  |       yield self.ts[i], frame | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CameraReader: | ||||||
|  |   def __init__(self, camera_paths, start_time, seg_idxs): | ||||||
|  |     self.seg_idxs = seg_idxs | ||||||
|  |     self.camera_paths = camera_paths | ||||||
|  |     self.start_time = start_time | ||||||
|  | 
 | ||||||
|  |     probe = ffprobe(camera_paths[0])["streams"][0] | ||||||
|  |     self.h = probe["height"] | ||||||
|  |     self.w = probe["width"] | ||||||
|  | 
 | ||||||
|  |     self.__frs = {} | ||||||
|  | 
 | ||||||
|  |   def _get_fr(self, i): | ||||||
|  |     if i not in self.__frs: | ||||||
|  |       self.__frs[i] = _FrameReader(self.camera_paths[i], segment=i, h=self.h, w=self.w, start_time=self.start_time) | ||||||
|  |     return self.__frs[i] | ||||||
|  | 
 | ||||||
|  |   def _run_on_segment(self, func, i): | ||||||
|  |     return func(self._get_fr(i)) | ||||||
|  | 
 | ||||||
|  |   def run_across_segments(self, num_processes, func): | ||||||
|  |     with multiprocessing.Pool(num_processes) as pool: | ||||||
|  |       num_segs = len(self.seg_idxs) | ||||||
|  |       for _ in tqdm.tqdm(pool.imap_unordered(partial(self._run_on_segment, func), self.seg_idxs), total=num_segs): | ||||||
|  |         continue | ||||||
|  | 
 | ||||||
					Loading…
					
					
				
		Reference in new issue