diff options
| author | Changyeon Jo <changyeon@google.com> | 2020-03-29 07:50:53 -0700 |
|---|---|---|
| committer | Changyeon Jo <changyeon@google.com> | 2020-04-02 13:10:40 -0700 |
| commit | 6e90a3b41bd62b111a6fcba87eaebf4388e1098a (patch) | |
| tree | 2b9fe0e9b075693b8a0894c9659fff45a04b48de /evs | |
| parent | fe92ef3972ff0f54ebb68bfb855b5a3d06c572a5 (diff) | |
| download | platform_packages_services_Car-6e90a3b41bd62b111a6fcba87eaebf4388e1098a.tar.gz platform_packages_services_Car-6e90a3b41bd62b111a6fcba87eaebf4388e1098a.tar.bz2 platform_packages_services_Car-6e90a3b41bd62b111a6fcba87eaebf4388e1098a.zip | |
Use user-allocated buffers to capture frames
This change implements importExternal() API that allows EVS HAL
implementation to import buffers allocated by EVS clients.
Bug: 152493110
Test: m -j and run /system/bin/evs_app --test
Change-Id: I29e917fce5c353eee8697bd060f6d667706886b3
Diffstat (limited to 'evs')
| -rw-r--r-- | evs/apps/default/ConfigManager.h | 5 | ||||
| -rw-r--r-- | evs/apps/default/EvsStateControl.cpp | 3 | ||||
| -rw-r--r-- | evs/apps/default/RenderDirectView.cpp | 9 | ||||
| -rw-r--r-- | evs/apps/default/RenderDirectView.h | 4 | ||||
| -rw-r--r-- | evs/apps/default/StreamHandler.cpp | 78 | ||||
| -rw-r--r-- | evs/apps/default/StreamHandler.h | 8 | ||||
| -rw-r--r-- | evs/apps/default/VideoTex.cpp | 9 | ||||
| -rw-r--r-- | evs/apps/default/VideoTex.h | 6 | ||||
| -rw-r--r-- | evs/apps/default/evs_app.cpp | 5 | ||||
| -rw-r--r-- | evs/manager/1.1/HalCamera.cpp | 52 | ||||
| -rw-r--r-- | evs/manager/1.1/HalCamera.h | 2 | ||||
| -rw-r--r-- | evs/manager/1.1/VirtualCamera.cpp | 28 | ||||
| -rw-r--r-- | evs/manager/1.1/VirtualCamera.h | 3 | ||||
| -rw-r--r-- | evs/sampleDriver/EvsV4lCamera.cpp | 90 | ||||
| -rw-r--r-- | evs/sampleDriver/EvsV4lCamera.h | 5 |
15 files changed, 284 insertions, 23 deletions
diff --git a/evs/apps/default/ConfigManager.h b/evs/apps/default/ConfigManager.h index b3d283e33..6acd83dbf 100644 --- a/evs/apps/default/ConfigManager.h +++ b/evs/apps/default/ConfigManager.h @@ -89,6 +89,8 @@ public: } const std::vector<DisplayInfo>& getDisplays() const { return mDisplays; }; const DisplayInfo& getActiveDisplay() const { return mDisplays[mActiveDisplayId]; }; + void useExternalMemory(bool flag) { mUseExternalMemory = flag; } + bool getUseExternalMemory() const { return mUseExternalMemory; } private: // Camera information @@ -98,6 +100,9 @@ private: std::vector<DisplayInfo> mDisplays; int mActiveDisplayId; + // Memory management + bool mUseExternalMemory; + // Car body information (assumes front wheel steering and origin at center of rear axel) // Note that units aren't specified and don't matter as long as all length units are consistent // within the JSON file from which we parse. That is, if everything is in meters, that's fine. diff --git a/evs/apps/default/EvsStateControl.cpp b/evs/apps/default/EvsStateControl.cpp index b96355102..e45fe2fa4 100644 --- a/evs/apps/default/EvsStateControl.cpp +++ b/evs/apps/default/EvsStateControl.cpp @@ -303,7 +303,8 @@ bool EvsStateControl::configureEvsPipeline(State desiredState) { if (mCameraList[desiredState].size() == 1) { // We have a camera assigned to this state for direct view. mDesiredRenderer = std::make_unique<RenderDirectView>(mEvs, - mCameraDescList[desiredState][0]); + mCameraDescList[desiredState][0], + mConfig); if (!mDesiredRenderer) { LOG(ERROR) << "Failed to construct direct renderer. Skipping state change."; return false; diff --git a/evs/apps/default/RenderDirectView.cpp b/evs/apps/default/RenderDirectView.cpp index 4a8db7035..293852168 100644 --- a/evs/apps/default/RenderDirectView.cpp +++ b/evs/apps/default/RenderDirectView.cpp @@ -42,9 +42,11 @@ const size_t kStreamCfgSz = sizeof(RawStreamConfig); RenderDirectView::RenderDirectView(sp<IEvsEnumerator> enumerator, - const CameraDesc& camDesc) : + const CameraDesc& camDesc, + const ConfigManager& config) : mEnumerator(enumerator), - mCameraDesc(camDesc) { + mCameraDesc(camDesc), + mConfig(config) { /* Nothing to do */ } @@ -113,7 +115,8 @@ bool RenderDirectView::activate() { mTexture.reset(createVideoTexture(mEnumerator, mCameraDesc.v1.cameraId.c_str(), foundCfg ? std::move(targetCfg) : nullptr, - sDisplay)); + sDisplay, + mConfig.getUseExternalMemory())); if (!mTexture) { LOG(ERROR) << "Failed to set up video texture for " << mCameraDesc.v1.cameraId; // TODO: For production use, we may actually want to fail in this case, but not yet... diff --git a/evs/apps/default/RenderDirectView.h b/evs/apps/default/RenderDirectView.h index 3514e05b8..65a94e2fd 100644 --- a/evs/apps/default/RenderDirectView.h +++ b/evs/apps/default/RenderDirectView.h @@ -35,7 +35,8 @@ using ::android::hardware::camera::device::V3_2::Stream; class RenderDirectView: public RenderBase { public: RenderDirectView(sp<IEvsEnumerator> enumerator, - const CameraDesc& camDesc); + const CameraDesc& camDesc, + const ConfigManager& config); virtual bool activate() override; virtual void deactivate() override; @@ -46,6 +47,7 @@ protected: sp<IEvsEnumerator> mEnumerator; ConfigManager::CameraInfo mCameraInfo; CameraDesc mCameraDesc; + const ConfigManager& mConfig; std::unique_ptr<VideoTex> mTexture; diff --git a/evs/apps/default/StreamHandler.cpp b/evs/apps/default/StreamHandler.cpp index 328f4546b..b1cfd1fa5 100644 --- a/evs/apps/default/StreamHandler.cpp +++ b/evs/apps/default/StreamHandler.cpp @@ -21,16 +21,73 @@ #include <android-base/logging.h> #include <cutils/native_handle.h> +#include <ui/GraphicBufferAllocator.h> using ::android::hardware::automotive::evs::V1_0::EvsResult; -StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera) : - mCamera(pCamera) -{ - // 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. - pCamera->setMaxFramesInFlight(2); +buffer_handle_t memHandle = nullptr; +StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera, + uint32_t numBuffers, + bool useOwnBuffers, + int32_t width, + int32_t height) + : mCamera(pCamera), + mUseOwnBuffers(useOwnBuffers) { + if (!useOwnBuffers) { + // 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. + pCamera->setMaxFramesInFlight(numBuffers); + } else { + mOwnBuffers.resize(numBuffers); + + // Acquire the graphics buffer allocator + android::GraphicBufferAllocator &alloc(android::GraphicBufferAllocator::get()); + const auto usage = GRALLOC_USAGE_HW_TEXTURE | + GRALLOC_USAGE_SW_READ_RARELY | + GRALLOC_USAGE_SW_WRITE_OFTEN; + const auto format = HAL_PIXEL_FORMAT_RGBA_8888; + for (auto i = 0; i < numBuffers; ++i) { + unsigned pixelsPerLine; + android::status_t result = alloc.allocate(width, + height, + format, + 1, + usage, + &memHandle, + &pixelsPerLine, + 0, + "EvsApp"); + if (result != android::NO_ERROR) { + LOG(ERROR) << __FUNCTION__ << " failed to allocate memory."; + } else { + BufferDesc_1_1 buf; + AHardwareBuffer_Desc* pDesc = + reinterpret_cast<AHardwareBuffer_Desc *>(&buf.buffer.description); + pDesc->width = 640; + pDesc->height = 360; + pDesc->layers = 1; + pDesc->format = HAL_PIXEL_FORMAT_RGBA_8888; + pDesc->usage = GRALLOC_USAGE_HW_TEXTURE | + GRALLOC_USAGE_SW_READ_RARELY | + GRALLOC_USAGE_SW_WRITE_OFTEN; + pDesc->stride = pixelsPerLine; + buf.buffer.nativeHandle = memHandle; + buf.bufferId = i; // Unique number to identify this buffer + mOwnBuffers[i] = buf; + } + } + + int delta = 0; + EvsResult result = EvsResult::OK; + pCamera->importExternalBuffers(mOwnBuffers, + [&](auto _result, auto _delta) { + result = _result; + delta = _delta; + }); + + LOG(INFO) << delta << " buffers are imported by EVS."; + } } @@ -42,6 +99,15 @@ void StreamHandler::shutdown() // 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; + + if (mUseOwnBuffers) { + android::GraphicBufferAllocator &alloc(android::GraphicBufferAllocator::get()); + for (auto& b : mOwnBuffers) { + alloc.free(b.buffer.nativeHandle); + } + + mOwnBuffers.resize(0); + } } diff --git a/evs/apps/default/StreamHandler.h b/evs/apps/default/StreamHandler.h index cb7a288e9..f877c78af 100644 --- a/evs/apps/default/StreamHandler.h +++ b/evs/apps/default/StreamHandler.h @@ -48,7 +48,11 @@ class StreamHandler : public IEvsCameraStream { public: virtual ~StreamHandler() { shutdown(); }; - StreamHandler(android::sp <IEvsCamera> pCamera); + StreamHandler(android::sp <IEvsCamera> pCamera, + uint32_t numBuffers = 2, + bool useOwnBuffers = false, + int32_t width = 640, + int32_t height = 360); void shutdown(); bool startStream(); @@ -83,6 +87,8 @@ private: BufferDesc mBuffers[2]; int mHeldBuffer = -1; // Index of the one currently held by the client int mReadyBuffer = -1; // Index of the newest available buffer + hidl_vec<BufferDesc_1_1> mOwnBuffers; + bool mUseOwnBuffers; }; diff --git a/evs/apps/default/VideoTex.cpp b/evs/apps/default/VideoTex.cpp index c9fc8955f..94e734a74 100644 --- a/evs/apps/default/VideoTex.cpp +++ b/evs/apps/default/VideoTex.cpp @@ -136,7 +136,8 @@ bool VideoTex::refresh() { VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum, const char* evsCameraId, std::unique_ptr<Stream> streamCfg, - EGLDisplay glDisplay) { + EGLDisplay glDisplay, + bool useExternalMemory) { // Set up the camera to feed this texture sp<IEvsCamera> pCamera = nullptr; if (streamCfg != nullptr) { @@ -153,7 +154,11 @@ VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum, } // Initialize the stream that will help us update this texture's contents - sp<StreamHandler> pStreamHandler = new StreamHandler(pCamera); + sp<StreamHandler> pStreamHandler = new StreamHandler(pCamera, + 2, // number of buffers + useExternalMemory, + streamCfg->width, + streamCfg->height); if (pStreamHandler.get() == nullptr) { LOG(ERROR) << "Failed to allocate FrameHandler"; return nullptr; diff --git a/evs/apps/default/VideoTex.h b/evs/apps/default/VideoTex.h index 00e9faa8e..d884faadc 100644 --- a/evs/apps/default/VideoTex.h +++ b/evs/apps/default/VideoTex.h @@ -37,7 +37,8 @@ class VideoTex: public TexWrapper { friend VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum, const char *evsCameraId, std::unique_ptr<Stream> streamCfg, - EGLDisplay glDisplay); + EGLDisplay glDisplay, + bool useExternalMemory); public: VideoTex() = delete; @@ -64,6 +65,7 @@ private: VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum, const char * deviceName, std::unique_ptr<Stream> streamCfg, - EGLDisplay glDisplay); + EGLDisplay glDisplay, + bool useExternalMemory = false); #endif // VIDEOTEX_H diff --git a/evs/apps/default/evs_app.cpp b/evs/apps/default/evs_app.cpp index 65519c7c2..a968990d1 100644 --- a/evs/apps/default/evs_app.cpp +++ b/evs/apps/default/evs_app.cpp @@ -76,6 +76,7 @@ int main(int argc, char** argv) bool printHelp = false; const char* evsServiceName = "default"; int displayId = 1; + bool useExternalMemory = false; for (int i=1; i< argc; i++) { if (strcmp(argv[i], "--test") == 0) { useVehicleHal = false; @@ -87,6 +88,8 @@ int main(int argc, char** argv) printHelp = true; } else if (strcmp(argv[i], "--display") == 0) { displayId = std::stoi(argv[++i]); + } else if (strcmp(argv[i], "--extmem") == 0) { + useExternalMemory = true; } else { printf("Ignoring unrecognized command line arg '%s'\n", argv[i]); printHelp = true; @@ -98,6 +101,7 @@ int main(int argc, char** argv) printf(" --hw Bypass EvsManager by connecting directly to EvsEnumeratorHw\n"); printf(" --mock Connect directly to EvsEnumeratorHw-Mock\n"); printf(" --display Specify the display to use\n"); + printf(" --extmem Application allocates buffers to capture camera frames\n"); } // Load our configuration information @@ -135,6 +139,7 @@ int main(int argc, char** argv) return 1; } config.setActiveDisplayId(displayId); + config.useExternalMemory(useExternalMemory); // Connect to the Vehicle HAL so we can monitor state sp<IVehicle> pVnet; diff --git a/evs/manager/1.1/HalCamera.cpp b/evs/manager/1.1/HalCamera.cpp index 902366edc..fef7e9eb0 100644 --- a/evs/manager/1.1/HalCamera.cpp +++ b/evs/manager/1.1/HalCamera.cpp @@ -153,6 +153,58 @@ bool HalCamera::changeFramesInFlight(int delta) { } +bool HalCamera::changeFramesInFlight(const hidl_vec<BufferDesc_1_1>& buffers, + int* delta) { + // Return immediately if a list is empty. + if (buffers.size() < 1) { + LOG(DEBUG) << "No external buffers to add."; + return true; + } + + // Walk all our clients and count their currently required frames + auto bufferCount = 0; + for (auto&& client : mClients) { + sp<VirtualCamera> virtCam = client.promote(); + if (virtCam != nullptr) { + bufferCount += virtCam->getAllowedBuffers(); + } + } + + EvsResult status = EvsResult::OK; + // Ask the hardware for the resulting buffer count + mHwCamera->importExternalBuffers(buffers, + [&](auto result, auto added) { + status = result; + *delta = added; + }); + if (status != EvsResult::OK) { + LOG(ERROR) << "Failed to add external capture buffers."; + return false; + } + + bufferCount += *delta; + + // Update the size of our array of outstanding frame records + std::vector<FrameRecord> newRecords; + newRecords.reserve(bufferCount); + + // Copy and compact the old records that are still active + for (const auto& rec : mFrames) { + if (rec.refCount > 0) { + newRecords.emplace_back(rec); + } + } + + if (newRecords.size() > (unsigned)bufferCount) { + LOG(WARNING) << "We found more frames in use than requested."; + } + + mFrames.swap(newRecords); + + return true; +} + + UniqueFence HalCamera::requestNewFrame(sp<VirtualCamera> client, const int64_t lastTimestamp) { if (!mSyncSupported) { diff --git a/evs/manager/1.1/HalCamera.h b/evs/manager/1.1/HalCamera.h index b888add55..970dc1691 100644 --- a/evs/manager/1.1/HalCamera.h +++ b/evs/manager/1.1/HalCamera.h @@ -79,6 +79,8 @@ public: std::string getId() { return mId; } Stream& getStreamConfig() { return mStreamConfig; } bool changeFramesInFlight(int delta); + bool changeFramesInFlight(const hardware::hidl_vec<BufferDesc_1_1>& buffers, + int* delta); UniqueFence requestNewFrame(sp<VirtualCamera> virtualCamera, const int64_t timestamp); diff --git a/evs/manager/1.1/VirtualCamera.cpp b/evs/manager/1.1/VirtualCamera.cpp index 6e203aab1..e9d7b3f65 100644 --- a/evs/manager/1.1/VirtualCamera.cpp +++ b/evs/manager/1.1/VirtualCamera.cpp @@ -865,6 +865,34 @@ Return<void> VirtualCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier, } +Return<void> +VirtualCamera::importExternalBuffers(const hidl_vec<BufferDesc_1_1>& buffers, + importExternalBuffers_cb _hidl_cb) { + if (mHalCamera.size() > 1) { + LOG(WARNING) << "Logical camera device does not support " << __FUNCTION__; + _hidl_cb(EvsResult::UNDERLYING_SERVICE_ERROR, 0); + return {}; + } + + auto pHwCamera = mHalCamera.begin()->second.promote(); + if (pHwCamera == nullptr) { + LOG(WARNING) << "Camera device " << mHalCamera.begin()->first << " is not alive."; + _hidl_cb(EvsResult::UNDERLYING_SERVICE_ERROR, 0); + return {}; + } + + int delta = 0; + if (!pHwCamera->changeFramesInFlight(buffers, &delta)) { + LOG(ERROR) << "Failed to add extenral capture buffers."; + _hidl_cb(EvsResult::UNDERLYING_SERVICE_ERROR, 0); + return {}; + } + + mFramesAllowed += delta; + _hidl_cb(EvsResult::OK, delta); + return {}; +} + } // namespace implementation } // namespace V1_1 } // namespace evs diff --git a/evs/manager/1.1/VirtualCamera.h b/evs/manager/1.1/VirtualCamera.h index 63744d945..764d6c529 100644 --- a/evs/manager/1.1/VirtualCamera.h +++ b/evs/manager/1.1/VirtualCamera.h @@ -105,7 +105,8 @@ public: const hidl_vec<uint8_t>& opaqueValue) override; Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier, getExtendedInfo_1_1_cb _hidl_cb) override; - + Return<void> importExternalBuffers(const hidl_vec<BufferDesc_1_1>& buffers, + importExternalBuffers_cb _hidl_cb) override; private: diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp index eefad2a54..fdb893775 100644 --- a/evs/sampleDriver/EvsV4lCamera.cpp +++ b/evs/sampleDriver/EvsV4lCamera.cpp @@ -449,6 +449,84 @@ Return<void> EvsV4lCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier, } +Return<void> +EvsV4lCamera::importExternalBuffers(const hidl_vec<BufferDesc_1_1>& buffers, + importExternalBuffers_cb _hidl_cb) { + LOG(DEBUG) << __FUNCTION__; + + // If we've been displaced by another owner of the camera, then we can't do anything else + if (!mVideo.isOpen()) { + LOG(WARNING) << "Ignoring a request add external buffers " + << "when camera has been lost."; + _hidl_cb(EvsResult::UNDERLYING_SERVICE_ERROR, mFramesAllowed); + return {}; + } + + auto numBuffersToAdd = buffers.size(); + if (numBuffersToAdd < 1) { + LOG(DEBUG) << "No buffers to add."; + _hidl_cb(EvsResult::OK, mFramesAllowed); + return {}; + } + + { + std::scoped_lock<std::mutex> lock(mAccessLock); + + if (numBuffersToAdd > (MAX_BUFFERS_IN_FLIGHT - mFramesAllowed)) { + numBuffersToAdd -= (MAX_BUFFERS_IN_FLIGHT - mFramesAllowed); + LOG(WARNING) << "Exceed the limit on number of buffers. " + << numBuffersToAdd << " buffers will be added only."; + } + + GraphicBufferMapper& mapper = GraphicBufferMapper::get(); + const auto before = mFramesAllowed; + for (auto i = 0; i < numBuffersToAdd; ++i) { + // TODO: reject if external buffer is configured differently. + auto& b = buffers[i]; + const AHardwareBuffer_Desc* pDesc = + reinterpret_cast<const AHardwareBuffer_Desc *>(&b.buffer.description); + + // Import a buffer to add + buffer_handle_t memHandle = nullptr; + status_t result = mapper.importBuffer(b.buffer.nativeHandle, + pDesc->width, + pDesc->height, + 1, + pDesc->format, + pDesc->usage, + pDesc->stride, + &memHandle); + if (result != android::NO_ERROR || !memHandle) { + LOG(WARNING) << "Failed to import a buffer " << b.bufferId; + continue; + } + + auto stored = false; + for (auto&& rec : mBuffers) { + if (rec.handle == nullptr) { + // Use this existing entry + rec.handle = memHandle; + rec.inUse = false; + + stored = true; + break; + } + } + + if (!stored) { + // Add a BufferRecord wrapping this handle to our set of available buffers + mBuffers.emplace_back(memHandle); + } + + ++mFramesAllowed; + } + + _hidl_cb(EvsResult::OK, mFramesAllowed - before); + return {}; + } +} + + EvsResult EvsV4lCamera::doneWithFrame_impl(const uint32_t bufferId, const buffer_handle_t memHandle) { std::lock_guard <std::mutex> lock(mAccessLock); @@ -649,7 +727,7 @@ void EvsV4lCamera::forwardFrame(imageBuffer* pV4lBuff, void* pData) { } if (!readyForFrame) { - // We need to return the vide buffer so it can capture a new frame + // We need to return the video buffer so it can capture a new frame mVideo.markFrameConsumed(); } else { // Assemble the buffer description we'll transmit below @@ -675,10 +753,11 @@ void EvsV4lCamera::forwardFrame(imageBuffer* pV4lBuff, void* pData) { // causes SEGV_MAPPER. void *targetPixels = nullptr; GraphicBufferMapper &mapper = GraphicBufferMapper::get(); - status_t result = mapper.lock(bufDesc_1_1.buffer.nativeHandle, - GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER, - android::Rect(pDesc->width, pDesc->height), - (void **)&targetPixels); + status_t result = + mapper.lock(bufDesc_1_1.buffer.nativeHandle, + GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER, + android::Rect(pDesc->width, pDesc->height), + (void **)&targetPixels); // If we failed to lock the pixel buffer, we're about to crash, but log it first if (!targetPixels) { @@ -740,6 +819,7 @@ void EvsV4lCamera::forwardFrame(imageBuffer* pV4lBuff, void* pData) { // Since we didn't actually deliver it, mark the frame as available std::lock_guard<std::mutex> lock(mAccessLock); mBuffers[idx].inUse = false; + mFramesInUse--; } } diff --git a/evs/sampleDriver/EvsV4lCamera.h b/evs/sampleDriver/EvsV4lCamera.h index 3dc9f064b..c5b15a953 100644 --- a/evs/sampleDriver/EvsV4lCamera.h +++ b/evs/sampleDriver/EvsV4lCamera.h @@ -24,8 +24,9 @@ #include <android/hardware/camera/device/3.2/ICameraDevice.h> #include <ui/GraphicBuffer.h> -#include <thread> #include <functional> +#include <thread> +#include <set> #include "VideoCapture.h" #include "ConfigManager.h" @@ -85,6 +86,8 @@ public: const hidl_vec<uint8_t>& opaqueValue) override; Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier, getExtendedInfo_1_1_cb _hidl_cb) override; + Return<void> importExternalBuffers(const hidl_vec<BufferDesc_1_1>& buffers, + importExternalBuffers_cb _hidl_cb) override; static sp<EvsV4lCamera> Create(const char *deviceName); static sp<EvsV4lCamera> Create(const char *deviceName, |
