Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a307799a authored by Scott Randolph's avatar Scott Randolph Committed by android-build-merger
Browse files

Merge "Add VTS tests for EVS HAL" into oc-dev

am: 35145b31

Change-Id: Iccc44f209348eddedd8586ee05f040c9e563572f
parents 8896e2f5 35145b31
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@
subdirs = [
subdirs = [
    "evs/1.0",
    "evs/1.0",
    "evs/1.0/default",
    "evs/1.0/default",
    "evs/1.0/vts/functional",
    "vehicle/2.0",
    "vehicle/2.0",
    "vehicle/2.1",
    "vehicle/2.1",
]
]
+45 −0
Original line number Original line Diff line number Diff line
//
// Copyright (C) 2016 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.
//

cc_test {
    name: "VtsEvsV1_0Target",

    srcs: [
        "VtsEvsV1_0TargetTest.cpp",
        "FrameHandler.cpp"
    ],

    defaults: [
        "hidl_defaults",
    ],

    shared_libs: [
        "android.hardware.automotive.evs@1.0",
        "liblog",
        "libutils",
        "libui",
        "libhidlbase",
        "libhidltransport",
    ],

    static_libs: ["VtsHalHidlTargetTestBase"],

    cflags: [
        "-O0",
        "-g",
    ],
}
+311 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

#define LOG_TAG "VtsHalEvsTest"

#include "FrameHandler.h"

#include <stdio.h>
#include <string.h>

#include <android/log.h>
#include <cutils/native_handle.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/GraphicBuffer.h>

#include <algorithm>    // std::min


// For the moment, we're assuming that the underlying EVS driver we're working with
// is providing 4 byte RGBx data.  This is fine for loopback testing, although
// real hardware is expected to provide YUV data -- most likly formatted as YV12
static const unsigned kBytesPerPixel = 4;   // assuming 4 byte RGBx pixels


FrameHandler::FrameHandler(android::sp <IEvsCamera> pCamera, CameraDesc cameraInfo,
                           android::sp <IEvsDisplay> pDisplay,
                           BufferControlFlag mode) :
    mCamera(pCamera),
    mCameraInfo(cameraInfo),
    mDisplay(pDisplay),
    mReturnMode(mode) {
    // Nothing but member initialization here...
}


void FrameHandler::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
    mCamera = nullptr;
    mDisplay = nullptr;
}


bool FrameHandler::startStream() {
    // Mark ourselves as running
    mLock.lock();
    mRunning = true;
    mLock.unlock();

    // Tell the camera to start streaming
    Return<EvsResult> result = mCamera->startVideoStream(this);
    return (result == EvsResult::OK);
}


void FrameHandler::asyncStopStream() {
    // Tell the camera to stop streaming.
    // This will result in a null frame being delivered when the stream actually stops.
    mCamera->stopVideoStream();
}


void FrameHandler::blockingStopStream() {
    // Tell the stream to stop
    asyncStopStream();

    // Wait until the stream has actually stopped
    std::unique_lock<std::mutex> lock(mLock);
    mSignal.wait(lock, [this](){ return !mRunning; });
}


bool FrameHandler::returnHeldBuffer() {
    std::unique_lock<std::mutex> lock(mLock);

    // Return the oldest buffer we're holding
    if (mHeldBuffers.empty()) {
        // No buffers are currently held
        return false;
    }

    BufferDesc buffer = mHeldBuffers.front();
    mHeldBuffers.pop();
    mCamera->doneWithFrame(buffer);

    return true;
}


bool FrameHandler::isRunning() {
    std::unique_lock<std::mutex> lock(mLock);
    return mRunning;
}


void FrameHandler::waitForFrameCount(unsigned frameCount) {
    // Wait until we've seen at least the requested number of frames (could be more)
    std::unique_lock<std::mutex> lock(mLock);
    mSignal.wait(lock, [this, frameCount](){ return mFramesReceived >= frameCount; });
}


void FrameHandler::getFramesCounters(unsigned* received, unsigned* displayed) {
    std::unique_lock<std::mutex> lock(mLock);

    if (received) {
        *received = mFramesReceived;
    }
    if (displayed) {
        *displayed = mFramesDisplayed;
    }
}


