🌐 English

    ADAS API#

    Oasis supports ADAS functions such as lane detection, forward vehicle departure, forward collision warning, lane departure, etc..

    Header File#

    OasisMedia.h

    Description#

    Activate ADAS by specifying the channel<N>-enable-adas and channel<N>-adas-inference-model-names key values when creating a Combined Recording or Individual Recording object.

    While driving, vehicle speed is reflected in real-time by calling the setVehicleSpeed and setMultiChannelVehicleSpeed functions.

    You can use ADAS after loading the model file by calling createInferenceEngine.

    Example#

    Below is an example of creating a combined recording object and using ADAS for one channel.

    Initializes the Oasis Inference Engine. nef_path is the model file path. The model file is loaded and used on the NPU of each chipset.

    parameters["model-path"] = nef_path;
    createInferenceEngine(parameters);
    

    Activates ADAS in the parameters when creating a combined recording object.

    parameters["channel-count"] = "1";
    
    parameters["channel1-camera-id"] = std::to_string(camera_id);
    parameters["channel1-ise-id"] = "-1";
    parameters["channel1-resolution"] = "1080p";
    parameters["channel1-bitrate"] = "8000000";
    parameters["channel1-fps"] = "30";
    parameters["channel1-file-framerate"] = "30";
    parameters["channel1-venc-framerate"] = "30";
    parameters["channel1-venc-keyframe-interval"] = "30";
    parameters["channel1-vencoder-type"] = "h265";
    parameters["channel1-h264-profile"] = "main";
    
    parameters["channel1-enable-adas"] = "1";
    parameters["channel1-adas-inference-model-names"] = "ufld,yolov5";
    

    Defines ADAS-related callback functions in a user-defined interface derived from MediaObserver.

    virtual void onAdasForwardVehicleCollisionWarning(void *user_data) {
      fprintf(stdout, ">>> Adas collision warning!\n");
    }
    
    virtual void onAdasForwardVehicleDepartueWarning(void *user_data) {
      fprintf(stdout, ">>> Adas vehicle departure warning!\n");
    }
    
    virtual void onAdasLaneDepartureWarning(void *user_data, int32_t land_side) {
      fprintf(stdout, ">>> Adas %s lane departure warning!\n", land_side==0?"left":"right");
    }
    
    virtual void onAdasForwardPedestrianWarning(void *user_data) {
      fprintf(stdout, ">>> Adas pedestrian warning!\n");
    }
    

    Periodically sets the speed in the timer thread. In actual use, obtain the speed from data such as GPS and set it.

    setVehicleSpeed(recorder, 60.f);
    

    Below is the complete code.

    #include "OasisAPI.h"
    #include "OasisLog.h"
    #include "OasisMedia.h"
    #include "OasisFS.h"
    #include "OasisUtil.h"
    #include "OasisMotion.h"
    #include "OasisInference.h"
    
    #include "common.h"
    
    
    #include <thread>
    #include <mutex>
    #include <memory>
    #include <condition_variable>
    
    #include <signal.h>
    
    #define DLOG_THIS   0x00010000
    #define DLOG_RECORD 0x00020000
    #define DLOG_FRAME  0x00040000
    #define DLOG_TRACE  0x00080000
    
    #undef DLOG_FLAGS
    #define  DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_RECORD|DLOG_TRACE|DLOG_THIS/*|DLOG_FRAME*/)
    
    #undef DLOG_TAG
    #define DLOG_TAG "ADAS"
    
    #define OFFS_QUEUE_SIZE_KBYTES  (20*1024)
    #define OFFS_CACHE_SIZE_KBYTES  (1024)
    #define OFFS_WRITE_ALIGNMENT_BYTES  8192
    #define MEDIA_CACHE_SIZE_KBYTES (40*1024)
    
    
    extern "C" int getch(const char *prompt);
    
    using namespace oasis;
    
    static bool timer_running = true;
    static bool continue_recording = true;
    
    enum {
      kStartEventRecording = 1,
      kRecorderExitNow = 2,
    };
    
    struct RecorderCommand {
      int32_t cmd_;
      RecorderCommand() = default;
      RecorderCommand(int32_t cmd) : cmd_(cmd) {}
      RecorderCommand(const RecorderCommand& src) : cmd_(src.cmd_) {}
      RecorderCommand& operator=(const RecorderCommand& src) {
        if(this != &src) {
          cmd_ = src.cmd_;
        }
        return *this;
      }
    };
    
    
    static std::condition_variable cmdq_cond_;
    static std::mutex cmdq_lock_;
    static std::list<RecorderCommand> cmdq_;
    
    
    static void postCommand(int32_t cmd) {
      try {
        std::lock_guard<std::mutex> _l(cmdq_lock_);
        cmdq_.emplace_back(cmd);
        cmdq_cond_.notify_one();
      } catch(...) {
    
      }
    }
    
    static uint8_t strd_data_header_[24] = {0, };
    
    class MyRecordingObserver : public MediaObserver {
      public:
      virtual void onStarted(void* user_data, const char* file_path) {
        DLOG(DLOG_RECORD|DLOG_INFO, "Recording started, file path \"%s\"\r\n", file_path);
      }
    
      virtual void onStopped(void* user_data, media_report_reason_t reason, void* details) {
        DLOG(DLOG_RECORD|DLOG_INFO, "Recording stopped, reason: %d(%s)\r\n", reason, mediaReportReasonString(reason));
        if(reason != kMediaReportNoError) {
          continue_recording = false;
        }
      }
    
      virtual void onFileChanged(void* user_data, const char* new_file_path) {
        DLOG(DLOG_RECORD|DLOG_INFO, "New file used: \"%s\"\r\n", new_file_path);
      }
    
      virtual void onFileDeleted(void* user_data, const char* deleted_file_path) {
        DLOG(DLOG_RECORD|DLOG_INFO, "Oldest file used: \"%s\"\r\n", deleted_file_path);
      }
    
      virtual void onError(void* user_data, media_report_reason_t reason, void* details) {
        if(reason == kRecordingErrorMediaDataTimeout) {
          int32_t camera_id = (intptr_t)details;
          DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s), camera<%d>\r\n", reason, mediaReportReasonString(reason), camera_id);
        } else if(reason == kRecordingErrorMediaCacheReadErrorBegin) {
          DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s) ===> stopping\r\n", reason, mediaReportReasonString(reason));
        } else if(reason == kRecordingErrorMediaCacheReadErrorEnd) {
          DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s)\r\n", reason, mediaReportReasonString(reason));
        }
    
        continue_recording = false;
      }
    
      virtual void onInfo(void* user_data, MediaInfo* info) {
        DLOG(DLOG_RECORD, "Recording duration %lld.%06lld sec\r\n", info->durationUs / 1000000ll, info->durationUs % 1000000ll);
      }
    
      virtual void onInfoEx(void* user_data, MediaInfoEx* info) {
        dumpInfoEx(info);
      }
    
      //player paused and resumed
      virtual void onPaused(void *user_data, MediaInfo *info) {}
      virtual void onResumed(void *user_data, MediaInfo *info) {}
      virtual void onEventRecordingStarted(void* user_data, media_report_reason_t reason, const char* file_path) {
        DLOG0(DLOG_THIS, "Event Recording started @ \"%s\" (reason: %d %s).\r\n", file_path, reason, mediaReportReasonString(reason));
      }
      virtual void onEventRecordingCompleted(void* user_data, media_report_reason_t reason, const char* file_path) {
        DLOG0(DLOG_THIS, "Event Recording completed @ \"%s\" (reason: %d %s).\r\n", file_path, reason, mediaReportReasonString(reason));
      }
      virtual void onMotionRecordingStarted(void *user_data, media_report_reason_t reason, const char *file_path) {
        DLOG0(DLOG_THIS, "Motion Recording started @ \"%s\" (reason: %d %s).\r\n", file_path, reason, mediaReportReasonString(reason));
      }
      virtual void onMotionRecordingCompleted(void *user_data, media_report_reason_t reason, const char *file_path) {
        DLOG0(DLOG_THIS, "Motion Recording completed @ \"%s\" (reason: %d %s).\r\n", file_path, reason, mediaReportReasonString(reason));
      }
    
      virtual void queryNewFilePath(void* user_data, oasis::recording_mode_t file_type, bool rear_camera_on, bool sound_on, std::string& file_path) {}
      virtual void onSnapshotCompleted(void *user_data, uint32_t snapshot_id, int32_t camera_id, int error, const std::vector<char> &image_data, const struct timeval &timestamp) {}
    
      virtual void onAdasForwardVehicleCollisionWarning(void *user_data) {
        fprintf(stdout, ">>> Adas collision warning!\n");
      }
    
      virtual void onAdasForwardVehicleDepartueWarning(void *user_data) {
        fprintf(stdout, ">>> Adas vehicle departure warning!\n");
      }
    
      virtual void onAdasLaneDepartureWarning(void *user_data, int32_t land_side) {
        fprintf(stdout, ">>> Adas %s lane departure warning!\n", land_side==0?"left":"right");
      }
    
      virtual void onAdasForwardPedestrianWarning(void *user_data) {
        fprintf(stdout, ">>> Adas pedestrian warning!\n");
      }
    
    };
    
    
    void print_usage(const char *pname)
    {
      DLOG0(DLOG_INFO, "USAGE: %s [-n <nef path>] [<-o|-t> <dir>]\n", pname);
    }
    
    void cancel_handler(int signum)
    {
      timer_running = false;
      continue_recording = false;
    }
    
    #if 0
    #define SND_PATH "default"
    #else
    #define SND_PATH "hw:0,0"
    #endif
    
    int main(int argc, char* argv[])
    {
      int32_t err;
      int32_t i, c, n;
      std::thread t1, t2, t3;
      int32_t camera_id = 0;
      bool use_offs = false;
      std::string offs_dir = "/mnt/sd";
      std::string tmp_dir = "/tmp";
      const char *forward_url = nullptr;
    
      oasis::key_value_map_t parameters;
    
      bool nef_path_valid = false;
      std::string nef_path = "/mnt/sd/model.nef";
    
      srand(time(NULL));
    
      opterr = 0;
      while ((c = getopt(argc, argv, "n:o:t:x:h")) != -1) {
        switch (c) {
        case 'n':
          if(optarg == nullptr) {
            fprintf(stderr, "error: bad option argument '%c'\n", optopt);
            return -1;
          }
          if(access(optarg, F_OK)) {
            fprintf(stderr, "nef file not found: %s\n", optarg);
            return -1;
          }
          nef_path = optarg;
          nef_path_valid = true;
          break;
        case 'x':
          if(optarg == nullptr) {
            print_usage(argv[0]);
            return -1;
          }
          forward_url = optarg;
          break;
        case 'o':
          if(optarg == nullptr) {
            fprintf(stderr, "error: bad option argument '%c'\n", optopt);
            return -1;
          }
          offs_dir = optarg;
          if(!isDirectory(offs_dir.c_str())) {
            fprintf(stderr, "error: bad option argument '%c'\n", optopt);
            print_usage(argv[0]);
            return -1;
          }
          use_offs = true;
          break;
        case 't':
          if(optarg == nullptr) {
            fprintf(stderr, "error: bad option argument '%c'\n", optopt);
            return -1;
          }
          tmp_dir = optarg;
          if(!isDirectory(tmp_dir.c_str())) {
            fprintf(stderr, "error: bad option argument '%c'\n", optopt);
            print_usage(argv[0]);
            return -1;
          }
          use_offs = false;
          break;
        case 'h':
        default:
          print_usage(argv[0]);
          return -1;
        }
      }
    
    
      DLOG0(DLOG_INFO, "using %s: %s\n", use_offs?"OFFS":"non-OFFS", use_offs?offs_dir.c_str():tmp_dir.c_str());
      DLOG0(DLOG_INFO, "camera id: %d\n", camera_id);
    
      signal(SIGINT, cancel_handler);
    
       ////////////////////////////////////////////////////////////////////////////////////////////
      // init
    
      parameters["offs-qsize-max"] = std::to_string(OFFS_QUEUE_SIZE_KBYTES);
      parameters["offs-overwrite-if-exist"] = "1";
      parameters["offs-cache-size"] = std::to_string(OFFS_CACHE_SIZE_KBYTES);
      parameters["media-cache-size"] = std::to_string(MEDIA_CACHE_SIZE_KBYTES);
      parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
    
      //enableLogLocalTime(true);
    
      if(oasis::initialize(parameters) < 0) {
        DLOG(DLOG_ERROR, "Oasis init failed\n");
        return -1;
      }
    
      //non-offs, use system fs
      if(!use_offs) {
        fs::offsConfigLocalFormatInfo((tmp_dir + "/DRIVING").c_str(), 24*1024*1024);
        fs::offsConfigLocalFormatInfo((tmp_dir + "/EVENT").c_str(), 12*1024*1024);
      }
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      // audio
      parameters.clear();
    
      //parameters["types"]="source,sink";
      parameters["types"]="source,sink";
      parameters["path"]=SND_PATH;
      parameters["always-on"]="0";
      parameters["channels"]="2";
      parameters["aec-disabled"]="1";
      parameters["denoise-enabled"]="1";
      parameters["snd-input-channels"]="2";
      parameters["snd-input-sample-size"]="16";
      parameters["snd-input-sampling-duration-msec"]="40";
      parameters["snd-input-sampling-rate"]="48000";
      //parameters["snd-input-sampling-rate"]="22050";
      parameters["snd-input-mono-channel-index"] = "0";
    
      createAudioDevice(parameters);
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      // inference
      if(nef_path_valid) {
        parameters.clear();
        if(access(nef_path.c_str(), F_OK)) {
          nef_path_valid = false;
          fprintf(stdout, "file not found: %s\n", nef_path.c_str());
          fprintf(stdout, "inference disabled\n");
        } else {
          fprintf(stdout, "Inference model path: %s\n", nef_path.c_str());
          parameters["model-path"] = nef_path;
          createInferenceEngine(parameters);
        }
      }
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      // sources
      parameters.clear();
    
      parameters["source-count"] = "1";
    
      parameters["source1-camera-id"] = "0";
      parameters["source1-isp-id"] = "0";
      parameters["source1-isp-wdr-mode"] = "0";
      parameters["source1-capture-format"] = "YUV420";
      parameters["source1-capture-buffers"] = "5";
      parameters["source1-fps"] = "30";
      parameters["source1-subchannel-rotation"] = "0";
      parameters["source1-loc"] = "front";
      parameters["source1-capture-resolution"] = "1080p";
      parameters["source1-sensor-config"] = "./Resource/VIC/0/tp2855_1920x1080_ch0.cfg";
      parameters["source1-autoscene-config"] = "./Resource/AutoScene/autoscene_conf.cfg";
      parameters["source1-resource-dir"] = "./Resource/";
    
    
      configCameras(parameters);
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      //recorer settings
      parameters.clear();
    
      parameters["file-prefix"] =  "oasis-";
      parameters["file-extension"] = "mp4";
      //parameters["file-extension"] = "avi";
      parameters["file-duration-secs"] = "60";
      if(use_offs) {
        parameters["normal-folder-path"] = offs_dir + "/DRIVING";
        parameters["event-folder-path"] = offs_dir + "/EVENT";
        parameters["motion-folder-path"] = offs_dir + "/EVENT";
      } else {
        parameters["normal-folder-path"] = tmp_dir + "/DRIVING";
        parameters["event-folder-path"] = tmp_dir + "/EVENT";
        parameters["motion-folder-path"] = tmp_dir + "/EVENT";
      }
    
      parameters["event-pre-recording-seconds"] = "10";
      parameters["event-post-recording-seconds"] = "10";
      parameters["motion-pre-recording-seconds"] = "10";
      parameters["motion-post-recording-seconds"] = "10";
      parameters["disable-event-recording"] = "0";
      parameters["disable-offs-recording"] = "0";
      if(use_offs) {
        parameters["max-files"] = "0";
      } else {
        parameters["max-files"] = "5";
      }
      parameters["delete-oldest-file-on-max-files"] = "1";
      parameters["recording-size-limit-threshold-seconds"] = "0";
      parameters["report-media-info-ex"] = "1";
    
      parameters["avi-strd-size-max"] = "65536";
      parameters["mp4-udta-size-max"] = "65536";
      parameters["enable-persistent-cache"] = "0";
      parameters["recording-file-header-write-interval-secs"] = "1";
    
      parameters["snd-path"] = "hw:0,0";
      parameters["snd-input-channels"] = "2";
      parameters["snd-input-sample-size"] = "16";
      parameters["snd-input-sampling-duration-msec"] = "120";
      //parameters["snd-input-sampling-rate"] = "44100";
      parameters["snd-input-sampling-rate"] = "48000";
      if(forward_url) {
        parameters["snd-forward-url"] = forward_url;
      }
    
      parameters["aencoder-type"] = "raw"; //aac, raw, mp3
      parameters["aencoder-bitrate"] = "128000";
    
      parameters["osd-font-size"] = "0"; // disable (default)
      parameters["osd-font-face"] = "Consolas";
      parameters["osd-text-color"] = "255,255,255";
      parameters["osd-use-text-color"] = "1";
      parameters["osd-use-bg-color"] = "0";
      parameters["osd-bg-color"] = "0,0,0";
      parameters["osd-use-outline-color"] = "1";
      parameters["osd-outline-color"] = "255,255,255";
      parameters["osd-horz-align"] = "left";
      parameters["osd-vert-align"] = "bottom";
      parameters["osd-font-path"] = "/mnt/flash/leipzig/consola.ttf";
    
      parameters["channel-count"] = "1";
    
      parameters["channel1-camera-id"] = std::to_string(camera_id);
      parameters["channel1-ise-id"] = "-1";
      parameters["channel1-resolution"] = "1080p";
      parameters["channel1-bitrate"] = "8000000";
      parameters["channel1-fps"] = "30";
      parameters["channel1-file-framerate"] = "30";
      parameters["channel1-venc-framerate"] = "30";
      parameters["channel1-venc-keyframe-interval"] = "30";
    #if 0  
      parameters["channel1-vencoder-type"] = "h264";
      parameters["channel1-h264-profile"] = "high";
      parameters["channel1-h264-level"] = "level51";
      parameters["channel1-h264-min-qp"] = "10";
      parameters["channel1-h264-max-qp"] = "31";
      parameters["channel1-h264-enable-fixqp"] = "0";
      parameters["channel1-h264-fix-iqp"] = "10";
      parameters["channel1-h264-fix-pqp"] = "20";
    #else
      parameters["channel1-vencoder-type"] = "h265";
      parameters["channel1-h264-profile"] = "main";
    #endif
    
      if(nef_path_valid) {
        parameters["channel1-enable-adas"] = "1";
        parameters["channel1-adas-inference-model-names"] = "ufld,yolov5";
      }
    
      parameters["channel1-media-wait-timeout-secs"] = "3";
      parameters["channel1-media-wait-timeout-notify-oneshot"] = "1";
      parameters["channel1-osd-font-size"] = "0"; //disable;
    
      std::shared_ptr<MyRecordingObserver> recording_observer = std::make_shared<MyRecordingObserver>();
    
      RecorderRef recorder = createRecorder(parameters);
      if(recorder == nullptr) {
        DLOG(DLOG_ERROR, "recorder creation failed!\r\n");
        oasis::finalize();
        return -1;
      }
    
    
      memset(&strd_data_header_, 0, sizeof(strd_data_header_));
    
    
      t3 = std::thread([&]() {
    
        RecorderCommand cmd;
    
        do {
          {
            std::unique_lock<std::mutex> _l(cmdq_lock_);
            while(cmdq_.empty()) {
              cmdq_cond_.wait(_l);
            }
            cmd = cmdq_.front();
            cmdq_.pop_front();
          }
    
          if(cmd.cmd_ == kRecorderExitNow) {
            DLOG(DLOG_TRACE, "got kRecorderExitNow\r\n");
            break;
          }
    
          DLOG(DLOG_INFO, "cmd %d\n", cmd.cmd_);
    
          switch(cmd.cmd_) {
            case kStartEventRecording: {
              if (recorderIsRunning(recorder) && isEventRecording(recorder) == false) {
                time_t t;
                struct tm tm_t;
                char path[PATH_MAX];
    
                time(&t);
                localtime_r(&t, &tm_t);
    
                if(use_offs) {
                  sprintf(path, "%s/EVENT/EVT_%04d-%02d-%02d-%02d-%02d-%02d.mp4", offs_dir.c_str(), tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
                } else {
                  sprintf(path, "%s/EVENT/EVT_%04d-%02d-%02d-%02d-%02d-%02d.mp4", tmp_dir.c_str(), tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
                }
                startEventRecording(recorder, path, nullptr, 0);
              } else {
                fprintf(stdout, "Recorder not started (%d) or busy in recording an motion (%d).\n", recorderIsRunning(recorder), isMotionRecording(recorder));
              }
              break;
            }
          default:
              break;
          }
    
        } while(1);
    
        {
          std::lock_guard<std::mutex> _l(cmdq_lock_);
          cmdq_.clear();
        }
      });
    
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      t2 = std::thread([&]() {
        time_t t;
        struct tm tm_t;
        uint64_t usec, preview_usec;
        std::string title;
        uint32_t count = 0;
    
        char stream_data[512] = { 0, };
    
        preview_usec = systemTime();
    
        do {
    
          usec = systemTime();
    
          time(&t);
          localtime_r(&t, &tm_t);
    
          if (isRecorderRecording(recorder)) {
            sprintf(stream_data, "OASIS %4d/%02d/%02d %02d:%02d:%02d", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
            addRecordingVideoStreamData(recorder, (void*)stream_data, strlen(stream_data) + 1);
          }
    
          if(/*count == 0 &&*/ isRecorderRecording(recorder)) {
            title = oasis::format("OASIS %4d/%02d/%02d %02d:%02d:%02d\n", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_mday);
            //title += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.";
            setRecordingText(recorder, kTextTrackOsd, title, usec);
            count++;
          }
    
          setVehicleSpeed(recorder, 60.f);
    
          //30 msec
          usleep(40000);
    
        } while (timer_running);
      });
    
      err = startRecording(recorder, recording_observer, nullptr, false, &strd_data_header_, sizeof(strd_data_header_));
      if(err < 0) {
        DLOG(DLOG_ERROR, "start recording failed\r\n");
        goto done;
      }  
    
      do {
        c = getch("Ctrl+C to exit...");
        if(c < 0 || continue_recording == false) {
          break;
        }
        if(c != '\n') {
          fprintf(stdout, "%c", c);
        }
        fprintf(stdout, "\n");
    
        if(c == 'e') {
          postCommand(kStartEventRecording);
        }
    
      } while (continue_recording == true);
    
    done:
      timer_running = false;
      if (t2.joinable()) {
        t2.join();
      }
    
      postCommand(kRecorderExitNow);
      if (t3.joinable()) {
        t3.join();
      }
    
      if(recorder) {
        destroyRecorder(recorder);
      }
    
      oasis::finalize();
    
      printf("goodbye.\n");
    
      return 0;
    }