| /* |
| * 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 <android-base/chrono_utils.h> |
| #include <android-base/logging.h> |
| #include <cutils/native_handle.h> |
| |
| #include <cstring> |
| |
| using namespace ::android::hardware::automotive::evs::V1_1; |
| |
| using ::android::sp; |
| using ::android::hardware::hidl_handle; |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::automotive::evs::V1_0::EvsResult; |
| using ::android::hardware::automotive::evs::V1_1::IEvsDisplay; |
| |
| using EvsDisplayState = ::android::hardware::automotive::evs::V1_0::DisplayState; |
| using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc; |
| using BufferDesc_1_1 = ::android::hardware::automotive::evs::V1_1::BufferDesc; |
| |
| namespace android { |
| namespace automotive { |
| namespace evs { |
| |
| StreamHandler::StreamHandler(sp<IEvsCamera>& camera, EvsServiceCallback* callback, |
| int maxNumFramesInFlight) : |
| mEvsCamera(camera), mCallback(callback), mMaxNumFramesInFlight(maxNumFramesInFlight) { |
| if (camera == nullptr) { |
| 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 = camera->setMaxFramesInFlight(maxNumFramesInFlight); |
| if (!status.isOk()) { |
| LOG(WARNING) << "Failed to adjust the maximum number of frames in flight."; |
| } |
| } |
| } |
| |
| /* |
| * 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 result = mEvsCamera->startVideoStream(this); |
| if (!result.isOk() or result != EvsResult::OK) { |
| LOG(ERROR) << "StreamHandler failed to start a video stream."; |
| 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<std::mutex> lock(mLock); |
| auto it = mReceivedBuffers.begin(); |
| while (it != mReceivedBuffers.end()) { |
| // Packages a returned buffer and sends it back to the camera |
| hidl_vec<BufferDesc_1_1> frames; |
| frames.resize(1); |
| frames[0] = *it; |
| auto status = mEvsCamera->doneWithFrame_1_1(frames); |
| if (!status.isOk()) { |
| LOG(WARNING) << "Failed to return a frame to EVS service; " |
| << "this may leak the memory."; |
| 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<std::mutex> lock(mLock); |
| mRunning = false; |
| return; |
| } |
| |
| // Waits until the stream has actually stopped |
| std::unique_lock<std::mutex> 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<std::mutex> lock(mLock); |
| return mRunning; |
| } |
| |
| void StreamHandler::doneWithFrame(const BufferDesc_1_1& buffer) { |
| { |
| std::lock_guard<std::mutex> lock(mLock); |
| auto it = mReceivedBuffers.begin(); |
| while (it != mReceivedBuffers.end()) { |
| if (it->bufferId == buffer.bufferId) { |
| // We intentionally do not update the iterator to detect a |
| // request to return an unknown buffer. |
| mReceivedBuffers.erase(it); |
| break; |
| } |
| ++it; |
| } |
| |
| if (it == mReceivedBuffers.end()) { |
| LOG(DEBUG) << "Ignores a request to return unknown buffer"; |
| return; |
| } |
| } |
| |
| // Packages a returned buffer and sends it back to the camera |
| hidl_vec<BufferDesc_1_1> frames; |
| frames.resize(1); |
| frames[0] = buffer; |
| auto status = mEvsCamera->doneWithFrame_1_1(frames); |
| if (!status.isOk()) { |
| LOG(ERROR) << "Failed to return a frame to EVS service; this may leak the memory."; |
| } |
| } |
| |
| Return<void> StreamHandler::deliverFrame(const BufferDesc_1_0& buffer) { |
| LOG(WARNING) << "Ignores a frame delivered from v1.0 EVS service."; |
| auto status = mEvsCamera->doneWithFrame(buffer); |
| if (!status.isOk()) { |
| LOG(ERROR) << "Failed to return a frame to EVS service; this may leak the memory."; |
| } |
| return {}; |
| } |
| |
| Return<void> StreamHandler::deliverFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffers) { |
| LOG(DEBUG) << "Received frames from the camera, bufferId = " << buffers[0].bufferId; |
| |
| // Takes the lock to protect our frameDesc slots and running state variable |
| BufferDesc_1_1 frameDesc = buffers[0]; |
| if (frameDesc.buffer.nativeHandle.getNativeHandle() == nullptr) { |
| // Signals that the last frameDesc has been received and the stream is stopped |
| LOG(WARNING) << "Invalid null frameDesc (id: 0x" << std::hex << frameDesc.bufferId |
| << ") is ignored"; |
| |
| return {}; |
| } |
| |
| size_t numBuffersInUse; |
| { |
| std::lock_guard<std::mutex> lock(mLock); |
| numBuffersInUse = mReceivedBuffers.size(); |
| } |
| |
| if (numBuffersInUse > mMaxNumFramesInFlight) { |
| // We're holding more than what allowed; returns this buffer |
| // immediately. |
| doneWithFrame(frameDesc); |
| } else { |
| { |
| std::lock_guard<std::mutex> lock(mLock); |
| // Records a new frameDesc and forwards to clients |
| mReceivedBuffers.emplace_back(frameDesc); |
| LOG(DEBUG) << "Got buffer " << frameDesc.bufferId |
| << ", total = " << mReceivedBuffers.size(); |
| |
| // Notify anybody who cares that things have changed |
| mCondition.notify_all(); |
| } |
| |
| // Forwards a new frame |
| mCallback->onNewFrame(frameDesc); |
| } |
| |
| return {}; |
| } |
| |
| Return<void> 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) |
| << " 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 {}; |
| } |
| |
| } // namespace evs |
| } // namespace automotive |
| } // namespace android |