Return<void> FrameHandler::deliverFrame(const BufferDesc& bufferArg) {
    ALOGD("Received a frame from the camera (%p)", bufferArg.memHandle.getNativeHandle());

    // Local flag we use to keep track of when the stream is stopping
    bool timeToStop = false;

    // TODO:  Why do we get a gralloc crash if we don't clone the buffer here?
    BufferDesc buffer(bufferArg);
    ALOGD("Clone the received frame as %p", buffer.memHandle.getNativeHandle());

    if (buffer.memHandle.getNativeHandle() == nullptr) {
        // Signal that the last frame has been received and the stream is stopped
        timeToStop = true;
    } else {
        // If we were given an opened display at construction time, then send the received
        // image back down the camera.
        if (mDisplay.get()) {
            // Get the output buffer we'll use to display the imagery
            BufferDesc tgtBuffer = {};
            mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc& buff) {
                                          tgtBuffer = buff;
                                      }
            );

            if (tgtBuffer.memHandle == nullptr) {
                printf("Didn't get target buffer - frame lost\n");
                ALOGE("Didn't get requested output buffer -- skipping this frame.");
            } else {
                // In order for the handles passed through HIDL and stored in the BufferDesc to
                // be lockable, we must register them with GraphicBufferMapper
                registerBufferHelper(tgtBuffer);
                registerBufferHelper(buffer);

                // Copy the contents of the of buffer.memHandle into tgtBuffer
                copyBufferContents(tgtBuffer, buffer);

                // Send the target buffer back for display
                Return <EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer);
                if (!result.isOk()) {
                    printf("HIDL error on display buffer (%s)- frame lost\n",
                           result.description().c_str());
                    ALOGE("Error making the remote function call.  HIDL said %s",
                          result.description().c_str());
                } else if (result != EvsResult::OK) {
                    printf("Display reported error - frame lost\n");
                    ALOGE("We encountered error %d when returning a buffer to the display!",
                          (EvsResult) result);
                } else {
                    // Everything looks good!
                    // Keep track so tests or watch dogs can monitor progress
                    mLock.lock();
                    mFramesDisplayed++;
                    mLock.unlock();
                }

                // Now tell GraphicBufferMapper we won't be using these handles anymore
                unregisterBufferHelper(tgtBuffer);
                unregisterBufferHelper(buffer);
            }
        }


        switch (mReturnMode) {
        case eAutoReturn:
            // Send the camera buffer back now that we're done with it
            ALOGD("Calling doneWithFrame");
            // TODO:  Why is it that we get a HIDL crash if we pass back the cloned buffer?
            mCamera->doneWithFrame(bufferArg);
            break;
        case eNoAutoReturn:
            // Hang onto the buffer handle for now -- we'll return it explicitly later
            mHeldBuffers.push(bufferArg);
        }


        ALOGD("Frame handling complete");
    }


    // Update our received frame count and notify anybody who cares that things have changed
    mLock.lock();
    if (timeToStop) {
        mRunning = false;
    } else {
        mFramesReceived++;
    }
    mLock.unlock();
    mSignal.notify_all();


    return Void();
}


bool FrameHandler::copyBufferContents(const BufferDesc& tgtBuffer,
                                      const BufferDesc& srcBuffer) {
    bool success = true;

    // Make sure we don't run off the end of either buffer
    const unsigned width     = std::min(tgtBuffer.width,
                                        srcBuffer.width);
    const unsigned height    = std::min(tgtBuffer.height,
                                        srcBuffer.height);

    android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();


    // Lock our source buffer for reading
    unsigned char* srcPixels = nullptr;
    mapper.registerBuffer(srcBuffer.memHandle);
    mapper.lock(srcBuffer.memHandle,
                GRALLOC_USAGE_SW_READ_OFTEN,
                android::Rect(width, height),
                (void **) &srcPixels);

    // Lock our target buffer for writing
    unsigned char* tgtPixels = nullptr;
    mapper.registerBuffer(tgtBuffer.memHandle);
    mapper.lock(tgtBuffer.memHandle,
                GRALLOC_USAGE_SW_WRITE_OFTEN,
                android::Rect(width, height),
                (void **) &tgtPixels);

    if (srcPixels && tgtPixels) {
        for (unsigned row = 0; row < height; row++) {
            // Copy the entire row of pixel data
            memcpy(tgtPixels, srcPixels, width * kBytesPerPixel);

            // Advance to the next row (keeping in mind that stride here is in units of pixels)
            tgtPixels += tgtBuffer.stride * kBytesPerPixel;
            srcPixels += srcBuffer.stride * kBytesPerPixel;
        }
    } else {
        ALOGE("Failed to copy buffer contents");
        success = false;
    }

    if (srcPixels) {
        mapper.unlock(srcBuffer.memHandle);
    }
    if (tgtPixels) {
        mapper.unlock(tgtBuffer.memHandle);
    }
    mapper.unregisterBuffer(srcBuffer.memHandle);
    mapper.unregisterBuffer(tgtBuffer.memHandle);

    return success;
}


