300 lines
9.8 KiB
C++
300 lines
9.8 KiB
C++
/*
|
|
* 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;
|
|
|
|
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), 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<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;
|
|
}
|
|
|
|
// 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
|
|
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();
|
|
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(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 >= mMaxNumFramesInFlight) {
|
|
// 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)
|
|
<< " 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
|