// Copyright 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "EvsInputManager.h" #include #include #include #include #include #include "AnalyzeUseCase.h" #include "BaseAnalyzeCallback.h" #include "InputConfig.pb.h" #include "InputEngineInterface.h" #include "Options.pb.h" using ::android::automotive::evs::support::AnalyzeUseCase; using ::android::automotive::evs::support::BaseAnalyzeCallback; namespace android { namespace automotive { namespace computepipe { namespace runner { namespace input_manager { void AnalyzeCallback::analyze(const ::android::automotive::evs::support::Frame& frame) { std::shared_lock lock(mEngineInterfaceLock); if (mInputEngineInterface != nullptr) { auto time_point = std::chrono::system_clock::now(); int64_t timestamp = std::chrono::time_point_cast(time_point) .time_since_epoch() .count(); // Stride for hardware buffers is specified in pixels whereas for // InputFrame, it is specified in bytes. We therefore need to multiply // the stride by 4 for an RGBA frame. InputFrame inputFrame(frame.height, frame.width, PixelFormat::RGBA, frame.stride * 4, frame.data); mInputEngineInterface->dispatchInputFrame(mInputStreamId, timestamp, inputFrame); } } void AnalyzeCallback::setEngineInterface(std::shared_ptr inputEngineInterface) { std::lock_guard lock(mEngineInterfaceLock); mInputEngineInterface = inputEngineInterface; } EvsInputManager::EvsInputManager(const proto::InputConfig& inputConfig, std::shared_ptr inputEngineInterface) : mInputEngineInterface(inputEngineInterface), mInputConfig(inputConfig) { } std::unique_ptr EvsInputManager::createEvsInputManager( const proto::InputConfig& inputConfig, std::shared_ptr inputEngineInterface) { auto evsManager = std::make_unique(inputConfig, inputEngineInterface); if (evsManager->initializeCameras() == Status::SUCCESS) { return evsManager; } return nullptr; } Status EvsInputManager::initializeCameras() { for (int i = 0; i < mInputConfig.input_stream_size(); i++) { // Verify that the stream type specified is a camera stream which is necessary for evs // manager. if (mInputConfig.input_stream(i).type() != proto::InputStreamConfig_InputType_CAMERA) { ALOGE("Evs stream manager expects the input stream type to be camera."); return Status::INVALID_ARGUMENT; } const std::string& cameraId = mInputConfig.input_stream(i).cam_config().cam_id(); std::unique_ptr analyzeCallback = std::make_unique(mInputConfig.input_stream(i).stream_id()); AnalyzeUseCase analyzeUseCase = AnalyzeUseCase::createDefaultUseCase(cameraId, analyzeCallback.get()); mAnalyzeCallbacks.push_back(std::move(analyzeCallback)); int streamId = mInputConfig.input_stream(i).stream_id(); auto [it, result] = mEvsUseCases.try_emplace(std::move(streamId), std::move(analyzeUseCase)); if (!result) { // Multiple camera streams found to have the same camera id. ALOGE("Multiple camera streams have the same stream id."); return Status::INVALID_ARGUMENT; } } return Status::SUCCESS; } Status EvsInputManager::handleExecutionPhase(const RunnerEvent& e) { // Starting execution cannot be stopped in between. handleStopImmediate needs to be called. if (e.isAborted()) { return Status::INVALID_ARGUMENT; } else if (e.isTransitionComplete()) { return Status::SUCCESS; } if (mEvsUseCases.empty()) { ALOGE("No evs use cases configured. Verify that handleConfigPhase has been called"); return Status::ILLEGAL_STATE; } // Start all the video streams. bool successfullyStartedAllCameras = true; for (auto& [streamId, evsUseCase] : mEvsUseCases) { if (!evsUseCase.startVideoStream()) { successfullyStartedAllCameras = false; ALOGE("Unable to successfully start all cameras"); break; } } // If not all video streams have started successfully, stop the streams. if (!successfullyStartedAllCameras) { for (auto& [streamId, evsUseCase] : mEvsUseCases) { evsUseCase.stopVideoStream(); } return Status::INTERNAL_ERROR; } // Set the input to engine interface for callbacks only when all the streams have successfully // started. This prevents any callback from going out unless all of the streams have started. for (auto& analyzeCallback : mAnalyzeCallbacks) { analyzeCallback->setEngineInterface(mInputEngineInterface); } return Status::SUCCESS; } Status EvsInputManager::handleStopImmediatePhase(const RunnerEvent& e) { if (e.isAborted()) { ALOGE( "Unable to abort immediate stopping of EVS cameras. Please start the video streams " "again if " "needed."); } else if (e.isTransitionComplete()) { return Status::SUCCESS; } // Reset all input engine interfaces so that callbacks stop going out even if there are evs // frames in flux. for (auto& analyzeCallback : mAnalyzeCallbacks) { analyzeCallback->setEngineInterface(nullptr); } for (auto& [streamId, evsUseCase] : mEvsUseCases) { evsUseCase.stopVideoStream(); } return Status::SUCCESS; } Status EvsInputManager::handleStopWithFlushPhase(const RunnerEvent& e) { if (e.isAborted()) { ALOGE( "Unable to abort stopping and flushing of EVS cameras. Please start the video streams " "again if " "needed."); } else if (e.isTransitionComplete()) { return Status::SUCCESS; } for (auto& [streamId, evsUseCase] : mEvsUseCases) { evsUseCase.stopVideoStream(); } return Status::SUCCESS; } Status EvsInputManager::handleResetPhase(const RunnerEvent& e) { if (e.isAborted()) { ALOGE("Unable to abort reset."); return Status::INVALID_ARGUMENT; } mEvsUseCases.clear(); mAnalyzeCallbacks.clear(); return Status::SUCCESS; } } // namespace input_manager } // namespace runner } // namespace computepipe } // namespace automotive } // namespace android