Simplify EVS HAL and move to "agressive opens"
This adapts the API implementation to allow a duplicate "open" operation to automatically close any previous connections to the device. This works around a binder level issue that can cause destructors triggered by remote clients to be delivered out of order to the server. This was originally change ag/1969959 on master, but has been recreated on oc-dev (cherry-picking was broken at the time). The original master change will be abandoned in favor of this getting merged down from oc-dev. Test: Run Vts test (added in following change) Change-Id: I7b417998e59a4d592fbb91811c4101f39097c5dd
This commit is contained in:
parent
63e15f0790
commit
de9880eece
11 changed files with 275 additions and 184 deletions
|
@ -28,10 +28,10 @@ interface IEvsCamera {
|
|||
/**
|
||||
* Returns the ID of this camera.
|
||||
*
|
||||
* Returns the string id of this camera. This must be the same value as reported in
|
||||
* the camera_id field of the CameraDesc structure by EvsEnumerator::getCamerList().
|
||||
* Returns the description of this camera. This must be the same value as reported
|
||||
* by EvsEnumerator::getCamerList().
|
||||
*/
|
||||
getId() generates (string cameraId);
|
||||
getCameraInfo() generates (CameraDesc info);
|
||||
|
||||
/**
|
||||
* Specifies the depth of the buffer chain the camera is asked to support.
|
||||
|
|
|
@ -27,7 +27,7 @@ interface IEvsDisplay {
|
|||
/**
|
||||
* Returns basic information about the EVS display provided by the system.
|
||||
*
|
||||
* See the description of the DisplayDesc structure below for details.
|
||||
* See the description of the DisplayDesc structure for details.
|
||||
*/
|
||||
getDisplayInfo() generates (DisplayDesc info);
|
||||
|
||||
|
|
|
@ -31,14 +31,14 @@ interface IEvsEnumerator {
|
|||
*/
|
||||
getCameraList() generates (vec<CameraDesc> cameras);
|
||||
|
||||
|
||||
/**
|
||||
* Get the IEvsCamera associated with a cameraId from a CameraDesc
|
||||
*
|
||||
* Given a camera's unique cameraId from ca CameraDesc, returns
|
||||
* the ICamera interface assocaited with the specified camera.
|
||||
* When done using the camera, it must be returned by calling
|
||||
* closeCamera on the ICamera interface.
|
||||
* the ICamera interface associated with the specified camera.
|
||||
* When done using the camera, the caller may release it by calling closeCamera().
|
||||
* TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
|
||||
* resources may not be released right away due to asynchronos behavior in the hardware binder.
|
||||
*/
|
||||
openCamera(string cameraId) generates (IEvsCamera carCamera);
|
||||
|
||||
|
@ -57,6 +57,9 @@ interface IEvsEnumerator {
|
|||
* There can be at most one EVS display object for the system and this function
|
||||
* requests access to it. If the EVS display is not available or is already in use,
|
||||
* a null pointer is returned.
|
||||
* When done using the display, the caller may release it by calling closeDisplay().
|
||||
* TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
|
||||
* resources may not be released right away due to asynchronos behavior in the hardware binder.
|
||||
*/
|
||||
openDisplay() generates (IEvsDisplay display);
|
||||
|
||||
|
@ -64,7 +67,7 @@ interface IEvsEnumerator {
|
|||
* Return the specified IEvsDisplay interface as no longer in use
|
||||
*
|
||||
* When the IEvsDisplay object is no longer required, it must be released.
|
||||
* NOTE: All buffer must have been returned to the display before making this call.
|
||||
* NOTE: All buffers must have been returned to the display before making this call.
|
||||
*/
|
||||
closeDisplay(IEvsDisplay display);
|
||||
|
||||
|
|
|
@ -23,4 +23,9 @@ cc_binary {
|
|||
"liblog",
|
||||
"libutils",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define LOG_TAG "android.hardware.automotive.evs@1.0-service"
|
||||
|
||||
#include "EvsCamera.h"
|
||||
#include "EvsEnumerator.h"
|
||||
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
|
@ -30,18 +31,15 @@ namespace V1_0 {
|
|||
namespace implementation {
|
||||
|
||||
|
||||
// These are the special camera names for which we'll initialize custom test data
|
||||
// Special camera names for which we'll initialize alternate test data
|
||||
const char EvsCamera::kCameraName_Backup[] = "backup";
|
||||
const char EvsCamera::kCameraName_RightTurn[] = "Right Turn";
|
||||
|
||||
|
||||
// Arbitrary limit on number of graphics buffers allowed to be allocated
|
||||
// Safeguards against unreasonable resource consumption and provides a testable limit
|
||||
const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
|
||||
|
||||
|
||||
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
|
||||
// As it stands, if the client dies suddenly, the buffer may be stranded.
|
||||
|
||||
EvsCamera::EvsCamera(const char *id) :
|
||||
mFramesAllowed(0),
|
||||
mFramesInUse(0),
|
||||
|
@ -53,22 +51,14 @@ EvsCamera::EvsCamera(const char *id) :
|
|||
|
||||
// Set up dummy data for testing
|
||||
if (mDescription.cameraId == kCameraName_Backup) {
|
||||
mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE);
|
||||
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
|
||||
mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA
|
||||
mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA
|
||||
} else if (mDescription.cameraId == kCameraName_RightTurn) {
|
||||
// Nothing but the name and the usage hint
|
||||
mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN);
|
||||
mWidth = 640; // full NTSC/VGA
|
||||
mHeight = 480; // full NTSC/VGA
|
||||
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
|
||||
} else {
|
||||
// Leave empty for a minimalist camera description without even a hint
|
||||
mWidth = 320; // 1/2 NTSC/VGA
|
||||
mHeight = 240; // 1/2 NTSC/VGA
|
||||
}
|
||||
|
||||
|
||||
// Set our buffer properties
|
||||
mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640;
|
||||
mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480;
|
||||
|
||||
mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
|
||||
|
@ -77,32 +67,49 @@ EvsCamera::EvsCamera(const char *id) :
|
|||
|
||||
EvsCamera::~EvsCamera() {
|
||||
ALOGD("EvsCamera being destroyed");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
forceShutdown();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// This gets called if another caller "steals" ownership of the camera
|
||||
//
|
||||
void EvsCamera::forceShutdown()
|
||||
{
|
||||
ALOGD("EvsCamera forceShutdown");
|
||||
|
||||
// Make sure our output stream is cleaned up
|
||||
// (It really should be already)
|
||||
stopVideoStream();
|
||||
|
||||
// Claim the lock while we work on internal state
|
||||
std::lock_guard <std::mutex> lock(mAccessLock);
|
||||
|
||||
// Drop all the graphics buffers we've been using
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.inUse) {
|
||||
ALOGE("Error - releasing buffer despite remote ownership");
|
||||
if (mBuffers.size() > 0) {
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.inUse) {
|
||||
ALOGE("Error - releasing buffer despite remote ownership");
|
||||
}
|
||||
alloc.free(rec.handle);
|
||||
rec.handle = nullptr;
|
||||
}
|
||||
alloc.free(rec.handle);
|
||||
rec.handle = nullptr;
|
||||
mBuffers.clear();
|
||||
}
|
||||
|
||||
ALOGD("EvsCamera destroyed");
|
||||
// Put this object into an unrecoverable error state since somebody else
|
||||
// is going to own the underlying camera now
|
||||
mStreamState = DEAD;
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
|
||||
Return<void> EvsCamera::getId(getId_cb id_cb) {
|
||||
ALOGD("getId");
|
||||
|
||||
id_cb(mDescription.cameraId);
|
||||
Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
|
||||
ALOGD("getCameraInfo");
|
||||
|
||||
// Send back our self description
|
||||
_hidl_cb(mDescription);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
@ -111,6 +118,12 @@ Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
|
|||
ALOGD("setMaxFramesInFlight");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
if (mStreamState == DEAD) {
|
||||
ALOGE("ignoring setMaxFramesInFlight call when camera has been lost.");
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
// We cannot function without at least one video buffer to send data
|
||||
if (bufferCount < 1) {
|
||||
ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
|
||||
|
@ -130,6 +143,11 @@ Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStre
|
|||
ALOGD("startVideoStream");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
if (mStreamState == DEAD) {
|
||||
ALOGE("ignoring startVideoStream call when camera has been lost.");
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
if (mStreamState != STOPPED) {
|
||||
ALOGE("ignoring startVideoStream call when a stream is already running.");
|
||||
return EvsResult::STREAM_ALREADY_RUNNING;
|
||||
|
@ -207,6 +225,7 @@ Return<void> EvsCamera::stopVideoStream() {
|
|||
lock.lock();
|
||||
|
||||
mStreamState = STOPPED;
|
||||
mStream = nullptr;
|
||||
ALOGD("Stream marked STOPPED.");
|
||||
}
|
||||
|
||||
|
@ -232,6 +251,12 @@ Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int3
|
|||
ALOGD("setExtendedInfo");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
if (mStreamState == DEAD) {
|
||||
ALOGE("ignoring setExtendedInfo call when camera has been lost.");
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
// We don't store any device specific information in this implementation
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
|
@ -358,7 +383,9 @@ void EvsCamera::generateFrames() {
|
|||
|
||||
while (true) {
|
||||
bool timeForFrame = false;
|
||||
// Lock scope
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
|
||||
// Lock scope for updating shared state
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
|
@ -427,8 +454,15 @@ void EvsCamera::generateFrames() {
|
|||
}
|
||||
}
|
||||
|
||||
// We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
|
||||
usleep(100000);
|
||||
// We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
|
||||
static const int kTargetFrameRate = 12;
|
||||
static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
|
||||
const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
const nsecs_t workTimeUs = (now - startTime) / 1000;
|
||||
const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
|
||||
if (sleepDurationUs > 0) {
|
||||
usleep(sleepDurationUs);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've been asked to stop, send one last NULL frame to signal the actual end of stream
|
||||
|
|
|
@ -32,54 +32,50 @@ namespace V1_0 {
|
|||
namespace implementation {
|
||||
|
||||
|
||||
// From EvsEnumerator.h
|
||||
class EvsEnumerator;
|
||||
|
||||
|
||||
class EvsCamera : public IEvsCamera {
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
|
||||
Return<void> getId(getId_cb id_cb) override;
|
||||
|
||||
Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
|
||||
Return <EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
|
||||
|
||||
Return <EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream>& stream) override;
|
||||
|
||||
Return<void> doneWithFrame(const BufferDesc& buffer) override;
|
||||
|
||||
Return<void> stopVideoStream() override;
|
||||
|
||||
Return <int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
|
||||
|
||||
Return <EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
|
||||
|
||||
// Implementation details
|
||||
EvsCamera(const char* id);
|
||||
|
||||
EvsCamera(const char *id);
|
||||
virtual ~EvsCamera() override;
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the camera
|
||||
|
||||
const CameraDesc& getDesc() { return mDescription; };
|
||||
|
||||
static const char kCameraName_Backup[];
|
||||
static const char kCameraName_RightTurn[];
|
||||
|
||||
private:
|
||||
// These three functions are expected to be called while mAccessLock is held
|
||||
bool setAvailableFrames_Locked(unsigned bufferCount);
|
||||
|
||||
unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
|
||||
|
||||
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
|
||||
|
||||
void generateFrames();
|
||||
|
||||
void fillTestFrame(const BufferDesc& buff);
|
||||
|
||||
CameraDesc mDescription = {}; // The properties of this camera
|
||||
sp<EvsEnumerator> mEnumerator; // The enumerator object that created this camera
|
||||
|
||||
CameraDesc mDescription = {}; // The properties of this camera
|
||||
|
||||
std::thread mCaptureThread; // The thread we'll use to synthesize frames
|
||||
|
||||
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
|
||||
uint32_t mHeight = 0; // Vertical pixel count in the buffers
|
||||
uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
|
||||
uint32_t mUsage = 0; // Values from from Gralloc.h
|
||||
uint32_t mStride = 0; // Bytes per line in the buffers
|
||||
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
|
||||
uint32_t mHeight = 0; // Vertical pixel count in the buffers
|
||||
uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
|
||||
uint32_t mUsage = 0; // Values from from Gralloc.h
|
||||
uint32_t mStride = 0; // Bytes per line in the buffers
|
||||
|
||||
sp <IEvsCameraStream> mStream = nullptr; // The callback used to deliver each frame
|
||||
|
||||
|
@ -98,10 +94,11 @@ private:
|
|||
STOPPED,
|
||||
RUNNING,
|
||||
STOPPING,
|
||||
DEAD,
|
||||
};
|
||||
StreamStateValues mStreamState;
|
||||
|
||||
// Syncrhonization necessary to deconflict mCaptureThread from the main service thread
|
||||
// Synchronization necessary to deconflict mCaptureThread from the main service thread
|
||||
std::mutex mAccessLock;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,12 +30,6 @@ namespace V1_0 {
|
|||
namespace implementation {
|
||||
|
||||
|
||||
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
|
||||
// As it stands, if the client dies suddently, the buffer may be stranded.
|
||||
// As possible work around would be to give the client a HIDL object to exclusively hold
|
||||
// and use it's destructor to perform some work in the server side.
|
||||
|
||||
|
||||
EvsDisplay::EvsDisplay() {
|
||||
ALOGD("EvsDisplay instantiated");
|
||||
|
||||
|
@ -43,34 +37,55 @@ EvsDisplay::EvsDisplay() {
|
|||
// NOTE: These are arbitrary values chosen for testing
|
||||
mInfo.displayId = "Mock Display";
|
||||
mInfo.vendorFlags = 3870;
|
||||
mInfo.defaultHorResolution = 320;
|
||||
mInfo.defaultVerResolution = 240;
|
||||
|
||||
// Assemble the buffer description we'll use for our render target
|
||||
mBuffer.width = 320;
|
||||
mBuffer.height = 240;
|
||||
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
|
||||
mBuffer.pixelSize = 4;
|
||||
}
|
||||
|
||||
|
||||
EvsDisplay::~EvsDisplay() {
|
||||
ALOGD("EvsDisplay being destroyed");
|
||||
forceShutdown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This gets called if another caller "steals" ownership of the display
|
||||
*/
|
||||
void EvsDisplay::forceShutdown()
|
||||
{
|
||||
ALOGD("EvsDisplay forceShutdown");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// Report if we're going away while a buffer is outstanding
|
||||
if (mFrameBusy) {
|
||||
ALOGE("EvsDisplay going down while client is holding a buffer");
|
||||
}
|
||||
|
||||
// Make sure we release our frame buffer
|
||||
// If the buffer isn't being held by a remote client, release it now as an
|
||||
// optimization to release the resources more quickly than the destructor might
|
||||
// get called.
|
||||
if (mBuffer.memHandle) {
|
||||
// Report if we're going away while a buffer is outstanding
|
||||
if (mFrameBusy) {
|
||||
ALOGE("EvsDisplay going down while client is holding a buffer");
|
||||
}
|
||||
|
||||
// Drop the graphics buffer we've been using
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
alloc.free(mBuffer.memHandle);
|
||||
mBuffer.memHandle = nullptr;
|
||||
}
|
||||
ALOGD("EvsDisplay destroyed");
|
||||
|
||||
// Put this object into an unrecoverable error state since somebody else
|
||||
// is going to own the display now.
|
||||
mRequestedState = DisplayState::DEAD;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns basic information about the EVS display provided by the system.
|
||||
* See the description of the DisplayDesc structure below for details.
|
||||
* See the description of the DisplayDesc structure for details.
|
||||
*/
|
||||
Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
|
||||
ALOGD("getDisplayInfo");
|
||||
|
@ -94,6 +109,11 @@ Return<EvsResult> EvsDisplay::setDisplayState(DisplayState state) {
|
|||
ALOGD("setDisplayState");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
// This object no longer owns the display -- it's been superceeded!
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
// Ensure we recognize the requested state so we don't go off the rails
|
||||
if (state < DisplayState::NUM_STATES) {
|
||||
// Record the requested state
|
||||
|
@ -119,10 +139,7 @@ Return<DisplayState> EvsDisplay::getDisplayState() {
|
|||
ALOGD("getDisplayState");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// At the moment, we treat the requested state as immediately active
|
||||
DisplayState currentState = mRequestedState;
|
||||
|
||||
return currentState;
|
||||
return mRequestedState;
|
||||
}
|
||||
|
||||
|
||||
|
@ -137,15 +154,16 @@ Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
|
|||
ALOGD("getTargetBuffer");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
ALOGE("Rejecting buffer request from object that lost ownership of the display.");
|
||||
BufferDesc nullBuff = {};
|
||||
_hidl_cb(nullBuff);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// If we don't already have a buffer, allocate one now
|
||||
if (!mBuffer.memHandle) {
|
||||
// Assemble the buffer description we'll use for our render target
|
||||
mBuffer.width = mInfo.defaultHorResolution;
|
||||
mBuffer.height = mInfo.defaultVerResolution;
|
||||
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
|
||||
|
||||
// Allocate the buffer that will hold our displayable image
|
||||
buffer_handle_t handle = nullptr;
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
status_t result = alloc.allocate(mBuffer.width, mBuffer.height,
|
||||
|
@ -220,6 +238,11 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc& buf
|
|||
|
||||
mFrameBusy = false;
|
||||
|
||||
// If we've been displaced by another owner of the display, then we can't do anything else
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
// If we were waiting for a new frame, this is it!
|
||||
if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
|
||||
mRequestedState = DisplayState::VISIBLE;
|
||||
|
@ -248,8 +271,8 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc& buf
|
|||
|
||||
// Check the test pixels
|
||||
bool frameLooksGood = true;
|
||||
for (unsigned row = 0; row < mInfo.defaultVerResolution; row++) {
|
||||
for (unsigned col = 0; col < mInfo.defaultHorResolution; col++) {
|
||||
for (unsigned row = 0; row < mBuffer.height; row++) {
|
||||
for (unsigned col = 0; col < mBuffer.width; col++) {
|
||||
// Index into the row to check the pixel at this column.
|
||||
// We expect 0xFF in the LSB channel, a vertical gradient in the
|
||||
// second channel, a horitzontal gradient in the third channel, and
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace evs {
|
|||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
|
||||
class EvsDisplay : public IEvsDisplay {
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
|
||||
|
@ -40,6 +41,8 @@ public:
|
|||
EvsDisplay();
|
||||
virtual ~EvsDisplay() override;
|
||||
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
|
||||
|
||||
private:
|
||||
DisplayDesc mInfo = {};
|
||||
BufferDesc mBuffer = {}; // A graphics buffer into which we'll store images
|
||||
|
|
|
@ -28,33 +28,36 @@ namespace V1_0 {
|
|||
namespace implementation {
|
||||
|
||||
|
||||
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
|
||||
// As it stands, if the client dies suddenly, the camera will be stuck "open".
|
||||
// NOTE: Display should already be safe by virtue of holding only a weak pointer.
|
||||
// NOTE: All members values are static so that all clients operate on the same state
|
||||
// That is to say, this is effectively a singleton despite the fact that HIDL
|
||||
// constructs a new instance for each client.
|
||||
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
|
||||
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
|
||||
|
||||
|
||||
EvsEnumerator::EvsEnumerator() {
|
||||
ALOGD("EvsEnumerator created");
|
||||
|
||||
// Add sample camera data to our list of cameras
|
||||
// NOTE: The id strings trigger special initialization inside the EvsCamera constructor
|
||||
mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_Backup), false );
|
||||
mCameraList.emplace_back( new EvsCamera("LaneView"), false );
|
||||
mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_RightTurn), false );
|
||||
// In a real driver, this would be expected to can the available hardware
|
||||
sCameraList.emplace_back(EvsCamera::kCameraName_Backup);
|
||||
sCameraList.emplace_back("LaneView");
|
||||
sCameraList.emplace_back("right turn");
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
|
||||
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
|
||||
ALOGD("getCameraList");
|
||||
|
||||
const unsigned numCameras = mCameraList.size();
|
||||
const unsigned numCameras = sCameraList.size();
|
||||
|
||||
// Build up a packed array of CameraDesc for return
|
||||
// NOTE: Only has to live until the callback returns
|
||||
std::vector<CameraDesc> descriptions;
|
||||
descriptions.reserve(numCameras);
|
||||
for (const auto& cam : mCameraList) {
|
||||
descriptions.push_back( cam.pCamera->getDesc() );
|
||||
for (const auto& cam : sCameraList) {
|
||||
descriptions.push_back( cam.desc );
|
||||
}
|
||||
|
||||
// Encapsulate our camera descriptions in the HIDL vec type
|
||||
|
@ -68,97 +71,137 @@ Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
|
|||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
|
||||
ALOGD("openCamera");
|
||||
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : mCameraList) {
|
||||
if (cam.pCamera->getDesc().cameraId == cameraId) {
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a recognized camera id?
|
||||
if (!pRecord) {
|
||||
ALOGE("Requested camera %s not found", cameraId.c_str());
|
||||
return nullptr;
|
||||
} else if (pRecord->inUse) {
|
||||
ALOGE("Cannot open camera %s which is already in use", cameraId.c_str());
|
||||
return nullptr;
|
||||
} else {
|
||||
pRecord->inUse = true;
|
||||
return(pRecord->pCamera);
|
||||
}
|
||||
|
||||
// Has this camera already been instantiated by another caller?
|
||||
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
|
||||
if (pActiveCamera != nullptr) {
|
||||
ALOGW("Killing previous camera because of new caller");
|
||||
closeCamera(pActiveCamera);
|
||||
}
|
||||
|
||||
// Construct a camera instance for the caller
|
||||
pActiveCamera = new EvsCamera(cameraId);
|
||||
pRecord->activeInstance = pActiveCamera;
|
||||
if (pActiveCamera == nullptr) {
|
||||
ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
|
||||
}
|
||||
|
||||
return pActiveCamera;
|
||||
}
|
||||
|
||||
Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& camera) {
|
||||
|
||||
Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
|
||||
ALOGD("closeCamera");
|
||||
|
||||
if (camera == nullptr) {
|
||||
ALOGE("Ignoring call to closeCamera with null camera pointer");
|
||||
} else {
|
||||
// Find this camera in our list
|
||||
auto it = std::find_if(mCameraList.begin(),
|
||||
mCameraList.end(),
|
||||
[camera](const CameraRecord& rec) {
|
||||
return (rec.pCamera == camera);
|
||||
});
|
||||
if (it == mCameraList.end()) {
|
||||
ALOGE("Ignoring close on unrecognized camera");
|
||||
} else {
|
||||
// Make sure the camera has stopped streaming
|
||||
camera->stopVideoStream();
|
||||
if (pCamera == nullptr) {
|
||||
ALOGE("Ignoring call to closeCamera with null camera ptr");
|
||||
return Void();
|
||||
}
|
||||
|
||||
it->inUse = false;
|
||||
// Get the camera id so we can find it in our list
|
||||
std::string cameraId;
|
||||
pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
|
||||
// TODO(b/36532780) Should we able to just use a simple assignment?
|
||||
// cameraId = desc.cameraId;
|
||||
cameraId.assign(desc.cameraId.c_str());
|
||||
}
|
||||
);
|
||||
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Is the display being destroyed actually the one we think is active?
|
||||
if (!pRecord) {
|
||||
ALOGE("Asked to close a camera who's name isn't recognized");
|
||||
} else {
|
||||
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
|
||||
|
||||
if (pActiveCamera == nullptr) {
|
||||
ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
|
||||
} else if (pActiveCamera != pCamera) {
|
||||
// This can happen if the camera was aggressively reopened, orphaning this previous instance
|
||||
ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
|
||||
} else {
|
||||
// Drop the active camera
|
||||
pActiveCamera->forceShutdown();
|
||||
pRecord->activeInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
|
||||
ALOGD("openDisplay");
|
||||
|
||||
// If we already have a display active, then this request must be denied
|
||||
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
|
||||
// If we already have a display active, then we need to shut it down so we can
|
||||
// give exclusive access to the new caller.
|
||||
sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
|
||||
if (pActiveDisplay != nullptr) {
|
||||
ALOGW("Rejecting openDisplay request the display is already in use.");
|
||||
return nullptr;
|
||||
} else {
|
||||
// Create a new display interface and return it
|
||||
pActiveDisplay = new EvsDisplay();
|
||||
mActiveDisplay = pActiveDisplay;
|
||||
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
|
||||
return pActiveDisplay;
|
||||
ALOGW("Killing previous display because of new caller");
|
||||
closeDisplay(pActiveDisplay);
|
||||
}
|
||||
|
||||
// Create a new display interface and return it
|
||||
pActiveDisplay = new EvsDisplay();
|
||||
sActiveDisplay = pActiveDisplay;
|
||||
|
||||
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
|
||||
return pActiveDisplay;
|
||||
}
|
||||
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& display) {
|
||||
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
|
||||
ALOGD("closeDisplay");
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
|
||||
|
||||
sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
|
||||
if (pActiveDisplay == nullptr) {
|
||||
ALOGE("Ignoring closeDisplay when there is no active display.");
|
||||
} else if (display != pActiveDisplay) {
|
||||
ALOGE("Ignoring closeDisplay on a display we didn't issue");
|
||||
ALOGI("Got %p while active display is %p.", display.get(), pActiveDisplay.get());
|
||||
ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed");
|
||||
} else if (sActiveDisplay != pDisplay) {
|
||||
ALOGW("Ignoring close of previously orphaned display - why did a client steal?");
|
||||
} else {
|
||||
// Drop the active display
|
||||
mActiveDisplay = nullptr;
|
||||
pActiveDisplay->forceShutdown();
|
||||
sActiveDisplay = nullptr;
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<DisplayState> EvsEnumerator::getDisplayState() {
|
||||
ALOGD("getDisplayState");
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
|
||||
sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
|
||||
if (pActiveDisplay != nullptr) {
|
||||
return pActiveDisplay->getDisplayState();
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
#include "EvsCamera.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
|
@ -31,6 +30,11 @@ namespace evs {
|
|||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
|
||||
class EvsCamera; // from EvsCamera.h
|
||||
class EvsDisplay; // from EvsDisplay.h
|
||||
|
||||
|
||||
class EvsEnumerator : public IEvsEnumerator {
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
|
||||
|
@ -45,14 +49,18 @@ public:
|
|||
EvsEnumerator();
|
||||
|
||||
private:
|
||||
// NOTE: All members values are static so that all clients operate on the same state
|
||||
// That is to say, this is effectively a singleton despite the fact that HIDL
|
||||
// constructs a new instance for each client.
|
||||
struct CameraRecord {
|
||||
sp<EvsCamera> pCamera;
|
||||
bool inUse;
|
||||
CameraRecord(EvsCamera* p, bool b) : pCamera(p), inUse(b) {}
|
||||
};
|
||||
std::list<CameraRecord> mCameraList;
|
||||
CameraDesc desc;
|
||||
wp<EvsCamera> activeInstance;
|
||||
|
||||
wp<IEvsDisplay> mActiveDisplay; // Weak pointer -> object destructs if client dies
|
||||
CameraRecord(const char *cameraId) : desc() { desc.cameraId = cameraId; }
|
||||
};
|
||||
static std::list<CameraRecord> sCameraList;
|
||||
|
||||
static wp<EvsDisplay> sActiveDisplay; // Weak pointer. Object destructs if client dies.
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
|
|
@ -17,41 +17,15 @@
|
|||
package android.hardware.automotive.evs@1.0;
|
||||
|
||||
|
||||
/**
|
||||
* Bit flags indicating suggested uses for a given EVS camera
|
||||
*
|
||||
* The values in the UsageHint bit field provide a generic expression of how a
|
||||
* given camera is intended to be used. The values for these flags support
|
||||
* existing use cases, and are used by the default EVS application to select
|
||||
* appropriate cameras for display based on observed vehicle state (such as
|
||||
* turn signal activation or selection of reverse gear). When implementing
|
||||
* their own specialized EVS Applications, OEMs are free to use these flags
|
||||
* and/or the opaque vendor_flags to drive their own vehicle specific logic.
|
||||
*/
|
||||
enum UsageHint : uint32_t {
|
||||
USAGE_HINT_REVERSE = 0x00000001,
|
||||
USAGE_HINT_LEFT_TURN = 0x00000002,
|
||||
USAGE_HINT_RIGHT_TURN = 0x00000004,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Structure describing the basic properties of an EVS camera
|
||||
*
|
||||
* The HAL is responsible for filling out this structure for each
|
||||
* EVS camera in the system. Attention should be given to the field
|
||||
* of view, direction of view, and location parameters as these may
|
||||
* be used to (if available) to project overlay graphics into the
|
||||
* scene by an EVS application.
|
||||
* Any of these values for which the HAL does not have reasonable values
|
||||
* should be set to ZERO.
|
||||
* EVS camera in the system.
|
||||
*/
|
||||
struct CameraDesc {
|
||||
string cameraId;
|
||||
bitfield<UsageHint> hints; // Mask of usage hints
|
||||
uint32_t vendorFlags; // Opaque value from driver
|
||||
uint32_t defaultHorResolution; // Units of pixels
|
||||
uint32_t defaultVerResolution; // Units of pixels
|
||||
string cameraId;
|
||||
uint32_t vendorFlags; // Opaque value from driver
|
||||
};
|
||||
|
||||
|
||||
|
@ -65,9 +39,7 @@ struct CameraDesc {
|
|||
*/
|
||||
struct DisplayDesc {
|
||||
string displayId;
|
||||
uint32_t vendorFlags; // Opaque value from driver
|
||||
uint32_t defaultHorResolution; // Units of pixels
|
||||
uint32_t defaultVerResolution; // Units of pixels
|
||||
uint32_t vendorFlags; // Opaque value from driver
|
||||
};
|
||||
|
||||
|
||||
|
@ -86,7 +58,8 @@ struct DisplayDesc {
|
|||
struct BufferDesc {
|
||||
uint32_t width; // Units of pixels
|
||||
uint32_t height; // Units of pixels
|
||||
uint32_t stride; // Units of bytes
|
||||
uint32_t stride; // Units of pixels to match gralloc
|
||||
uint32_t pixelSize; // Units of bytes
|
||||
uint32_t format; // May contain values from android_pixel_format_t
|
||||
uint32_t usage; // May contain values from from Gralloc.h
|
||||
uint32_t bufferId; // Opaque value from driver
|
||||
|
@ -108,6 +81,7 @@ enum DisplayState : uint32_t {
|
|||
NOT_VISIBLE, // Display is inhibited
|
||||
VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame
|
||||
VISIBLE, // Display is currently active
|
||||
DEAD, // Driver is in an undefined state. Interface should be closed.
|
||||
NUM_STATES // Must be last
|
||||
};
|
||||
|
||||
|
@ -118,5 +92,6 @@ enum EvsResult : uint32_t {
|
|||
INVALID_ARG,
|
||||
STREAM_ALREADY_RUNNING,
|
||||
BUFFER_NOT_AVAILABLE,
|
||||
OWNERSHIP_LOST,
|
||||
UNDERLYING_SERVICE_ERROR,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue