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.
		
		
		
		
			
				
					93 lines
				
				2.6 KiB
			
		
		
			
		
	
	
					93 lines
				
				2.6 KiB
			| 
								 
											1 year ago
										 
									 | 
							
								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
							 | 
						||
| 
								 | 
							
								
							 |