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 " , " - " ] ,
stdin = subprocess . PIPE ,
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 , desc = None ) :
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 , desc = desc ) :
continue