EVS 应用
- 1、EVS启动
 - 2、EvsStateControl.cpp 控制管理
 - 2.1 EvsStateControl初始化
 - 2.2 EvsVehicleListener.h唤起处理`EvsStateControl::updateLoop()`
 
- 3、EVS 应用逻辑流程
 
android12-release
 增强型视觉系统 (EVS)
1、EVS启动
Android 包含与
EVS 管理器和车载 HAL通信的 EVS 应用的原生 C++ 参考实现,以提供基本的后视摄像头功能。该应用应在系统启动过程的早期启动,根据可用的相机和汽车状态(车轮和转向灯状态)显示合适的视频。原始设备制造商 (OEM) 可以使用自己的汽车专用逻辑和呈现来修改或替换 EVS 应用。
packages/services/Car/cpp/evs/apps/default/Android.bp
packages/services/Car/cpp/evs/apps/default/evs_app.rc
packages/services/Car/cpp/evs/apps/default/evs_app.cpp
evs_app.rc解析主要入口evs_app.cpp#main():
ConfigManager config加载我们的配置信息:
const char* CONFIG_DEFAULT_PATH = "/system/etc/automotive/evs/config.json";
const char* CONFIG_OVERRIDE_PATH = "/system/etc/automotive/evs/config_override.json";pEvs = IEvsEnumerator::getService(evsServiceName);获取EVS 管理器服务,evsServiceName为default、EvsEnumeratorHw 或 EvsEnumeratorHw-MockpDisplay = pEvs->openDisplay_1_1(displayId);获取用于专门与系统的 EVS 显示进行交互的接口对象pVnet = IVehicle::getService()连接Vehicle HAL;注册属性监听class EvsVehicleListener : public IVehicleCallback:
subscribeToVHal(pVnet, pEvsListener, VehicleProperty::GEAR_SELECTION)车辆档位属性监听
subscribeToVHal(pVnet, pEvsListener, VehicleProperty::TURN_SIGNAL_STATE)车辆转向信号灯属性监听pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config)转态控制器创建并启动pStateController->startUpdateLoop()pEvsListener->run(pStateController);等到车辆属性事件,waitForEvents(5000)每隔一段时间唤醒并验证当前的状态“以防万一”
// Main entry point
int main(int argc, char** argv)
{
    LOG(INFO) << "EVS app starting";
    // Register a signal handler
    registerSigHandler();
    // Set up default behavior, then check for command line options
    bool useVehicleHal = true;
    bool printHelp = false;
    const char* evsServiceName = "default";
    int displayId = -1;
    bool useExternalMemory = false;
    android_pixel_format_t extMemoryFormat = HAL_PIXEL_FORMAT_RGBA_8888;
    int32_t mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_REVERSE);
    for (int i=1; i< argc; i++) {
        if (strcmp(argv[i], "--test") == 0) {
            useVehicleHal = false;
        } else if (strcmp(argv[i], "--hw") == 0) {
            evsServiceName = "EvsEnumeratorHw";
        } else if (strcmp(argv[i], "--mock") == 0) {
            evsServiceName = "EvsEnumeratorHw-Mock";
        } else if (strcmp(argv[i], "--help") == 0) {
            printHelp = true;
        } else if (strcmp(argv[i], "--display") == 0) {
            displayId = std::stoi(argv[++i]);
        } else if (strcmp(argv[i], "--extmem") == 0) {
            useExternalMemory = true;
            if (i + 1 >= argc) {
                // use RGBA8888 by default
                LOG(INFO) << "External buffer format is not set.  "
                          << "RGBA8888 will be used.";
            } else {
                if (!convertStringToFormat(argv[i + 1], &extMemoryFormat)) {
                    LOG(WARNING) << "Color format string " << argv[i + 1]
                                 << " is unknown or not supported.  RGBA8888 will be used.";
                } else {
                    // move the index
                    ++i;
                }
            }
        } else if (strcmp(argv[i], "--gear") == 0) {
            // Gear signal to simulate
            i += 1; // increase an index to next argument
            if (strcasecmp(argv[i], "Park") == 0) {
                mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_PARK);
            } else if (strcasecmp(argv[i], "Reverse") != 0) {
                LOG(WARNING) << "Unknown gear signal, " << argv[i] << ", is ignored "
                             << "and the reverse signal will be used instead";
            }
        } else {
            printf("Ignoring unrecognized command line arg '%s'\n", argv[i]);
            printHelp = true;
        }
    }
    if (printHelp) {
        printf("Options include:\n");
        printf("  --test\n\tDo not talk to Vehicle Hal, "
               "but simulate a given mock gear signal instead\n");
        printf("  --gear\n\tMock gear signal for the test mode.");
        printf("  Available options are Reverse and Park (case insensitive)\n");
        printf("  --hw\n\tBypass EvsManager by connecting directly to EvsEnumeratorHw\n");
        printf("  --mock\n\tConnect directly to EvsEnumeratorHw-Mock\n");
        printf("  --display\n\tSpecify the display to use.  If this is not set, the first"
                              "display in config.json's list will be used.\n");
        printf("  --extmem  <format>\n\t"
               "Application allocates buffers to capture camera frames.  "
               "Available format strings are (case insensitive):\n");
        printf("\t\tRGBA8888: 4x8-bit RGBA format.  This is the default format to be used "
               "when no format is specified.\n");
        printf("\t\tYV12: YUV420 planar format with a full resolution Y plane "
               "followed by a V values, with U values last.\n");
        printf("\t\tNV21: A biplanar format with a full resolution Y plane "
               "followed by a single chrome plane with weaved V and U values.\n");
        printf("\t\tYUYV: Packed format with a half horizontal chrome resolution.  "
               "Known as YUV4:2:2.\n");
        return EXIT_FAILURE;
    }
    // Load our configuration information
    ConfigManager config;
    if (!config.initialize(CONFIG_OVERRIDE_PATH)) {
        if (!config.initialize(CONFIG_DEFAULT_PATH)) {
            LOG(ERROR) << "Missing or improper configuration for the EVS application.  Exiting.";
            return EXIT_FAILURE;
        }
    }
    // Set thread pool size to one to avoid concurrent events from the HAL.
    // This pool will handle the EvsCameraStream callbacks.
    // Note:  This _will_ run in parallel with the EvsListener run() loop below which
    // runs the application logic that reacts to the async events.
    configureRpcThreadpool(1, false /* callerWillJoin */);
    // Construct our async helper object
    sp<EvsVehicleListener> pEvsListener = new EvsVehicleListener();
    // Get the EVS manager service
    LOG(INFO) << "Acquiring EVS Enumerator";
    pEvs = IEvsEnumerator::getService(evsServiceName);
    if (pEvs.get() == nullptr) {
        LOG(ERROR) << "getService(" << evsServiceName
                   << ") returned NULL.  Exiting.";
        return EXIT_FAILURE;
    }
    // Request exclusive access to the EVS display
    LOG(INFO) << "Acquiring EVS Display";
    // We'll use an available display device.
    displayId = config.setActiveDisplayId(displayId);
    if (displayId < 0) {
        PLOG(ERROR) << "EVS Display is unknown.  Exiting.";
        return EXIT_FAILURE;
    }
    pDisplay = pEvs->openDisplay_1_1(displayId);
    if (pDisplay.get() == nullptr) {
        LOG(ERROR) << "EVS Display unavailable.  Exiting.";
        return EXIT_FAILURE;
    }
    config.useExternalMemory(useExternalMemory);
    config.setExternalMemoryFormat(extMemoryFormat);
    // Set a mock gear signal for the test mode
    config.setMockGearSignal(mockGearSignal);
    // Connect to the Vehicle HAL so we can monitor state
    sp<IVehicle> pVnet;
    if (useVehicleHal) {
        LOG(INFO) << "Connecting to Vehicle HAL";
        pVnet = IVehicle::getService();
        if (pVnet.get() == nullptr) {
            LOG(ERROR) << "Vehicle HAL getService returned NULL.  Exiting.";
            return EXIT_FAILURE;
        } else {
            // Register for vehicle state change callbacks we care about
            // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
            if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::GEAR_SELECTION)) {
                LOG(ERROR) << "Without gear notification, we can't support EVS.  Exiting.";
                return EXIT_FAILURE;
            }
            if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::TURN_SIGNAL_STATE)) {
                LOG(WARNING) << "Didn't get turn signal notifications, so we'll ignore those.";
            }
        }
    } else {
        LOG(WARNING) << "Test mode selected, so not talking to Vehicle HAL";
    }
    // Configure ourselves for the current vehicle state at startup
    LOG(INFO) << "Constructing state controller";
    pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config);
    if (!pStateController->startUpdateLoop()) {
        LOG(ERROR) << "Initial configuration failed.  Exiting.";
        return EXIT_FAILURE;
    }
    // Run forever, reacting to events as necessary
    LOG(INFO) << "Entering running state";
    pEvsListener->run(pStateController);
    // In normal operation, we expect to run forever, but in some error conditions we'll quit.
    // One known example is if another process preempts our registration for our service name.
    LOG(ERROR) << "EVS Listener stopped.  Exiting.";
    return EXIT_SUCCESS;
}
 
在
android12-release上C++ EVS 示例应用是位于packages/services/Car/cpp/evs/apps

2、EvsStateControl.cpp 控制管理
2.1 EvsStateControl初始化
pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config):
mVehicle(pVnet):VehicleHal 车辆属性服务;参考 【IVI】VehicleService启动、【IVI】车载设备硬件抽象层VHALmEvs(pEvs):EVS 管理器服务mDisplay(pDisplay):与系统的 EVS 显示进行交互mConfig(config):EVS json配置mCurrentState(OFF):当前状态mEvsStats(EvsStats::build()):evs状态mEvs->getCameraList_1_1():系统中所有相机的说明的矢量mCameraList、mCameraDescList:保存相关相机说明信息CameraDesc_1_1
hardware/interfaces/automotive/evs/1.1/types.hal
hardware/interfaces/automotive/evs/1.0/types.hal
EvsStateControl::EvsStateControl(android::sp<IVehicle> pVnet, android::sp<IEvsEnumerator> pEvs,
                                 android::sp<IEvsDisplay> pDisplay, const ConfigManager& config) :
      mVehicle(pVnet),
      mEvs(pEvs),
      mDisplay(pDisplay),
      mConfig(config),
      mCurrentState(OFF),
      mEvsStats(EvsStats::build()) {
    // Initialize the property value containers we'll be updating (they'll be zeroed by default)
    static_assert(getPropType(VehicleProperty::GEAR_SELECTION) == VehiclePropertyType::INT32,
                  "Unexpected type for GEAR_SELECTION property");
    static_assert(getPropType(VehicleProperty::TURN_SIGNAL_STATE) == VehiclePropertyType::INT32,
                  "Unexpected type for TURN_SIGNAL_STATE property");
    mGearValue.prop       = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
    mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
    // This way we only ever deal with cameras which exist in the system
    // Build our set of cameras for the states we support
    LOG(DEBUG) << "Requesting camera list";
    mEvs->getCameraList_1_1(
        [this, &config](hidl_vec<CameraDesc> cameraList) {
            LOG(INFO) << "Camera list callback received " << cameraList.size() << "cameras.";
            for (auto&& cam: cameraList) {
                LOG(DEBUG) << "Found camera " << cam.v1.cameraId;
                bool cameraConfigFound = false;
                // Check our configuration for information about this camera
                // Note that a camera can have a compound function string
                // such that a camera can be "right/reverse" and be used for both.
                // If more than one camera is listed for a given function, we'll
                // list all of them and let the UX/rendering logic use one, some
                // or all of them as appropriate.
                for (auto&& info: config.getCameras()) {
                    if (cam.v1.cameraId == info.cameraId) {
                        // We found a match!
                        if (info.function.find("reverse") != std::string::npos) {
                            mCameraList[State::REVERSE].emplace_back(info);
                            mCameraDescList[State::REVERSE].emplace_back(cam);
                        }
                        if (info.function.find("right") != std::string::npos) {
                            mCameraList[State::RIGHT].emplace_back(info);
                            mCameraDescList[State::RIGHT].emplace_back(cam);
                        }
                        if (info.function.find("left") != std::string::npos) {
                            mCameraList[State::LEFT].emplace_back(info);
                            mCameraDescList[State::LEFT].emplace_back(cam);
                        }
                        if (info.function.find("park") != std::string::npos) {
                            mCameraList[State::PARKING].emplace_back(info);
                            mCameraDescList[State::PARKING].emplace_back(cam);
                        }
                        cameraConfigFound = true;
                        break;
                    }
                }
                if (!cameraConfigFound) {
                    LOG(WARNING) << "No config information for hardware camera "
                                 << cam.v1.cameraId;
                }
            }
        }
    );
    LOG(DEBUG) << "State controller ready";
}
 
