/* * Copyright 2021 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 "StreamHandler.h" #include <aidl/android/hardware/automotive/evs/EvsEventType.h> #include <aidl/android/hardware/automotive/evs/EvsResult.h> #include <aidl/android/hardware/common/NativeHandle.h> #include <android-base/chrono_utils.h> #include <android-base/logging.h> #include <android/hardware_buffer.h> #include <android/hardware_buffer_jni.h> #include <vndk/hardware_buffer.h> namespace { using ::aidl::android::hardware::automotive::evs::BufferDesc; using ::aidl::android::hardware::automotive::evs::EvsEventDesc; using ::aidl::android::hardware::automotive::evs::EvsEventType; using ::aidl::android::hardware::automotive::evs::EvsResult; using ::aidl::android::hardware::automotive::evs::IEvsCamera; using ::aidl::android::hardware::common::NativeHandle; using ::aidl::android::hardware::graphics::common::HardwareBuffer; using ::android::base::ScopedLockAssertion; NativeHandle dupNativeHandle(const NativeHandle& handle, bool doDup) { NativeHandle dup; dup.fds = std::vector<::ndk::ScopedFileDescriptor>(handle.fds.size()); if (!doDup) { for (auto i = 0; i < handle.fds.size(); ++i) { dup.fds.at(i).set(handle.fds[i].get()); } } else { for (auto i = 0; i < handle.fds.size(); ++i) { dup.fds[i] = std::move(handle.fds[i].dup()); } } dup.ints = handle.ints; return std::move(dup); } HardwareBuffer dupHardwareBuffer(const HardwareBuffer& buffer, bool doDup) { HardwareBuffer dup = { .description = buffer.description, .handle = dupNativeHandle(buffer.handle, doDup), }; return std::move(dup); } BufferDesc dupBufferDesc(const BufferDesc& src, bool doDup) { BufferDesc dup = { .buffer = dupHardwareBuffer(src.buffer, doDup), .pixelSizeBytes = src.pixelSizeBytes, .bufferId = src.bufferId, .deviceId = src.deviceId, .timestamp = src.timestamp, .metadata = src.metadata, }; return std::move(dup); } } // namespace namespace android::automotive::evs { StreamHandler::StreamHandler(const std::shared_ptr<IEvsCamera>& camObj, EvsServiceCallback* callback, int maxNumFramesInFlight) : mEvsCamera(camObj), mCallback(callback), mMaxNumFramesInFlightPerClient(maxNumFramesInFlight), mNumClients(0) { if (!camObj) { LOG(ERROR) << "IEvsCamera is invalid."; } else { // We rely on the camera having at least two buffers available since we'll hold one and // expect the camera to be able to capture a new image in the background. auto status = camObj->setMaxFramesInFlight(maxNumFramesInFlight); if (!status.isOk()) { LOG(ERROR) << "Failed to adjust the maximum number of frames in flight: " << status.getServiceSpecificError(); } } } /* * Shuts down a stream handler */ StreamHandler::~StreamHandler() { shutdown(); } /* * Stops an active stream and releases the camera device in use */ void StreamHandler::shutdown() { // Make sure we're not still streaming blockingStopStream(); // At this point, the receiver thread is no longer running, so we can safely drop // our remote object references so they can be freed mEvsCamera = nullptr; } /* * Requests EVS to start a video stream */ bool StreamHandler::startStream() { std::lock_guard<std::mutex> lock(mLock); if (!mRunning) { auto status = mEvsCamera->startVideoStream(ref<StreamHandler>()); if (!status.isOk()) { LOG(ERROR) << "StreamHandler failed to start a video stream: " << status.getServiceSpecificError(); return false; } // This is the first client of this stream. mNumClients = 1; // Marks ourselves as running mRunning = true; } else { // Increase a number of active clients and the max. number of frames in // flight. int desired = (++mNumClients) * mMaxNumFramesInFlightPerClient; auto status = mEvsCamera->setMaxFramesInFlight(desired); if (!status.isOk()) { LOG(ERROR) << "Failed to adjust the maximum number of frames in flight for " << mNumClients << " clients. Error: " << status.getServiceSpecificError(); // Decrease a number of clients back. mNumClients -= 1; return false; } } return true; } /* * Requests to stop a video stream and waits for a confirmation */ void StreamHandler::blockingStopStream() { { std::lock_guard lock(mLock); if (!mRunning) { // Nothing to do. return; } if (mNumClients > 1) { // Decrease a number of active clients and return. --mNumClients; return; } // Return all buffers currently held by us. auto it = mReceivedBuffers.begin(); while (it != mReceivedBuffers.end()) { // Packages a returned buffer and sends it back to the camera std::vector<BufferDesc> frames(1); frames[0] = std::move(*it); auto status = mEvsCamera->doneWithFrame(frames); if (!status.isOk()) { LOG(WARNING) << "Failed to return a frame to EVS service; " << "this may leak the memory: " << status.getServiceSpecificError(); } it = mReceivedBuffers.erase(it); } } auto status = mEvsCamera->stopVideoStream(); if (!status.isOk()) { LOG(WARNING) << "stopVideoStream() failed but ignored."; } // Now, we are waiting for the ack from EvsManager service. { std::unique_lock<std::mutex> lock(mLock); ScopedLockAssertion lock_assertion(mLock); while (mRunning) { if (!mCondition.wait_for(lock, 1s, [this]() REQUIRES(mLock) { return !mRunning; })) { LOG(WARNING) << "STREAM_STOPPED event timer expired. EVS service may die."; break; } } // Decrease a number of active clients. --mNumClients; } } bool StreamHandler::isRunning() { std::lock_guard<std::mutex> lock(mLock); return mRunning; } void StreamHandler::doneWithFrame(int bufferId) { BufferDesc bufferToReturn; { std::lock_guard<std::mutex> lock(mLock); auto it = std::find_if(mReceivedBuffers.begin(), mReceivedBuffers.end(), [bufferId](BufferDesc& b) { return b.bufferId == bufferId; }); if (it == mReceivedBuffers.end()) { LOG(DEBUG) << "Ignores a request to return unknown buffer"; return; } bufferToReturn = std::move(*it); mReceivedBuffers.erase(it); } // Packages a returned buffer and sends it back to the camera std::vector<BufferDesc> frames(1); frames[0] = std::move(bufferToReturn); if (auto status = mEvsCamera->doneWithFrame(frames); !status.isOk()) { LOG(ERROR) << "Status = " << status.getStatus(); LOG(ERROR) << "Failed to return a frame (id = " << bufferId << " to EVS service; this may leak the memory: " << status.getServiceSpecificError(); } } void StreamHandler::doneWithFrame(const BufferDesc& buffer) { return doneWithFrame(buffer.bufferId); } ::ndk::ScopedAStatus StreamHandler::deliverFrame(const std::vector<BufferDesc>& buffers) { LOG(DEBUG) << "Received frames from the camera, bufferId = " << buffers[0].bufferId; const BufferDesc& bufferToUse = buffers[0]; size_t numBuffersInUse; { std::lock_guard<std::mutex> lock(mLock); numBuffersInUse = mReceivedBuffers.size(); } if (numBuffersInUse >= mMaxNumFramesInFlightPerClient) { // We're holding more than what allowed; returns this buffer // immediately. doneWithFrame(bufferToUse); return ::ndk::ScopedAStatus::ok(); } { std::lock_guard<std::mutex> lock(mLock); // Records a new frameDesc and forwards to clients mReceivedBuffers.push_back(dupBufferDesc(bufferToUse, /* dup= */ true)); LOG(DEBUG) << "Got buffer " << bufferToUse.bufferId << ", total = " << mReceivedBuffers.size(); // Notify anybody who cares that things have changed mCondition.notify_all(); } // Forwards a new frame if (!mCallback->onNewFrame(bufferToUse)) { doneWithFrame(bufferToUse); return ::ndk::ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(EvsResult::INVALID_ARG)); } return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus StreamHandler::notify(const EvsEventDesc& event) { switch (event.aType) { case EvsEventType::STREAM_STOPPED: { { std::lock_guard<std::mutex> lock(mLock); // Signal that the last frame has been received and the stream is stopped mRunning = false; } LOG(DEBUG) << "Received a STREAM_STOPPED event"; break; } case EvsEventType::PARAMETER_CHANGED: LOG(DEBUG) << "Camera parameter 0x" << std::hex << event.payload[0] << " is set to 0x" << std::hex << event.payload[1]; break; // Below events are ignored in reference implementation. case EvsEventType::STREAM_STARTED: [[fallthrough]]; case EvsEventType::FRAME_DROPPED: [[fallthrough]]; case EvsEventType::TIMEOUT: LOG(INFO) << "Event 0x" << std::hex << static_cast<int32_t>(event.aType) << " from " << (event.deviceId.empty() ? "Unknown" : event.deviceId) << " is received but ignored"; break; default: LOG(ERROR) << "Unknown event id 0x" << std::hex << static_cast<int32_t>(event.aType); break; } mCallback->onNewEvent(event); return ::ndk::ScopedAStatus::ok(); } } // namespace android::automotive::evs