diff --git a/RELEASES.md b/RELEASES.md index 1cc907e407..0bcea1bdbb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,8 +1,10 @@ -Version 0.9.7 (2024-XX-XX) +Version 0.9.7 (2024-05-XX) ======================== * New driving model * Adjust driving personality with the follow distance button * Support for hybrid variants of supported Ford models +* Added toggle to enable driver monitoring even when openpilot is not engaged +* Fingerprinting without the OBD-II port on all cars * Hyundai Palisade (with HDA II) 2023-24 support thanks to sunnyhaibin! * Kia Telluride (with HDA II) 2023-24 support thanks to sunnyhaibin! diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index d5cf773cdd..e715d3b5e9 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -104,10 +104,10 @@ class CarController(CarControllerBase): can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN]) hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2 - can_canfd = self.CP.flags & HyundaiFlags.CAN_CANFD + can_canfd_hybrid = self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID # CAN-FD platforms - if self.CP.carFingerprint in CANFD_CAR or (hda2 and can_canfd): + if self.CP.carFingerprint in CANFD_CAR or (hda2 and can_canfd_hybrid): hda2_long = hda2 and self.CP.openpilotLongitudinalControl # steering control @@ -146,7 +146,7 @@ class CarController(CarControllerBase): self.accel_last = accel else: # button presses - can_sends.extend(self.create_button_messages(CC, CS, use_clu11=(hda2 and can_canfd))) + can_sends.extend(self.create_button_messages(CC, CS, use_clu11=(hda2 and can_canfd_hybrid))) else: can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, apply_steer_req, torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled, diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 8c6e1ffd41..5851e9501b 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -8,7 +8,7 @@ from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \ - CANFD_CAR, Buttons, CarControllerParams + CANFD_CAR, Buttons, CarControllerParams, CAN_CANFD_HYBRID_CAR from openpilot.selfdrive.car.interfaces import CarStateBase PREV_BUTTON_SAMPLES = 8 @@ -27,7 +27,7 @@ class CarState(CarStateBase): self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \ "GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \ "GEAR_SHIFTER" - if CP.carFingerprint in CANFD_CAR: + if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"] elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]: self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"] @@ -53,7 +53,7 @@ class CarState(CarStateBase): self.params = CarControllerParams(CP) def update(self, cp, cp_cam): - if self.CP.carFingerprint in CANFD_CAR: + if self.CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): return self.update_canfd(cp, cp_cam) ret = car.CarState.new_message() @@ -107,7 +107,7 @@ class CarState(CarStateBase): ret.cruiseState.standstill = False ret.cruiseState.nonAdaptive = False else: - scc_bus = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD else "SCC11" + scc_bus = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else "SCC11" ret.cruiseState.available = cp_cruise.vl[scc_bus]["MainMode_ACC"] == 1 ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0 ret.cruiseState.standstill = cp_cruise.vl[scc_bus]["SCCInfoDisplay"] == 4. @@ -145,7 +145,7 @@ class CarState(CarStateBase): ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) - if not self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CAN_CANFD.value): + if not self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID): aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12" aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct" aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0 @@ -159,10 +159,10 @@ class CarState(CarStateBase): ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0 # save the entire LKAS11 and CLU11 - if not self.CP.flags & HyundaiFlags.CAN_CANFD: + if not self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID: self.lkas11 = copy.copy(cp_cam.vl["LKAS11"]) self.clu11 = copy.copy(cp.vl["CLU11"]) - if self.CP.flags & HyundaiFlags.CAN_CANFD and self.CP.flags & HyundaiFlags.CANFD_HDA2: + if self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID and self.CP.flags & HyundaiFlags.CANFD_HDA2: self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x2a4"]) self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE self.prev_cruise_buttons = self.cruise_buttons[-1] @@ -254,12 +254,12 @@ class CarState(CarStateBase): return ret def get_can_parser(self, CP): - if CP.carFingerprint in CANFD_CAR: + if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): return self.get_can_parser_canfd(CP) messages = [ # address, frequency - ("MDPS12", 100 if CP.flags & HyundaiFlags.CAN_CANFD else 50), + ("MDPS12", 100 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50), ("TCS11", 100), ("TCS13", 50), ("TCS15", 10), @@ -274,7 +274,7 @@ class CarState(CarStateBase): ] if not CP.openpilotLongitudinalControl: - if CP.flags & HyundaiFlags.CAN_CANFD: + if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID: messages.append(("SCC12", 50)) elif CP.carFingerprint not in CAMERA_SCC_CAR: messages += [ @@ -285,7 +285,7 @@ class CarState(CarStateBase): messages.append(("FCA11", 50)) if CP.enableBsm: - messages.append(("LCA11", 20 if CP.flags & HyundaiFlags.CAN_CANFD else 50)) + messages.append(("LCA11", 20 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50)) if CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV): messages.append(("E_EMS11", 50)) @@ -304,17 +304,17 @@ class CarState(CarStateBase): else: messages.append(("LVR12", 100)) - bus = CanBus(CP).ECAN if CP.flags & HyundaiFlags.CAN_CANFD.value else 0 + bus = CanBus(CP).ECAN if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0 return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus) @staticmethod def get_cam_can_parser(CP): - if CP.carFingerprint in CANFD_CAR: + if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): return CarState.get_cam_can_parser_canfd(CP) messages = [] - if CP.flags & HyundaiFlags.CAN_CANFD: + if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID: messages.append(("CAM_0x2a4", 20)) else: messages.append(("LKAS11", 100)) @@ -328,7 +328,7 @@ class CarState(CarStateBase): if CP.flags & HyundaiFlags.USE_FCA.value: messages.append(("FCA11", 50)) - bus = CanBus(CP).CAM if CP.flags & HyundaiFlags.CAN_CANFD else 2 + bus = CanBus(CP).CAM if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 2 return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus) def get_can_parser_canfd(self, CP): diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index 628a33a261..854540721b 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -113,7 +113,7 @@ def create_clu11(packer, frame, clu11, button, CP, CAN): values["CF_Clu_CruiseSwState"] = button values["CF_Clu_AliveCnt1"] = frame % 0x10 # send buttons to camera on camera-scc based cars - bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else CAN.ECAN if CP.flags & HyundaiFlags.CAN_CANFD else 0 + bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else CAN.ECAN if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0 return packer.make_can_msg("CLU11", bus, values) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index b4dce8fc32..34d9373bd8 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -3,7 +3,7 @@ from panda import Panda from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \ CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \ - UNSUPPORTED_LONGITUDINAL_CAR, Buttons, CAN_CANFD_CAR + UNSUPPORTED_LONGITUDINAL_CAR, Buttons, CAN_CANFD_HYBRID_CAR from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase @@ -36,14 +36,13 @@ class CarInterface(CarInterfaceBase): if hda2: ret.flags |= HyundaiFlags.CANFD_HDA2.value - if candidate in CANFD_CAR: + if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): # detect if car is hybrid if 0x105 in fingerprint[CAN.ECAN]: ret.flags |= HyundaiFlags.HYBRID.value elif candidate in EV_CAR: ret.flags |= HyundaiFlags.EV.value - # detect HDA2 with ADAS Driving ECU if hda2: if 0x110 in fingerprint[CAN.CAM]: ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value @@ -66,10 +65,6 @@ class CarInterface(CarInterfaceBase): elif candidate in EV_CAR: ret.flags |= HyundaiFlags.EV.value - # detect cars with hybrid definitions of CAN and CAN-FD - if candidate in CAN_CANFD_CAR: - ret.flags |= HyundaiFlags.CAN_CANFD.value - # Send LFA message on cars with HDA if 0x485 in fingerprint[2]: ret.flags |= HyundaiFlags.SEND_LFA.value @@ -92,7 +87,7 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(platform_str, ret.lateralTuning) # *** longitudinal control *** - if candidate in CANFD_CAR: + if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): ret.longitudinalTuning.kpV = [0.1] ret.longitudinalTuning.kiV = [0.0] ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR) @@ -114,45 +109,43 @@ class CarInterface(CarInterfaceBase): ret.stoppingDecelRate = 0.4 # *** feature detection *** - if candidate in CANFD_CAR: + if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN] else: - bus = CAN.ECAN if ret.flags & HyundaiFlags.CAN_CANFD else 0 + bus = CAN.ECAN if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0 ret.enableBsm = 0x58b in fingerprint[bus] # *** panda safety config *** - if candidate in CANFD_CAR: + if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd), ] if CAN.ECAN >= 4: cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput)) ret.safetyConfigs = cfgs - - if ret.flags & HyundaiFlags.CANFD_HDA2: - ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2 - if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING: - ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC else: - if ret.flags & HyundaiFlags.CAN_CANFD: + if candidate in LEGACY_SAFETY_MODE_CAR: + # these cars require a special panda safety mode due to missing counters and checksums in the messages + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] + else: cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundai), ] if CAN.ECAN >= 4: cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput)) ret.safetyConfigs = cfgs - if ret.flags & HyundaiFlags.CANFD_HDA2: - ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2 - ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAN_CANFD - elif candidate in LEGACY_SAFETY_MODE_CAR: - # these cars require a special panda safety mode due to missing counters and checksums in the messages - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] - else: - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)] + if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID: + ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAN_CANFD_HYBRID if candidate in CAMERA_SCC_CAR: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC + # these flags apply to both CANFD_CAR and CAN_CANFD_HYBRID_CAR platforms + if ret.flags & HyundaiFlags.CANFD_HDA2: + ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2 + if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING: + ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING + if ret.openpilotLongitudinalControl: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG if ret.flags & HyundaiFlags.HYBRID: @@ -166,6 +159,10 @@ class CarInterface(CarInterfaceBase): ret.centerToFront = ret.wheelbase * 0.4 + # FIXME: Non-HDA2 2023-24 Hyundai Palisade / Kia Telluride is disabled in this branch; + # The non-HDA2 variant is supported in another PR: https://github.com/commaai/openpilot/pull/27478 + ret.dashcamOnly |= candidate in (CAR.HYUNDAI_PALISADE_2023, ) and not hda2 + return ret @staticmethod diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index f3073812eb..ee0ab2562d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -25,7 +25,7 @@ class CarControllerParams: self.STEER_THRESHOLD = 150 self.STEER_STEP = 1 # 100 Hz - if CP.carFingerprint in CANFD_CAR: + if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR): self.STEER_MAX = 270 self.STEER_DRIVER_ALLOWANCE = 250 self.STEER_DRIVER_MULTIPLIER = 2 @@ -95,8 +95,8 @@ class HyundaiFlags(IntFlag): MIN_STEER_32_MPH = 2 ** 23 - # CAN CAN-FD - CAN_CANFD = 2 ** 24 + # CAN CAN-FD hybrid architecture + CAN_CANFD_HYBRID = 2 ** 24 class Footnote(Enum): @@ -126,9 +126,6 @@ class HyundaiPlatformConfig(PlatformConfig): if self.flags & HyundaiFlags.MIN_STEER_32_MPH: self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS) - if self.flags & HyundaiFlags.CAN_CANFD: - self.dbc_dict = dbc_dict('hyundai_palisade_2023_generated', None) - @dataclass class HyundaiCanFDPlatformConfig(PlatformConfig): @@ -137,6 +134,9 @@ class HyundaiCanFDPlatformConfig(PlatformConfig): def init(self): self.flags |= HyundaiFlags.CANFD + if self.flags & HyundaiFlags.CAN_CANFD_HYBRID: + self.dbc_dict = dbc_dict('hyundai_palisade_2023_generated', None) + class CAR(Platforms): # Hyundai @@ -300,13 +300,13 @@ class CAR(Platforms): CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63), flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8, ) - HYUNDAI_PALISADE_2023 = HyundaiPlatformConfig( + HYUNDAI_PALISADE_2023 = HyundaiCanFDPlatformConfig( [ HyundaiCarDocs("Hyundai Palisade (with HDA II) 2023-24", "All", car_parts=CarParts.common([CarHarness.hyundai_r])), HyundaiCarDocs("Kia Telluride (with HDA II) 2023-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), ], HYUNDAI_PALISADE.specs, - flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD | HyundaiFlags.RADAR_SCC, + flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_HYBRID | HyundaiFlags.RADAR_SCC, ) HYUNDAI_VELOSTER = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e]))], @@ -586,7 +586,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str # Non-electric CAN FD platforms often do not have platform code specifiers needed # to distinguish between hybrid and ICE. All EVs so far are either exclusively # electric or specify electric in the platform code. - fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)} + fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - CAN_CANFD_HYBRID_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)} candidates: set[str] = set() for candidate, fws in offline_fw_versions.items(): @@ -743,7 +743,7 @@ CAN_GEARS = { CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD) CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) -CAN_CANFD_CAR = CAR.with_flags(HyundaiFlags.CAN_CANFD) +CAN_CANFD_HYBRID_CAR = CAR.with_flags(HyundaiFlags.CAN_CANFD_HYBRID) # These CAN FD cars do not accept communication control to disable the ADAS ECU, # responds with 0x7F2822 - 'conditions not correct' diff --git a/selfdrive/debug/uiview.py b/selfdrive/debug/uiview.py index f4440a912c..958ee72e04 100755 --- a/selfdrive/debug/uiview.py +++ b/selfdrive/debug/uiview.py @@ -4,12 +4,13 @@ import time from cereal import car, log, messaging from openpilot.common.params import Params from openpilot.selfdrive.manager.process_config import managed_processes +from openpilot.system.hardware import HARDWARE if __name__ == "__main__": - CP = car.CarParams(notCar=True) + CP = car.CarParams(notCar=True, wheelbase=1, steerRatio=10) Params().put("CarParams", CP.to_bytes()) - procs = ['camerad', 'ui', 'modeld', 'calibrationd'] + procs = ['camerad', 'ui', 'modeld', 'calibrationd', 'plannerd', 'dmonitoringmodeld', 'dmonitoringd'] for p in procs: managed_processes[p].start() @@ -17,6 +18,7 @@ if __name__ == "__main__": msgs = {s: messaging.new_message(s) for s in ['controlsState', 'deviceState', 'carParams']} msgs['deviceState'].deviceState.started = True + msgs['deviceState'].deviceState.deviceType = HARDWARE.get_device_type() msgs['carParams'].carParams.openpilotLongitudinalControl = True msgs['pandaStates'] = messaging.new_message('pandaStates', 1) diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index 664364e04f..5f178e9f8f 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -123,12 +123,15 @@ void MapWindow::updateState(const UIState &s) { auto locationd_pos = locationd_location.getPositionGeodetic(); auto locationd_orientation = locationd_location.getCalibratedOrientationNED(); auto locationd_velocity = locationd_location.getVelocityCalibrated(); + auto locationd_ecef = locationd_location.getPositionECEF(); - // Check std norm - auto pos_ecef_std = locationd_location.getPositionECEF().getStd(); - bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100; - - locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough); + locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && locationd_ecef.getValid()); + if (locationd_valid) { + // Check std norm + auto pos_ecef_std = locationd_ecef.getStd(); + bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100; + locationd_valid = pos_accurate_enough; + } if (locationd_valid) { last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]); diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc index 0c50d02f4f..717da47096 100644 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ b/selfdrive/ui/qt/network/wifi_manager.cc @@ -1,5 +1,7 @@ #include "selfdrive/ui/qt/network/wifi_manager.h" +#include + #include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/widgets/prime.h" @@ -14,9 +16,15 @@ bool compare_by_strength(const Network &a, const Network &b) { template T call(const QString &path, const QString &interface, const QString &method, Args &&...args) { - QDBusInterface nm = QDBusInterface(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus()); + QDBusInterface nm(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus()); nm.setTimeout(DBUS_TIMEOUT); - QDBusMessage response = nm.call(method, args...); + + QDBusMessage response = nm.call(method, std::forward(args)...); + if (response.type() == QDBusMessage::ErrorMessage) { + qCritical() << "DBus call error:" << response.errorMessage(); + return T(); + } + if constexpr (std::is_same_v) { return response; } else if (response.arguments().count() >= 1) { @@ -99,10 +107,20 @@ void WifiManager::refreshFinished(QDBusPendingCallWatcher *watcher) { seenNetworks.clear(); const QDBusReply> watcher_reply = *watcher; + if (!watcher_reply.isValid()) { + qCritical() << "Failed to refresh"; + watcher->deleteLater(); + return; + } + for (const QDBusObjectPath &path : watcher_reply.value()) { QDBusReply reply = call(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "GetAll", NM_DBUS_INTERFACE_ACCESS_POINT); - auto properties = reply.value(); + if (!reply.isValid()) { + qCritical() << "Failed to retrieve properties for path:" << path.path(); + continue; + } + auto properties = reply.value(); const QByteArray ssid = properties["Ssid"].toByteArray(); if (ssid.isEmpty()) continue; diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 081279d38d..096e288cc2 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -31,8 +31,7 @@ int CameraState::clear_req_queue() { req_mgr_flush_request.session_hdl = session_handle; req_mgr_flush_request.link_hdl = link_handle; req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL; - int ret; - ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request)); + int ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request)); // LOGD("flushed all req: %d", ret); return ret; } @@ -470,7 +469,6 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num enabled = enabled_; if (!enabled) return; - int ret; sensor_fd = open_v4l_by_name_and_index("cam-sensor-driver", camera_num); assert(sensor_fd >= 0); LOGD("opened sensor for %d", camera_num); @@ -501,7 +499,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num // create session struct cam_req_mgr_session_info session_info = {}; - ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_CREATE_SESSION, &session_info, sizeof(session_info)); + int ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_CREATE_SESSION, &session_info, sizeof(session_info)); LOGD("get session: %d 0x%X", ret, session_info.session_hdl); session_handle = session_info.session_hdl; @@ -654,8 +652,6 @@ void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_i } void cameras_open(MultiCameraState *s) { - int ret; - LOG("-- Opening devices"); // video0 is req_mgr, the target of many ioctls s->video0_fd = HANDLE_EINTR(open("/dev/v4l/by-path/platform-soc:qcom_cam-req-mgr-video-index0", O_RDWR | O_NONBLOCK)); @@ -679,7 +675,7 @@ void cameras_open(MultiCameraState *s) { query_cap_cmd.handle_type = 1; query_cap_cmd.caps_handle = (uint64_t)&isp_query_cap_cmd; query_cap_cmd.size = sizeof(isp_query_cap_cmd); - ret = do_cam_control(s->isp_fd, CAM_QUERY_CAP, &query_cap_cmd, sizeof(query_cap_cmd)); + int ret = do_cam_control(s->isp_fd, CAM_QUERY_CAP, &query_cap_cmd, sizeof(query_cap_cmd)); assert(ret == 0); LOGD("using MMU handle: %x", isp_query_cap_cmd.device_iommu.non_secure); LOGD("using MMU handle: %x", isp_query_cap_cmd.cdm_iommu.non_secure); @@ -703,15 +699,13 @@ void cameras_open(MultiCameraState *s) { } void CameraState::camera_close() { - int ret; - // stop devices LOG("-- Stop devices %d", camera_num); if (enabled) { // ret = device_control(sensor_fd, CAM_STOP_DEV, session_handle, sensor_dev_handle); // LOGD("stop sensor: %d", ret); - ret = device_control(multi_cam_state->isp_fd, CAM_STOP_DEV, session_handle, isp_dev_handle); + int ret = device_control(multi_cam_state->isp_fd, CAM_STOP_DEV, session_handle, isp_dev_handle); LOGD("stop isp: %d", ret); ret = device_control(csiphy_fd, CAM_STOP_DEV, session_handle, csiphy_dev_handle); LOGD("stop csiphy: %d", ret); @@ -746,7 +740,7 @@ void CameraState::camera_close() { LOGD("released buffers"); } - ret = device_control(sensor_fd, CAM_RELEASE_DEV, session_handle, sensor_dev_handle); + int ret = device_control(sensor_fd, CAM_RELEASE_DEV, session_handle, sensor_dev_handle); LOGD("release sensor: %d", ret); // destroyed session diff --git a/system/camerad/cameras/camera_util.cc b/system/camerad/cameras/camera_util.cc index 4fe38997ce..7bd23fd9fe 100644 --- a/system/camerad/cameras/camera_util.cc +++ b/system/camerad/cameras/camera_util.cc @@ -86,11 +86,10 @@ void *alloc_w_mmu_hdl(int video0_fd, int len, uint32_t *handle, int align, int f } void release(int video0_fd, uint32_t handle) { - int ret; struct cam_mem_mgr_release_cmd mem_mgr_release_cmd = {0}; mem_mgr_release_cmd.buf_handle = handle; - ret = do_cam_control(video0_fd, CAM_REQ_MGR_RELEASE_BUF, &mem_mgr_release_cmd, sizeof(mem_mgr_release_cmd)); + int ret = do_cam_control(video0_fd, CAM_REQ_MGR_RELEASE_BUF, &mem_mgr_release_cmd, sizeof(mem_mgr_release_cmd)); assert(ret == 0); } diff --git a/system/camerad/main.cc b/system/camerad/main.cc index 19de21c9bb..b86b4f57ff 100644 --- a/system/camerad/main.cc +++ b/system/camerad/main.cc @@ -12,8 +12,7 @@ int main(int argc, char *argv[]) { return 0; } - int ret; - ret = util::set_realtime_priority(53); + int ret = util::set_realtime_priority(53); assert(ret == 0); ret = util::set_core_affinity({6}); assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index 6f08a9f20b..dd34782e33 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -111,6 +111,8 @@ void ChartView::setTheme(QChart::ChartTheme theme) { axis_y->setLabelsBrush(palette().text()); chart()->legend()->setLabelColor(palette().color(QPalette::Text)); } + axis_x->setLineVisible(false); + axis_y->setLineVisible(false); for (auto &s : sigs) { s.series->setColor(s.sig->color); } @@ -745,8 +747,8 @@ void ChartView::drawTimeline(QPainter *painter) { const auto plot_area = chart()->plotArea(); // draw vertical time line qreal x = std::clamp(chart()->mapToPosition(QPointF{cur_sec, 0}).x(), plot_area.left(), plot_area.right()); - painter->setPen(QPen(chart()->titleBrush().color(), 2)); - painter->drawLine(QPointF{x, plot_area.top()}, QPointF{x, plot_area.bottom() + 1}); + painter->setPen(QPen(chart()->titleBrush().color(), 1)); + painter->drawLine(QPointF{x, plot_area.top() - 1}, QPointF{x, plot_area.bottom() + 1}); // draw current time under the axis-x QString time_str = QString::number(cur_sec, 'f', 2); diff --git a/tools/cabana/dbc/dbc.h b/tools/cabana/dbc/dbc.h index 71838a1df5..da44319b5c 100644 --- a/tools/cabana/dbc/dbc.h +++ b/tools/cabana/dbc/dbc.h @@ -29,11 +29,11 @@ struct MessageId { } bool operator<(const MessageId &other) const { - return std::pair{source, address} < std::pair{other.source, other.address}; + return std::tie(source, address) < std::tie(other.source, other.address); } bool operator>(const MessageId &other) const { - return std::pair{source, address} > std::pair{other.source, other.address}; + return std::tie(source, address) > std::tie(other.source, other.address); } }; diff --git a/tools/cabana/dbc/dbcfile.cc b/tools/cabana/dbc/dbcfile.cc index bf246fd342..4a1c52819a 100644 --- a/tools/cabana/dbc/dbcfile.cc +++ b/tools/cabana/dbc/dbcfile.cc @@ -3,7 +3,6 @@ #include #include #include -#include DBCFile::DBCFile(const QString &dbc_file_name) { QFile file(dbc_file_name); @@ -76,117 +75,44 @@ cabana::Msg *DBCFile::msg(const QString &name) { return it != msgs.end() ? &(it->second) : nullptr; } +cabana::Signal *DBCFile::signal(uint32_t address, const QString name) { + auto m = msg(address); + return m ? (cabana::Signal *)m->sig(name) : nullptr; +} + void DBCFile::parse(const QString &content) { - static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); - static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); - static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); - static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)"); - static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)"); - static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))"); + msgs.clear(); int line_num = 0; QString line; - auto dbc_assert = [&line_num, &line, this](bool condition, const QString &msg = "") { - if (!condition) throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(msg).arg(line).toStdString()); - }; - auto get_sig = [this](uint32_t address, const QString &name) -> cabana::Signal * { - auto m = (cabana::Msg *)msg(address); - return m ? (cabana::Signal *)m->sig(name) : nullptr; - }; - - msgs.clear(); - QTextStream stream((QString *)&content); cabana::Msg *current_msg = nullptr; int multiplexor_cnt = 0; bool seen_first = false; + QTextStream stream((QString *)&content); + while (!stream.atEnd()) { ++line_num; QString raw_line = stream.readLine(); line = raw_line.trimmed(); bool seen = true; - if (line.startsWith("BO_ ")) { - multiplexor_cnt = 0; - auto match = bo_regexp.match(line); - dbc_assert(match.hasMatch()); - auto address = match.captured(1).toUInt(); - dbc_assert(msgs.count(address) == 0, QString("Duplicate message address: %1").arg(address)); - current_msg = &msgs[address]; - current_msg->address = address; - current_msg->name = match.captured(2); - current_msg->size = match.captured(3).toULong(); - current_msg->transmitter = match.captured(4).trimmed(); - } else if (line.startsWith("SG_ ")) { - int offset = 0; - auto match = sg_regexp.match(line); - if (!match.hasMatch()) { - match = sgm_regexp.match(line); - offset = 1; + try { + if (line.startsWith("BO_ ")) { + multiplexor_cnt = 0; + current_msg = parseBO(line); + } else if (line.startsWith("SG_ ")) { + parseSG(line, current_msg, multiplexor_cnt); + } else if (line.startsWith("VAL_ ")) { + parseVAL(line); + } else if (line.startsWith("CM_ BO_")) { + parseCM_BO(line, content, raw_line, stream); + } else if (line.startsWith("CM_ SG_ ")) { + parseCM_SG(line, content, raw_line, stream); + } else { + seen = false; } - dbc_assert(match.hasMatch()); - dbc_assert(current_msg, "No Message"); - auto name = match.captured(1); - dbc_assert(current_msg->sig(name) == nullptr, "Duplicate signal name"); - cabana::Signal s{}; - if (offset == 1) { - auto indicator = match.captured(2); - if (indicator == "M") { - // Only one signal within a single message can be the multiplexer switch. - dbc_assert(++multiplexor_cnt < 2, "Multiple multiplexor"); - s.type = cabana::Signal::Type::Multiplexor; - } else { - s.type = cabana::Signal::Type::Multiplexed; - s.multiplex_value = indicator.mid(1).toInt(); - } - } - s.name = name; - s.start_bit = match.captured(offset + 2).toInt(); - s.size = match.captured(offset + 3).toInt(); - s.is_little_endian = match.captured(offset + 4).toInt() == 1; - s.is_signed = match.captured(offset + 5) == "-"; - s.factor = match.captured(offset + 6).toDouble(); - s.offset = match.captured(offset + 7).toDouble(); - s.min = match.captured(8 + offset).toDouble(); - s.max = match.captured(9 + offset).toDouble(); - s.unit = match.captured(10 + offset); - s.receiver_name = match.captured(11 + offset).trimmed(); - - current_msg->sigs.push_back(new cabana::Signal(s)); - } else if (line.startsWith("VAL_ ")) { - auto match = val_regexp.match(line); - dbc_assert(match.hasMatch()); - if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) { - QStringList desc_list = match.captured(3).trimmed().split('"'); - for (int i = 0; i < desc_list.size(); i += 2) { - auto val = desc_list[i].trimmed(); - if (!val.isEmpty() && (i + 1) < desc_list.size()) { - auto desc = desc_list[i + 1].trimmed(); - s->val_desc.push_back({val.toDouble(), desc}); - } - } - } - } else if (line.startsWith("CM_ BO_")) { - if (!line.endsWith("\";")) { - int pos = stream.pos() - raw_line.length() - 1; - line = content.mid(pos, content.indexOf("\";", pos)); - } - auto match = msg_comment_regexp.match(line); - dbc_assert(match.hasMatch()); - if (auto m = (cabana::Msg *)msg(match.captured(1).toUInt())) { - m->comment = match.captured(2).trimmed().replace("\\\"", "\""); - } - } else if (line.startsWith("CM_ SG_ ")) { - if (!line.endsWith("\";")) { - int pos = stream.pos() - raw_line.length() - 1; - line = content.mid(pos, content.indexOf("\";", pos)); - } - auto match = sg_comment_regexp.match(line); - dbc_assert(match.hasMatch()); - if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) { - s->comment = match.captured(3).trimmed().replace("\\\"", "\""); - } - } else { - seen = false; + } catch (std::exception &e) { + throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(e.what()).arg(line).toStdString()); } if (seen) { @@ -201,6 +127,127 @@ void DBCFile::parse(const QString &content) { } } +cabana::Msg *DBCFile::parseBO(const QString &line) { + static QRegularExpression bo_regexp(R"(^BO_ (?
\w+) (?\w+) *: (?\w+) (?\w+))"); + + QRegularExpressionMatch match = bo_regexp.match(line); + if (!match.hasMatch()) + throw std::runtime_error("Invalid BO_ line format"); + + uint32_t address = match.captured("address").toUInt(); + if (msgs.count(address) > 0) + throw std::runtime_error(QString("Duplicate message address: %1").arg(address).toStdString()); + + // Create a new message object + cabana::Msg *msg = &msgs[address]; + msg->address = address; + msg->name = match.captured("name"); + msg->size = match.captured("size").toULong(); + msg->transmitter = match.captured("transmitter").trimmed(); + return msg; +} + +void DBCFile::parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) { + static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(?
\w+) *\"(?(?:[^"\\]|\\.)*)\"\s*;)"); + + QString parse_line = line; + if (!parse_line.endsWith("\";")) { + int pos = stream.pos() - raw_line.length() - 1; + parse_line = content.mid(pos, content.indexOf("\";", pos)); + } + auto match = msg_comment_regexp.match(parse_line); + if (!match.hasMatch()) + throw std::runtime_error("Invalid message comment format"); + + if (auto m = (cabana::Msg *)msg(match.captured("address").toUInt())) + m->comment = match.captured("comment").trimmed().replace("\\\"", "\""); +} + +void DBCFile::parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt) { + static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); + static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); + + if (!current_msg) + throw std::runtime_error("No Message"); + + int offset = 0; + auto match = sg_regexp.match(line); + if (!match.hasMatch()) { + match = sgm_regexp.match(line); + offset = 1; + } + if (!match.hasMatch()) + throw std::runtime_error("Invalid SG_ line format"); + + QString name = match.captured(1); + if (current_msg->sig(name) != nullptr) + throw std::runtime_error("Duplicate signal name"); + + cabana::Signal s{}; + if (offset == 1) { + auto indicator = match.captured(2); + if (indicator == "M") { + ++multiplexor_cnt; + // Only one signal within a single message can be the multiplexer switch. + if (multiplexor_cnt >= 2) + throw std::runtime_error("Multiple multiplexor"); + + s.type = cabana::Signal::Type::Multiplexor; + } else { + s.type = cabana::Signal::Type::Multiplexed; + s.multiplex_value = indicator.mid(1).toInt(); + } + } + s.name = name; + s.start_bit = match.captured(offset + 2).toInt(); + s.size = match.captured(offset + 3).toInt(); + s.is_little_endian = match.captured(offset + 4).toInt() == 1; + s.is_signed = match.captured(offset + 5) == "-"; + s.factor = match.captured(offset + 6).toDouble(); + s.offset = match.captured(offset + 7).toDouble(); + s.min = match.captured(8 + offset).toDouble(); + s.max = match.captured(9 + offset).toDouble(); + s.unit = match.captured(10 + offset); + s.receiver_name = match.captured(11 + offset).trimmed(); + current_msg->sigs.push_back(new cabana::Signal(s)); +} + +void DBCFile::parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) { + static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)"); + + QString parse_line = line; + if (!parse_line.endsWith("\";")) { + int pos = stream.pos() - raw_line.length() - 1; + parse_line = content.mid(pos, content.indexOf("\";", pos)); + } + auto match = sg_comment_regexp.match(parse_line); + if (!match.hasMatch()) + throw std::runtime_error("Invalid CM_ SG_ line format"); + + if (auto s = signal(match.captured(1).toUInt(), match.captured(2))) { + s->comment = match.captured(3).trimmed().replace("\\\"", "\""); + } +} + +void DBCFile::parseVAL(const QString &line) { + static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))"); + + auto match = val_regexp.match(line); + if (!match.hasMatch()) + throw std::runtime_error("invalid VAL_ line format"); + + if (auto s = signal(match.captured(1).toUInt(), match.captured(2))) { + QStringList desc_list = match.captured(3).trimmed().split('"'); + for (int i = 0; i < desc_list.size(); i += 2) { + auto val = desc_list[i].trimmed(); + if (!val.isEmpty() && (i + 1) < desc_list.size()) { + auto desc = desc_list[i + 1].trimmed(); + s->val_desc.push_back({val.toDouble(), desc}); + } + } + } +} + QString DBCFile::generateDBC() { QString dbc_string, comment, val_desc; for (const auto &[address, m] : msgs) { diff --git a/tools/cabana/dbc/dbcfile.h b/tools/cabana/dbc/dbcfile.h index 29f19a80e4..20de04a861 100644 --- a/tools/cabana/dbc/dbcfile.h +++ b/tools/cabana/dbc/dbcfile.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "tools/cabana/dbc/dbc.h" @@ -26,6 +27,7 @@ public: cabana::Msg *msg(uint32_t address); cabana::Msg *msg(const QString &name); inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); } + cabana::Signal *signal(uint32_t address, const QString name); inline QString name() const { return name_.isEmpty() ? "untitled" : name_; } inline bool isEmpty() const { return msgs.empty() && name_.isEmpty(); } @@ -34,6 +36,12 @@ public: private: void parse(const QString &content); + cabana::Msg *parseBO(const QString &line); + void parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt); + void parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream); + void parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream); + void parseVAL(const QString &line); + QString header; std::map msgs; QString name_; diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 8c9ee922f8..1ac6e5bbad 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -53,7 +53,7 @@ public: std::function filter_cmp = nullptr; std::deque messages; std::vector sigs; - bool hex_mode = true; + bool hex_mode = false; }; class LogsWidget : public QFrame { diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 4c3efa0385..396cfdc38b 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -43,7 +43,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget view->setItemsExpandable(false); view->setIndentation(0); view->setRootIsDecorated(false); - view->setUniformRowHeights(!settings.multiple_lines_hex); // Must be called before setting any header parameters to avoid overriding restoreHeaderState(settings.message_header_state); @@ -135,15 +134,9 @@ void MessagesWidget::selectMessage(const MessageId &msg_id) { } void MessagesWidget::suppressHighlighted() { - if (sender() == suppress_add) { - size_t n = can->suppressHighlighted(); - suppress_clear->setText(tr("Clear (%1)").arg(n)); - suppress_clear->setEnabled(true); - } else { - can->clearSuppressed(); - suppress_clear->setText(tr("Clear")); - suppress_clear->setEnabled(false); - } + int n = sender() == suppress_add ? can->suppressHighlighted() : (can->clearSuppressed(), 0); + suppress_clear->setText(n > 0 ? tr("Clear (%1)").arg(n) : tr("Clear")); + suppress_clear->setEnabled(n > 0); } void MessagesWidget::headerContextMenuEvent(const QPoint &pos) { @@ -174,7 +167,6 @@ void MessagesWidget::menuAboutToShow() { void MessagesWidget::setMultiLineBytes(bool multi) { settings.multiple_lines_hex = multi; delegate->setMultipleLines(multi); - view->setUniformRowHeights(!multi); view->updateBytesSectionSize(); view->doItemsLayout(); } @@ -207,22 +199,22 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } }; + const static QString NA = QStringLiteral("N/A"); const auto &item = items_[index.row()]; - const auto &data = can->lastMessage(item.id); if (role == Qt::DisplayRole) { switch (index.column()) { case Column::NAME: return item.name; - case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A"; + case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : NA; case Column::ADDRESS: return QString::number(item.id.address, 16); case Column::NODE: return item.node; - case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(data.freq) : "N/A"; - case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(data.count) : "N/A"; - case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A"; + case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(can->lastMessage(item.id).freq) : NA; + case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(can->lastMessage(item.id).count) : NA; + case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : NA; } } else if (role == ColorsRole) { - return QVariant::fromValue((void*)(&data.colors)); + return QVariant::fromValue((void*)(&can->lastMessage(item.id).colors)); } else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) { - return QVariant::fromValue((void*)(&data.dat)); + return QVariant::fromValue((void*)(&can->lastMessage(item.id).dat)); } else if (role == Qt::ForegroundRole && !item.active) { return settings.theme == DARK_THEME ? QApplication::palette().color(QPalette::Text).darker(150) : QColor(Qt::gray); } else if (role == Qt::ToolTipRole && index.column() == Column::NAME) { @@ -366,18 +358,17 @@ bool MessageListModel::filterAndSort() { } void MessageListModel::msgsReceived(const std::set *new_msgs, bool has_new_ids) { - if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) { + if (has_new_ids || ((filters_.count(Column::FREQ) || filters_.count(Column::COUNT) || filters_.count(Column::DATA)) && + ++sort_threshold_ == settings.fps)) { + sort_threshold_ = 0; if (filterAndSort()) return; } - for (int i = 0; i < items_.size(); ++i) { - auto &item = items_[i]; - bool prev_active = item.active; + + for (auto &item : items_) { item.active = isMessageActive(item.id); - if (item.active != prev_active || !new_msgs || new_msgs->count(item.id)) { - for (int col = 0; col < columnCount(); ++col) - emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); - } } + // Update viewport + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } void MessageListModel::sort(int column, Qt::SortOrder order) { @@ -420,6 +411,7 @@ void MessageView::updateBytesSectionSize() { max_bytes = std::max(max_bytes, m.dat.size()); } } + setUniformRowHeights(!delegate->multipleLines() || max_bytes <= 8); header()->resizeSection(MessageListModel::Column::DATA, delegate->sizeForBytes(max_bytes).width()); } diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index c110db2b56..823fcb74e3 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -61,6 +61,7 @@ private: std::set dbc_messages_; int sort_column = 0; Qt::SortOrder sort_order = Qt::AscendingOrder; + int sort_threshold_ = 0; }; class MessageView : public QTreeView { diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index b63789a026..523cbf3be7 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -12,6 +12,9 @@ #include "tools/cabana/utils/util.h" +const int MIN_CACHE_MINIUTES = 30; +const int MAX_CACHE_MINIUTES = 120; + Settings settings; template @@ -72,7 +75,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { fps->setValue(settings.fps); form_layout->addRow(tr("Max Cached Minutes"), cached_minutes = new QSpinBox(this)); - cached_minutes->setRange(5, 60); + cached_minutes->setRange(MIN_CACHE_MINIUTES, MAX_CACHE_MINIUTES); cached_minutes->setSingleStep(1); cached_minutes->setValue(settings.max_cached_minutes); main_layout->addWidget(groupbox); diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 9c52908c36..593d1bf5d8 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -130,6 +130,7 @@ void AbstractStream::updateLastMsgsTo(double sec) { current_sec_ = sec; uint64_t last_ts = (sec + routeStartTime()) * 1e9; std::unordered_map msgs; + msgs.reserve(events_.size()); for (const auto &[id, ev] : events_) { auto it = std::upper_bound(ev.begin(), ev.end(), last_ts, CompareCanEvent()); @@ -171,6 +172,8 @@ const CanEvent *AbstractStream::newEvent(uint64_t mono_time, const cereal::CanDa void AbstractStream::mergeEvents(const std::vector &events) { static MessageEventsMap msg_events; std::for_each(msg_events.begin(), msg_events.end(), [](auto &e) { e.second.clear(); }); + + // Group events by message ID for (auto e : events) { msg_events[{.source = e->src, .address = e->address}].push_back(e); } @@ -207,7 +210,7 @@ inline QColor blend(const QColor &a, const QColor &b) { return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); } -// Calculate the frequency of the past minute. +// Calculate the frequency from the past one minute data double calc_freq(const MessageId &msg_id, double current_sec) { const auto &events = can->events(msg_id); uint64_t cur_mono_time = (can->routeStartTime() + current_sec) * 1e9; @@ -255,11 +258,8 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in if (last != cur) { const int delta = cur - last; // Keep track if signal is changing randomly, or mostly moving in the same direction - if (std::signbit(delta) == std::signbit(last_change.delta)) { - last_change.same_delta_counter = std::min(16, last_change.same_delta_counter + 1); - } else { - last_change.same_delta_counter = std::max(0, last_change.same_delta_counter - 4); - } + last_change.same_delta_counter += std::signbit(delta) == std::signbit(last_change.delta) ? 1 : -4; + last_change.same_delta_counter = std::clamp(last_change.same_delta_counter, 0, 16); const double delta_t = ts - last_change.ts; // Mostly moves in the same direction, color based on delta up/down @@ -272,10 +272,10 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in } // Track bit level changes - const uint8_t tmp = (cur ^ last); + const uint8_t diff = (cur ^ last); for (int bit = 0; bit < 8; bit++) { - if (tmp & (1 << (7 - bit))) { - last_change.bit_change_counts[bit] += 1; + if (diff & (1u << bit)) { + ++last_change.bit_change_counts[7 - bit]; } } diff --git a/tools/cabana/utils/util.cc b/tools/cabana/utils/util.cc index f85ea6d105..4556f90850 100644 --- a/tools/cabana/utils/util.cc +++ b/tools/cabana/utils/util.cc @@ -51,7 +51,8 @@ std::pair SegmentTree::get_minmax(int n, int left, int right, in // MessageBytesDelegate -MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines) : multiple_lines(multiple_lines), QStyledItemDelegate(parent) { +MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines) + : font_metrics(QApplication::font()), multiple_lines(multiple_lines), QStyledItemDelegate(parent) { fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); byte_size = QFontMetrics(fixed_font).size(Qt::TextSingleLine, "00 ") + QSize(0, 2); for (int i = 0; i < 256; ++i) { @@ -73,31 +74,38 @@ QSize MessageBytesDelegate::sizeHint(const QStyleOptionViewItem &option, const Q } void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - auto data = index.data(BytesRole); - if (!data.isValid()) { - return QStyledItemDelegate::paint(painter, option, index); - } - - QFont old_font = painter->font(); - QPen old_pen = painter->pen(); if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight)); } - const QPoint pt{option.rect.left() + h_margin, option.rect.top() + v_margin}; - painter->setFont(fixed_font); - const auto &bytes = *static_cast*>(data.value()); - const auto &colors = *static_cast*>(index.data(ColorsRole).value()); + QRect item_rect = option.rect.adjusted(h_margin, v_margin, -h_margin, -v_margin); + QColor highlighted_color = option.palette.color(QPalette::HighlightedText); auto text_color = index.data(Qt::ForegroundRole).value(); bool inactive = text_color.isValid(); if (!inactive) { text_color = option.palette.color(QPalette::Text); } + auto data = index.data(BytesRole); + if (!data.isValid()) { + painter->setFont(option.font); + painter->setPen(option.state & QStyle::State_Selected ? highlighted_color : text_color); + QString text = font_metrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, item_rect.width()); + painter->drawText(item_rect, Qt::AlignLeft | Qt::AlignVCenter, text); + return; + } + // Paint hex column + const auto &bytes = *static_cast *>(data.value()); + const auto &colors = *static_cast *>(index.data(ColorsRole).value()); + + painter->setFont(fixed_font); + const QPen text_pen(option.state & QStyle::State_Selected ? highlighted_color : text_color); + const QPoint pt = item_rect.topLeft(); for (int i = 0; i < bytes.size(); ++i) { int row = !multiple_lines ? 0 : i / 8; int column = !multiple_lines ? i : i % 8; - QRect r = QRect({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size); + QRect r({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size); + if (!inactive && i < colors.size() && colors[i].alpha() > 0) { if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(QPalette::Text)); @@ -105,12 +113,10 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & } painter->fillRect(r, colors[i]); } else { - painter->setPen(option.state & QStyle::State_Selected ? option.palette.color(QPalette::HighlightedText) : text_color); + painter->setPen(text_pen); } utils::drawStaticText(painter, r, hex_text_table[bytes[i]]); } - painter->setFont(old_font); - painter->setPen(old_pen); } // TabBar diff --git a/tools/cabana/utils/util.h b/tools/cabana/utils/util.h index 218b8eeb51..0ebd7e41f8 100644 --- a/tools/cabana/utils/util.h +++ b/tools/cabana/utils/util.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ public: private: std::array hex_text_table; + QFontMetrics font_metrics; QFont fixed_font; QSize byte_size = {}; bool multiple_lines = false; diff --git a/tools/install_python_dependencies.sh b/tools/install_python_dependencies.sh index f7ba316480..df815b582f 100755 --- a/tools/install_python_dependencies.sh +++ b/tools/install_python_dependencies.sh @@ -54,8 +54,8 @@ fi eval "$(pyenv init --path)" echo "update pip" -pip install pip==23.3 -pip install poetry==1.6.1 +pip install pip==24.0 +pip install poetry==1.7.0 poetry config virtualenvs.prefer-active-python true --local poetry config virtualenvs.in-project true --local diff --git a/tools/replay/framereader.cc b/tools/replay/framereader.cc index fb70a82c40..26ef4684a4 100644 --- a/tools/replay/framereader.cc +++ b/tools/replay/framereader.cc @@ -1,5 +1,10 @@ #include "tools/replay/framereader.h" +#include +#include +#include +#include + #include "common/util.h" #include "third_party/libyuv/include/libyuv.h" #include "tools/replay/util.h" @@ -12,7 +17,6 @@ #define HW_PIX_FMT AV_PIX_FMT_CUDA #endif - namespace { enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { @@ -21,11 +25,32 @@ enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat * if (*p == *hw_pix_fmt) return *p; } rWarning("Please run replay with the --no-hw-decoder flag!"); - // fallback to YUV420p *hw_pix_fmt = AV_PIX_FMT_NONE; return AV_PIX_FMT_YUV420P; } +struct DecoderManager { + VideoDecoder *acquire(CameraType type, AVCodecParameters *codecpar, bool hw_decoder) { + auto key = std::tuple(type, codecpar->width, codecpar->height); + std::unique_lock lock(mutex_); + if (auto it = decoders_.find(key); it != decoders_.end()) { + return it->second.get(); + } + + auto decoder = std::make_unique(); + if (!decoder->open(codecpar, hw_decoder)) { + decoder.reset(nullptr); + } + decoders_[key] = std::move(decoder); + return decoders_[key].get(); + } + + std::mutex mutex_; + std::map, std::unique_ptr> decoders_; +}; + +DecoderManager decoder_manager; + } // namespace FrameReader::FrameReader() { @@ -33,12 +58,10 @@ FrameReader::FrameReader() { } FrameReader::~FrameReader() { - if (decoder_ctx) avcodec_free_context(&decoder_ctx); if (input_ctx) avformat_close_input(&input_ctx); - if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); } -bool FrameReader::load(const std::string &url, bool no_hw_decoder, std::atomic *abort, bool local_cache, int chunk_size, int retries) { +bool FrameReader::load(CameraType type, const std::string &url, bool no_hw_decoder, std::atomic *abort, bool local_cache, int chunk_size, int retries) { auto local_file_path = url.find("https://") == 0 ? cacheFilePath(url) : url; if (!util::file_exists(local_file_path)) { FileReader f(local_cache, chunk_size, retries); @@ -46,10 +69,10 @@ bool FrameReader::load(const std::string &url, bool no_hw_decoder, std::atomic *abort) { +bool FrameReader::loadFromFile(CameraType type, const std::string &file, bool no_hw_decoder, std::atomic *abort) { if (avformat_open_input(&input_ctx, file.c_str(), nullptr, nullptr) != 0 || avformat_find_stream_info(input_ctx, nullptr) < 0) { rError("Failed to open input file or find video stream"); @@ -57,27 +80,12 @@ bool FrameReader::loadFromFile(const std::string &file, bool no_hw_decoder, std: } input_ctx->probesize = 10 * 1024 * 1024; // 10MB - AVStream *video = input_ctx->streams[0]; - const AVCodec *decoder = avcodec_find_decoder(video->codecpar->codec_id); - if (!decoder) return false; - - decoder_ctx = avcodec_alloc_context3(decoder); - if (!decoder_ctx || avcodec_parameters_to_context(decoder_ctx, video->codecpar) != 0) { - rError("Failed to allocate or initialize codec context"); - return false; - } - - width = (decoder_ctx->width + 3) & ~3; - height = decoder_ctx->height; - - if (has_hw_decoder && !no_hw_decoder && !initHardwareDecoder(HW_DEVICE_TYPE)) { - rWarning("No device with hardware decoder found. fallback to CPU decoding."); - } - - if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) { - rError("Failed to open codec"); + decoder_ = decoder_manager.acquire(type, input_ctx->streams[0]->codecpar, !no_hw_decoder); + if (!decoder_) { return false; } + width = decoder_->width; + height = decoder_->height; AVPacket pkt; packets_info.reserve(60 * 20); // 20fps, one minute @@ -85,29 +93,70 @@ bool FrameReader::loadFromFile(const std::string &file, bool no_hw_decoder, std: packets_info.emplace_back(PacketInfo{.flags = pkt.flags, .pos = pkt.pos}); av_packet_unref(&pkt); } - avio_seek(input_ctx->pb, 0, SEEK_SET); return !packets_info.empty(); } -bool FrameReader::initHardwareDecoder(AVHWDeviceType hw_device_type) { - for (int i = 0;; i++) { - const AVCodecHWConfig *config = avcodec_get_hw_config(decoder_ctx->codec, i); - if (!config) { - rWarning("decoder %s does not support hw device type %s.", decoder_ctx->codec->name, - av_hwdevice_get_type_name(hw_device_type)); - return false; - } +bool FrameReader::get(int idx, VisionBuf *buf) { + if (!buf || idx < 0 || idx >= packets_info.size()) { + return false; + } + return decoder_->decode(this, idx, buf); +} + +// class VideoDecoder + +VideoDecoder::VideoDecoder() { + av_frame_ = av_frame_alloc(); + hw_frame_ = av_frame_alloc(); +} + +VideoDecoder::~VideoDecoder() { + if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); + if (decoder_ctx) avcodec_free_context(&decoder_ctx); + av_frame_free(&av_frame_); + av_frame_free(&hw_frame_); +} + +bool VideoDecoder::open(AVCodecParameters *codecpar, bool hw_decoder) { + const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id); + if (!decoder) return false; + + decoder_ctx = avcodec_alloc_context3(decoder); + if (!decoder_ctx || avcodec_parameters_to_context(decoder_ctx, codecpar) != 0) { + rError("Failed to allocate or initialize codec context"); + return false; + } + width = (decoder_ctx->width + 3) & ~3; + height = decoder_ctx->height; + + if (hw_decoder && !initHardwareDecoder(HW_DEVICE_TYPE)) { + rWarning("No device with hardware decoder found. fallback to CPU decoding."); + } + + if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) { + rError("Failed to open codec"); + return false; + } + return true; +} + +bool VideoDecoder::initHardwareDecoder(AVHWDeviceType hw_device_type) { + const AVCodecHWConfig *config = nullptr; + for (int i = 0; (config = avcodec_get_hw_config(decoder_ctx->codec, i)) != nullptr; i++) { if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == hw_device_type) { hw_pix_fmt = config->pix_fmt; break; } } + if (!config) { + rWarning("Hardware configuration not found"); + return false; + } int ret = av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, nullptr, nullptr, 0); if (ret < 0) { hw_pix_fmt = AV_PIX_FMT_NONE; - has_hw_decoder = false; rWarning("Failed to create specified HW device %d.", ret); return false; } @@ -118,34 +167,27 @@ bool FrameReader::initHardwareDecoder(AVHWDeviceType hw_device_type) { return true; } -bool FrameReader::get(int idx, VisionBuf *buf) { - if (!buf || idx < 0 || idx >= packets_info.size()) { - return false; - } - return decode(idx, buf); -} - -bool FrameReader::decode(int idx, VisionBuf *buf) { +bool VideoDecoder::decode(FrameReader *reader, int idx, VisionBuf *buf) { int from_idx = idx; - if (idx != prev_idx + 1) { + if (idx != reader->prev_idx + 1) { // seeking to the nearest key frame for (int i = idx; i >= 0; --i) { - if (packets_info[i].flags & AV_PKT_FLAG_KEY) { + if (reader->packets_info[i].flags & AV_PKT_FLAG_KEY) { from_idx = i; break; } } - avio_seek(input_ctx->pb, packets_info[from_idx].pos, SEEK_SET); + avio_seek(reader->input_ctx->pb, reader->packets_info[from_idx].pos, SEEK_SET); } - prev_idx = idx; + reader->prev_idx = idx; bool result = false; AVPacket pkt; for (int i = from_idx; i <= idx; ++i) { - if (av_read_frame(input_ctx, &pkt) == 0) { + if (av_read_frame(reader->input_ctx, &pkt) == 0) { AVFrame *f = decodeFrame(&pkt); if (f && i == idx) { - result = copyBuffers(f, buf); + result = copyBuffer(f, buf); } av_packet_unref(&pkt); } @@ -153,33 +195,27 @@ bool FrameReader::decode(int idx, VisionBuf *buf) { return result; } -AVFrame *FrameReader::decodeFrame(AVPacket *pkt) { +AVFrame *VideoDecoder::decodeFrame(AVPacket *pkt) { int ret = avcodec_send_packet(decoder_ctx, pkt); if (ret < 0) { rError("Error sending a packet for decoding: %d", ret); return nullptr; } - av_frame_.reset(av_frame_alloc()); - ret = avcodec_receive_frame(decoder_ctx, av_frame_.get()); + ret = avcodec_receive_frame(decoder_ctx, av_frame_); if (ret != 0) { rError("avcodec_receive_frame error: %d", ret); return nullptr; } - if (av_frame_->format == hw_pix_fmt) { - hw_frame.reset(av_frame_alloc()); - if ((ret = av_hwframe_transfer_data(hw_frame.get(), av_frame_.get(), 0)) < 0) { - rError("error transferring the data from GPU to CPU"); - return nullptr; - } - return hw_frame.get(); - } else { - return av_frame_.get(); + if (av_frame_->format == hw_pix_fmt && av_hwframe_transfer_data(hw_frame_, av_frame_, 0) < 0) { + rError("error transferring frame data from GPU to CPU"); + return nullptr; } + return (av_frame_->format == hw_pix_fmt) ? hw_frame_ : av_frame_; } -bool FrameReader::copyBuffers(AVFrame *f, VisionBuf *buf) { +bool VideoDecoder::copyBuffer(AVFrame *f, VisionBuf *buf) { if (hw_pix_fmt == HW_PIX_FMT) { for (int i = 0; i < height/2; i++) { memcpy(buf->y + (i*2 + 0)*buf->stride, f->data[0] + (i*2 + 0)*f->linesize[0], width); diff --git a/tools/replay/framereader.h b/tools/replay/framereader.h index a25b508cc7..b9abefb7c3 100644 --- a/tools/replay/framereader.h +++ b/tools/replay/framereader.h @@ -1,10 +1,10 @@ #pragma once -#include #include #include #include "cereal/visionipc/visionbuf.h" +#include "system/camerad/cameras/camera_common.h" #include "tools/replay/filereader.h" extern "C" { @@ -12,39 +12,46 @@ extern "C" { #include } -struct AVFrameDeleter { - void operator()(AVFrame* frame) const { av_frame_free(&frame); } -}; +class VideoDecoder; class FrameReader { public: FrameReader(); ~FrameReader(); - bool load(const std::string &url, bool no_hw_decoder = false, std::atomic *abort = nullptr, bool local_cache = false, + bool load(CameraType type, const std::string &url, bool no_hw_decoder = false, std::atomic *abort = nullptr, bool local_cache = false, int chunk_size = -1, int retries = 0); - bool loadFromFile(const std::string &file, bool no_hw_decoder = false, std::atomic *abort = nullptr); + bool loadFromFile(CameraType type, const std::string &file, bool no_hw_decoder = false, std::atomic *abort = nullptr); bool get(int idx, VisionBuf *buf); size_t getFrameCount() const { return packets_info.size(); } int width = 0, height = 0; -private: - bool initHardwareDecoder(AVHWDeviceType hw_device_type); - bool decode(int idx, VisionBuf *buf); - AVFrame * decodeFrame(AVPacket *pkt); - bool copyBuffers(AVFrame *f, VisionBuf *buf); - - std::unique_ptrav_frame_, hw_frame; + VideoDecoder *decoder_ = nullptr; AVFormatContext *input_ctx = nullptr; - AVCodecContext *decoder_ctx = nullptr; - - AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; - AVBufferRef *hw_device_ctx = nullptr; int prev_idx = -1; struct PacketInfo { int flags; int64_t pos; }; std::vector packets_info; - inline static std::atomic has_hw_decoder = true; +}; + + +class VideoDecoder { +public: + VideoDecoder(); + ~VideoDecoder(); + bool open(AVCodecParameters *codecpar, bool hw_decoder); + bool decode(FrameReader *reader, int idx, VisionBuf *buf); + int width = 0, height = 0; + +private: + bool initHardwareDecoder(AVHWDeviceType hw_device_type); + AVFrame *decodeFrame(AVPacket *pkt); + bool copyBuffer(AVFrame *f, VisionBuf *buf); + + AVFrame *av_frame_, *hw_frame_; + AVCodecContext *decoder_ctx = nullptr; + AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; + AVBufferRef *hw_device_ctx = nullptr; }; diff --git a/tools/replay/route.cc b/tools/replay/route.cc index 1c7010eb8a..e8e73459ea 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -160,7 +160,7 @@ void Segment::loadFile(int id, const std::string file) { bool success = false; if (id < MAX_CAMERAS) { frames[id] = std::make_unique(); - success = frames[id]->load(file, flags & REPLAY_FLAG_NO_HW_DECODER, &abort_, local_cache, 20 * 1024 * 1024, 3); + success = frames[id]->load((CameraType)id, file, flags & REPLAY_FLAG_NO_HW_DECODER, &abort_, local_cache, 20 * 1024 * 1024, 3); } else { log = std::make_unique(filters_); success = log->load(file, &abort_, local_cache, 0, 3);