2.2 EvsVehicleListener.h唤起处理EvsStateControl::updateLoop()
 
VehicleProperty::GEAR_SELECTION、VehicleProperty::TURN_SIGNAL_STATE事件接收onPropertyEvent,mEventCond.notify_one()处理EvsVehicleListener#run死循环调用pStateController->postCommand(cmd)
 mWakeSignal.notify_all()唤起最终调用EvsStateControl::updateLoop()
CHECK_VEHICLE_STATE这里提示 Just running selectStateForCurrentConditions below will take care of this
selectStateForCurrentConditions()就是根据车辆档位和转向信号灯属性判断configureEvsPipeline处理状态和renderermCurrentRenderer->drawFrame(convertBufferDesc(tgtBuffer))生成我们的输出图像
packages/services/Car/cpp/evs/apps/default/EvsVehicleListener.h
void run(EvsStateControl *pStateController) {
    while (true) {
        // Wait until we have an event to which to react
        // (wake up and validate our current state "just in case" every so often)
        waitForEvents(5000);
        // If we were delivered an event (or it's been a while) update as necessary
        EvsStateControl::Command cmd = {
            .operation = EvsStateControl::Op::CHECK_VEHICLE_STATE,
            .arg1      = 0,
            .arg2      = 0,
        };
        pStateController->postCommand(cmd);
    }
}
 