void FrameHandler::registerBufferHelper(const BufferDesc& buffer)
{
    // In order for the handles passed through HIDL and stored in the BufferDesc to
    // be lockable, we must register them with GraphicBufferMapper.
    // If the device upon which we're running supports gralloc1, we could just call
    // registerBuffer directly with the handle.  But that call  is broken for gralloc0 devices
    // (which we care about, at least for now).  As a result, we have to synthesize a GraphicBuffer
    // object around the buffer handle in order to make a call to the overloaded alternate
    // version of the registerBuffer call that does happen to work on gralloc0 devices.
#if REGISTER_BUFFER_ALWAYS_WORKS
    android::GraphicBufferMapper::get().registerBuffer(buffer.memHandle);
#else
    android::sp<android::GraphicBuffer> pGfxBuff = new android::GraphicBuffer(
            buffer.width, buffer.height, buffer.format,
            1, /* we always use exactly one layer */
            buffer.usage, buffer.stride,
            const_cast<native_handle_t*>(buffer.memHandle.getNativeHandle()),
            false /* GraphicBuffer should not try to free the handle */
    );

    android::GraphicBufferMapper::get().registerBuffer(pGfxBuff.get());
#endif
}


void FrameHandler::unregisterBufferHelper(const BufferDesc& buffer)
{
    // Now tell GraphicBufferMapper we won't be using these handles anymore
    android::GraphicBufferMapper::get().unregisterBuffer(buffer.memHandle);
}
+93 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

#ifndef EVS_VTS_FRAMEHANDLER_H
#define EVS_VTS_FRAMEHANDLER_H

#include <queue>

#include <android/hardware/automotive/evs/1.0/IEvsCameraStream.h>
#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>

using namespace ::android::hardware::automotive::evs::V1_0;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
using ::android::hardware::hidl_handle;
using ::android::sp;


/*
 * FrameHandler:
 * This class can be used to receive camera imagery from an IEvsCamera implementation.  Given an
 * IEvsDisplay instance at startup, it will forward the received imagery to the display,
 * providing a trivial implementation of a rear vew camera type application.
 * Note that the video frames are delivered on a background thread, while the control interface
 * is actuated from the applications foreground thread.
 */
class FrameHandler : public IEvsCameraStream {
public:
    enum BufferControlFlag {
        eAutoReturn,
        eNoAutoReturn,
    };

    FrameHandler(android::sp <IEvsCamera> pCamera, CameraDesc cameraInfo,
                 android::sp <IEvsDisplay> pDisplay = nullptr,
                 BufferControlFlag mode = eAutoReturn);
    void shutdown();

    bool startStream();
    void asyncStopStream();
    void blockingStopStream();

    bool returnHeldBuffer();

    bool isRunning();

    void waitForFrameCount(unsigned frameCount);
    void getFramesCounters(unsigned* received, unsigned* displayed);

private:
    // Implementation for ::android::hardware::automotive::evs::V1_0::ICarCameraStream
    Return<void> deliverFrame(const BufferDesc& buffer)  override;

    // Local implementation details
    bool copyBufferContents(const BufferDesc& tgtBuffer, const BufferDesc& srcBuffer);
    void registerBufferHelper(const BufferDesc& buffer);
    void unregisterBufferHelper(const BufferDesc& buffer);

    // Values initialized as startup
    android::sp <IEvsCamera>    mCamera;
    CameraDesc                  mCameraInfo;
    android::sp <IEvsDisplay>   mDisplay;
    BufferControlFlag           mReturnMode;

    // Since we get frames delivered to us asnchronously via the ICarCameraStream interface,
    // we need to protect all member variables that may be modified while we're streaming
    // (ie: those below)
    std::mutex                  mLock;
    std::condition_variable     mSignal;

    std::queue<BufferDesc>      mHeldBuffers;
    bool                        mRunning = false;
    unsigned                    mFramesReceived = 0;    // Simple counter -- rolls over eventually!
    unsigned                    mFramesDisplayed = 0;   // Simple counter -- rolls over eventually!
};


#endif //EVS_VTS_FRAMEHANDLER_H
+464 −0

File added.

Preview size limit exceeded, changes collapsed.