#pragma once #include #include #include "cereal/messaging/messaging.h" #include "cereal/services.h" #include "msgq/visionipc/visionipc_client.h" #include "system/hardware/hw.h" #include "common/params.h" #include "common/swaglog.h" #include "common/util.h" #include "system/loggerd/logger.h" constexpr int MAIN_FPS = 20; const auto MAIN_ENCODE_TYPE = Hardware::PC() ? cereal::EncodeIndex::Type::BIG_BOX_LOSSLESS : cereal::EncodeIndex::Type::FULL_H_E_V_C; #define NO_CAMERA_PATIENCE 500 // fall back to time-based rotation if all cameras are dead #define INIT_ENCODE_FUNCTIONS(encode_type) \ .get_encode_data_func = &cereal::Event::Reader::get##encode_type##Data, \ .set_encode_idx_func = &cereal::Event::Builder::set##encode_type##Idx, \ .init_encode_data_func = &cereal::Event::Builder::init##encode_type##Data const bool LOGGERD_TEST = getenv("LOGGERD_TEST"); const int SEGMENT_LENGTH = LOGGERD_TEST ? atoi(getenv("LOGGERD_SEGMENT_LENGTH")) : 60; constexpr char PRESERVE_ATTR_NAME[] = "user.preserve"; constexpr char PRESERVE_ATTR_VALUE = '1'; constexpr int QCAM_WIDTH = 1316; constexpr int QCAM_HEIGHT = 826; constexpr int MICI_QCAM_WIDTH = 1210; constexpr int MICI_QCAM_HEIGHT = 760; static_assert(QCAM_WIDTH % 2 == 0 && QCAM_HEIGHT % 2 == 0 && MICI_QCAM_WIDTH % 2 == 0 && MICI_QCAM_HEIGHT % 2 == 0, "qcamera dimensions must be even"); inline bool is_mici() { return Hardware::get_device_type() == cereal::InitData::DeviceType::MICI; } struct EncoderSettings { cereal::EncodeIndex::Type encode_type; int bitrate; int gop_size; int b_frames = 0; // we don't use b frames static EncoderSettings MainEncoderSettings(int in_width) { if (in_width <= 1344) { return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 5'000'000, .gop_size = 20}; } else { return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 10'000'000, .gop_size = 30}; } } static EncoderSettings QcamEncoderSettings() { // Keep Konn3kt route uploads useful without turning each 60-second qcamera // segment into a 26 MB file. This is still a substantial improvement over // stock qcam, while CBR keeps storage and upload usage predictable. int _qcam_bitrate = getenv("QCAM_BITRATE") ? atoi(getenv("QCAM_BITRATE")) : 2'400'000; return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = _qcam_bitrate, .gop_size = 15}; } static EncoderSettings StreamEncoderSettings() { // Startup bitrate for the livestream encoder. The adaptive bitrate controller // (webrtcd LivestreamBitrateController) overrides this at runtime via the // LivestreamEncoderBitrate param based on measured RTCP loss/RTT. Start at a // conservative mid level so a poor link doesn't choke the first seconds, then ramp up. int _stream_bitrate = getenv("STREAM_BITRATE") ? atoi(getenv("STREAM_BITRATE")) : 1'500'000; return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = _stream_bitrate , .gop_size = 30}; } }; class EncoderInfo { public: const char *publish_name; const char *thumbnail_name = NULL; const char *filename = NULL; bool record = true; bool include_audio = false; bool adaptive_bitrate = false; // allow runtime bitrate updates (livestream ABR) bool cbr = false; // force constant bitrate so file size = bitrate * duration (qcamera) int frame_width = -1; int frame_height = -1; int fps = MAIN_FPS; std::function get_settings; ::cereal::EncodeData::Reader (cereal::Event::Reader::*get_encode_data_func)() const; void (cereal::Event::Builder::*set_encode_idx_func)(::cereal::EncodeIndex::Reader); cereal::EncodeData::Builder (cereal::Event::Builder::*init_encode_data_func)(); }; class LogCameraInfo { public: const char *thread_name; int fps = MAIN_FPS; VisionStreamType stream_type; std::vector encoder_infos; }; const EncoderInfo main_road_encoder_info = { .publish_name = "roadEncodeData", .thumbnail_name = "thumbnail", .filename = "fcamera.hevc", .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(RoadEncode), }; const EncoderInfo main_wide_road_encoder_info = { .publish_name = "wideRoadEncodeData", .filename = "ecamera.hevc", .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(WideRoadEncode), }; const EncoderInfo main_driver_encoder_info = { .publish_name = "driverEncodeData", .filename = "dcamera.hevc", .record = Params().getBool("RecordFront"), .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(DriverEncode), }; const EncoderInfo stream_road_encoder_info = { .publish_name = "livestreamRoadEncodeData", //.thumbnail_name = "thumbnail", .record = false, .adaptive_bitrate = true, .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamRoadEncode), }; const EncoderInfo stream_wide_road_encoder_info = { .publish_name = "livestreamWideRoadEncodeData", .record = false, .adaptive_bitrate = true, .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamWideRoadEncode), }; const EncoderInfo stream_driver_encoder_info = { .publish_name = "livestreamDriverEncodeData", .record = false, .adaptive_bitrate = true, .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamDriverEncode), }; const EncoderInfo qcam_encoder_info = { .publish_name = "qRoadEncodeData", .filename = "qcamera.ts", .cbr = true, // enforce the bitrate so upload size stays predictable (no VBR overshoot) .get_settings = [](int){return EncoderSettings::QcamEncoderSettings();}, .frame_width = is_mici() ? MICI_QCAM_WIDTH : QCAM_WIDTH, .frame_height = is_mici() ? MICI_QCAM_HEIGHT : QCAM_HEIGHT, .include_audio = Params().getBool("RecordAudio"), INIT_ENCODE_FUNCTIONS(QRoadEncode), }; const LogCameraInfo road_camera_info{ .thread_name = "road_cam_encoder", .stream_type = VISION_STREAM_ROAD, .encoder_infos = {main_road_encoder_info, qcam_encoder_info} }; const LogCameraInfo wide_road_camera_info{ .thread_name = "wide_road_cam_encoder", .stream_type = VISION_STREAM_WIDE_ROAD, .encoder_infos = {main_wide_road_encoder_info} }; const LogCameraInfo driver_camera_info{ .thread_name = "driver_cam_encoder", .stream_type = VISION_STREAM_DRIVER, .encoder_infos = {main_driver_encoder_info} }; const LogCameraInfo stream_road_camera_info{ .thread_name = "road_cam_encoder", .stream_type = VISION_STREAM_ROAD, .encoder_infos = {stream_road_encoder_info} }; const LogCameraInfo stream_wide_road_camera_info{ .thread_name = "wide_road_cam_encoder", .stream_type = VISION_STREAM_WIDE_ROAD, .encoder_infos = {stream_wide_road_encoder_info} }; const LogCameraInfo stream_driver_camera_info{ .thread_name = "driver_cam_encoder", .stream_type = VISION_STREAM_DRIVER, .encoder_infos = {stream_driver_encoder_info} }; const LogCameraInfo cameras_logged[] = {road_camera_info, wide_road_camera_info, driver_camera_info}; const LogCameraInfo stream_cameras_logged[] = {stream_road_camera_info, stream_wide_road_camera_info, stream_driver_camera_info};