packages/services/Car/cpp/evs/apps/default/EvsStateControl.cpp
void EvsStateControl::updateLoop() {
    LOG(DEBUG) << "Starting EvsStateControl update loop";
    bool run = true;
    while (run) {
        // Process incoming commands
        {
            std::lock_guard <std::mutex> lock(mLock);
            while (!mCommandQueue.empty()) {
                const Command& cmd = mCommandQueue.front();
                switch (cmd.operation) {
                case Op::EXIT:
                    run = false;
                    break;
                case Op::CHECK_VEHICLE_STATE:
                    // Just running selectStateForCurrentConditions below will take care of this
                    break;
                case Op::TOUCH_EVENT:
                    // Implement this given the x/y location of the touch event
                    break;
                }
                mCommandQueue.pop();
            }
        }
        // Review vehicle state and choose an appropriate renderer
        if (!selectStateForCurrentConditions()) {
            LOG(ERROR) << "selectStateForCurrentConditions failed so we're going to die";
            break;
        }
        // If we have an active renderer, give it a chance to draw
        if (mCurrentRenderer) {
            // Get the output buffer we'll use to display the imagery
            BufferDesc_1_0 tgtBuffer = {};
            mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc_1_0& buff) {
                                          tgtBuffer = buff;
                                      }
            );
            if (tgtBuffer.memHandle == nullptr) {
                LOG(ERROR) << "Didn't get requested output buffer -- skipping this frame.";
                run = false;
            } else {
                // Generate our output image
                if (!mCurrentRenderer->drawFrame(convertBufferDesc(tgtBuffer))) {
                    // If drawing failed, we want to exit quickly so an app restart can happen
                    run = false;
                }
                // Send the finished image back for display
                mDisplay->returnTargetBufferForDisplay(tgtBuffer);
                if (!mFirstFrameIsDisplayed) {
                    mFirstFrameIsDisplayed = true;
                    // returnTargetBufferForDisplay() is finished, the frame should be displayed
                    mEvsStats.finishComputingFirstFrameLatency(android::uptimeMillis());
                }
            }
        } else if (run) {
            // No active renderer, so sleep until somebody wakes us with another command
            // or exit if we received EXIT command
            std::unique_lock<std::mutex> lock(mLock);
            mWakeSignal.wait(lock);
        }
    }
    LOG(WARNING) << "EvsStateControl update loop ending";
    if (mCurrentRenderer) {
        // Deactive the renderer
        mCurrentRenderer->deactivate();
    }
    // If `ICarTelemetry` service was not ready before, we need to try sending data again.
    mEvsStats.sendCollectedDataBlocking();
    printf("Shutting down app due to state control loop ending\n");
    LOG(ERROR) << "Shutting down app due to state control loop ending";
}
 
3、EVS 应用逻辑流程






















