/* * 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 #include #include #include #include #include #include #include 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; 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& camObj, EvsServiceCallback* callback, int maxNumFramesInFlight) : mEvsCamera(camObj), mCallback(callback), mMaxNumFramesInFlight(maxNumFramesInFlight) { 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 lock(mLock); if (!mRunning) { auto status = mEvsCamera->startVideoStream(ref()); if (!status.isOk()) { LOG(ERROR) << "StreamHandler failed to start a video stream: " << status.getServiceSpecificError(); return false; } // Marks ourselves as running mRunning = true; } return true; } /* * Requests to stop a video stream */ bool StreamHandler::asyncStopStream() { bool success = true; // This will result in STREAM_STOPPED event; the client may want to wait // this event to confirm the closure. { std::lock_guard lock(mLock); auto it = mReceivedBuffers.begin(); while (it != mReceivedBuffers.end()) { // Packages a returned buffer and sends it back to the camera std::vector 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(); success = false; } it = mReceivedBuffers.erase(it); } } auto status = mEvsCamera->stopVideoStream(); if (!status.isOk()) { LOG(WARNING) << "stopVideoStream() failed but ignored."; success = false; } return success; } /* * Requests to stop a video stream and waits for a confirmation */ void StreamHandler::blockingStopStream() { if (!asyncStopStream()) { // EVS service may die so no stream-stop event occurs. std::lock_guard lock(mLock); mRunning = false; return; } // Waits until the stream has actually stopped std::unique_lock lock(mLock); while (mRunning) { if (!mCondition.wait_for(lock, 1s, [this]() { return !mRunning; })) { LOG(WARNING) << "STREAM_STOPPED event timer expired. EVS service may die."; break; } } } bool StreamHandler::isRunning() { std::lock_guard lock(mLock); return mRunning; } void StreamHandler::doneWithFrame(int bufferId) { BufferDesc bufferToReturn; { std::lock_guard 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 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& buffers) { LOG(DEBUG) << "Received frames from the camera, bufferId = " << buffers[0].bufferId; const BufferDesc& bufferToUse = buffers[0]; size_t numBuffersInUse; { std::lock_guard lock(mLock); numBuffersInUse = mReceivedBuffers.size(); } if (numBuffersInUse >= mMaxNumFramesInFlight) { // We're holding more than what allowed; returns this buffer // immediately. doneWithFrame(bufferToUse); return ::ndk::ScopedAStatus::ok(); } { std::lock_guard 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(EvsResult::INVALID_ARG)); } return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus StreamHandler::notify(const EvsEventDesc& event) { switch (event.aType) { case EvsEventType::STREAM_STOPPED: { { std::lock_guard 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(event.aType) << " is received but ignored"; break; default: LOG(ERROR) << "Unknown event id 0x" << std::hex << static_cast(event.aType); break; } mCallback->onNewEvent(event); return ::ndk::ScopedAStatus::ok(); } } // namespace android::automotive::evs