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

Commit 35145b31 authored by Scott Randolph's avatar Scott Randolph Committed by Android (Google) Code Review
Browse files

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

parents c499fd86 6c085589
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.