mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "Add a method to support multi-display usages"
This commit is contained in:
@@ -10,9 +10,11 @@ hidl_interface {
|
||||
"types.hal",
|
||||
"IEvsCamera.hal",
|
||||
"IEvsCameraStream.hal",
|
||||
"IEvsDisplay.hal",
|
||||
"IEvsEnumerator.hal",
|
||||
],
|
||||
interfaces: [
|
||||
"android.frameworks.automotive.display@1.0",
|
||||
"android.hardware.automotive.evs@1.0",
|
||||
"android.hardware.camera.device@3.2",
|
||||
"android.hardware.graphics.common@1.0",
|
||||
|
||||
35
automotive/evs/1.1/IEvsDisplay.hal
Normal file
35
automotive/evs/1.1/IEvsDisplay.hal
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.automotive.evs@1.1;
|
||||
|
||||
import @1.0::IEvsDisplay;
|
||||
import @1.0::EvsResult;
|
||||
import android.frameworks.automotive.display@1.0::HwDisplayConfig;
|
||||
import android.frameworks.automotive.display@1.0::HwDisplayState;
|
||||
|
||||
/**
|
||||
* Represents a single display.
|
||||
*/
|
||||
interface IEvsDisplay extends @1.0::IEvsDisplay {
|
||||
/**
|
||||
* Returns the description of this display.
|
||||
*
|
||||
* @return cfg Current configuration of this display.
|
||||
* @return state Current state of this display.
|
||||
*/
|
||||
getDisplayInfo_1_1() generates (HwDisplayConfig cfg, HwDisplayState state);
|
||||
};
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.hardware.automotive.evs@1.1;
|
||||
|
||||
import IEvsCamera;
|
||||
import IEvsDisplay;
|
||||
import @1.0::IEvsEnumerator;
|
||||
import @1.0::EvsResult;
|
||||
import android.hardware.camera.device@3.2::Stream;
|
||||
@@ -54,4 +55,25 @@ interface IEvsEnumerator extends @1.0::IEvsEnumerator {
|
||||
* @return result False for EVS manager implementations and true for all others.
|
||||
*/
|
||||
isHardware() generates (bool result);
|
||||
|
||||
/**
|
||||
* Returns a list of all EVS displays available to the system
|
||||
*
|
||||
* @return displayIds Identifiers of available displays.
|
||||
*/
|
||||
getDisplayIdList() generates (vec<uint8_t> displayIds);
|
||||
|
||||
/**
|
||||
* Get exclusive access to IEvsDisplay for the system
|
||||
*
|
||||
* There can be more than one EVS display objects for the system and this function
|
||||
* requests access to the display identified by a given ID. If the target EVS display
|
||||
* is not available or is already in use the old instance shall be closed and give
|
||||
* the new caller exclusive access.
|
||||
* When done using the display, the caller may release it by calling closeDisplay().
|
||||
*
|
||||
* @param id Target display identifier.
|
||||
* @return display EvsDisplay object to be used.
|
||||
*/
|
||||
openDisplay_1_1(uint8_t id) generates (IEvsDisplay display);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,10 @@ cc_binary {
|
||||
"libutils",
|
||||
"libcamera_metadata",
|
||||
"libtinyxml2",
|
||||
"android.hidl.token@1.0-utils",
|
||||
"android.frameworks.automotive.display@1.0",
|
||||
"android.hardware.graphics.bufferqueue@1.0",
|
||||
"android.hardware.graphics.bufferqueue@2.0",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
|
||||
@@ -280,7 +280,7 @@ Return<EvsResult> EvsCamera::setMaster() {
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay>& ) {
|
||||
Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay_1_0>& ) {
|
||||
// Default implementation does not expect multiple subscribers and therefore
|
||||
// return a success code always.
|
||||
return EvsResult::OK;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
|
||||
#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <thread>
|
||||
@@ -33,7 +33,8 @@ using IEvsCameraStream_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCam
|
||||
using IEvsCameraStream_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using ::android::hardware::automotive::evs::V1_0::CameraDesc;
|
||||
using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
|
||||
|
||||
namespace android {
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
Return<EvsResult> resumeVideoStream() override;
|
||||
Return<EvsResult> doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffer) override;
|
||||
Return<EvsResult> setMaster() override;
|
||||
Return<EvsResult> forceMaster(const sp<IEvsDisplay>& display) override;
|
||||
Return<EvsResult> forceMaster(const sp<IEvsDisplay_1_0>& display) override;
|
||||
Return<EvsResult> unsetMaster() override;
|
||||
Return<void> getParameterList(getParameterList_cb _hidl_cb) override;
|
||||
Return<void> getIntParameterRange(CameraParam id,
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
|
||||
using ::android::frameworks::automotive::display::V1_0::HwDisplayConfig;
|
||||
using ::android::frameworks::automotive::display::V1_0::HwDisplayState;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
@@ -31,6 +33,13 @@ namespace implementation {
|
||||
|
||||
|
||||
EvsDisplay::EvsDisplay() {
|
||||
EvsDisplay(nullptr, 0);
|
||||
}
|
||||
|
||||
|
||||
EvsDisplay::EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId)
|
||||
: mDisplayProxy(pDisplayProxy),
|
||||
mDisplayId(displayId) {
|
||||
ALOGD("EvsDisplay instantiated");
|
||||
|
||||
// Set up our self description
|
||||
@@ -327,6 +336,18 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc_1_0&
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsDisplay::getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) {
|
||||
if (mDisplayProxy != nullptr) {
|
||||
return mDisplayProxy->getDisplayInfo(mDisplayId, _info_cb);
|
||||
} else {
|
||||
HwDisplayConfig nullConfig;
|
||||
HwDisplayState nullState;
|
||||
_info_cb(nullConfig, nullState);
|
||||
return Void();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
|
||||
@@ -17,14 +17,16 @@
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
|
||||
|
||||
#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc;
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
@@ -43,9 +45,12 @@ public:
|
||||
Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override;
|
||||
Return<EvsResult> returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) override;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsDisplay follow.
|
||||
Return<void> getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) override;
|
||||
|
||||
// Implementation details
|
||||
EvsDisplay();
|
||||
EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId);
|
||||
virtual ~EvsDisplay() override;
|
||||
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
|
||||
@@ -60,6 +65,9 @@ private:
|
||||
DisplayState mRequestedState = DisplayState::NOT_VISIBLE;
|
||||
|
||||
std::mutex mAccessLock;
|
||||
|
||||
sp<IAutomotiveDisplayProxyService> mDisplayProxy;
|
||||
uint64_t mDisplayId;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
||||
@@ -34,18 +34,38 @@ namespace implementation {
|
||||
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
|
||||
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
|
||||
unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
|
||||
sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
|
||||
std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
|
||||
|
||||
|
||||
EvsEnumerator::EvsEnumerator() {
|
||||
EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
|
||||
ALOGD("EvsEnumerator created");
|
||||
|
||||
// Add sample camera data to our list of cameras
|
||||
// In a real driver, this would be expected to can the available hardware
|
||||
sConfigManager =
|
||||
ConfigManager::Create("/vendor/etc/automotive/evs/evs_default_configuration.xml");
|
||||
|
||||
// Add available cameras
|
||||
for (auto v : sConfigManager->getCameraList()) {
|
||||
sCameraList.emplace_back(v.c_str());
|
||||
}
|
||||
|
||||
if (sDisplayProxyService == nullptr) {
|
||||
/* sets a car-window service handle */
|
||||
sDisplayProxyService = windowService;
|
||||
}
|
||||
|
||||
// Add available displays
|
||||
if (sDisplayProxyService != nullptr) {
|
||||
// Get a display ID list.
|
||||
sDisplayProxyService->getDisplayIdList([](const auto& displayIds) {
|
||||
for (const auto& id : displayIds) {
|
||||
const auto port = id & 0xF;
|
||||
sDisplayPortList.insert_or_assign(port, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +185,7 @@ Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& pCa
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
|
||||
Return<sp<IEvsDisplay_1_0>> EvsEnumerator::openDisplay() {
|
||||
ALOGD("openDisplay");
|
||||
|
||||
// If we already have a display active, then we need to shut it down so we can
|
||||
@@ -185,7 +205,42 @@ Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
|
||||
Return<void> EvsEnumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
|
||||
hidl_vec<uint8_t> ids;
|
||||
|
||||
ids.resize(sDisplayPortList.size());
|
||||
unsigned i = 0;
|
||||
for (const auto& [port, id] : sDisplayPortList) {
|
||||
ids[i++] = port;
|
||||
}
|
||||
|
||||
_list_cb(ids);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay_1_1(uint8_t port) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// 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("Killing previous display because of new caller");
|
||||
closeDisplay(pActiveDisplay);
|
||||
}
|
||||
|
||||
// Create a new display interface and return it
|
||||
pActiveDisplay = new EvsDisplay(sDisplayProxyService, sDisplayPortList[port]);
|
||||
sActiveDisplay = pActiveDisplay;
|
||||
|
||||
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
|
||||
return pActiveDisplay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& pDisplay) {
|
||||
ALOGD("closeDisplay");
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
|
||||
@@ -19,19 +19,22 @@
|
||||
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
|
||||
using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
|
||||
using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
|
||||
using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
|
||||
|
||||
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
@@ -51,8 +54,8 @@ public:
|
||||
Return<void> getCameraList(getCameraList_cb _hidl_cb) override;
|
||||
Return<sp<IEvsCamera_1_0>> openCamera(const hidl_string& cameraId) override;
|
||||
Return<void> closeCamera(const ::android::sp<IEvsCamera_1_0>& carCamera) override;
|
||||
Return<sp<IEvsDisplay>> openDisplay() override;
|
||||
Return<void> closeDisplay(const ::android::sp<IEvsDisplay>& display) override;
|
||||
Return<sp<IEvsDisplay_1_0>> openDisplay() override;
|
||||
Return<void> closeDisplay(const ::android::sp<IEvsDisplay_1_0>& display) override;
|
||||
Return<DisplayState> getDisplayState() override;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
|
||||
@@ -60,9 +63,11 @@ public:
|
||||
Return<sp<IEvsCamera_1_1>> openCamera_1_1(const hidl_string& cameraId,
|
||||
const Stream& streamCfg) override;
|
||||
Return<bool> isHardware() override { return true; }
|
||||
Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
|
||||
Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
|
||||
|
||||
// Implementation details
|
||||
EvsEnumerator();
|
||||
EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
|
||||
|
||||
private:
|
||||
// NOTE: All members values are static so that all clients operate on the same state
|
||||
@@ -83,6 +88,10 @@ private:
|
||||
static wp<EvsDisplay> sActiveDisplay;
|
||||
|
||||
static unique_ptr<ConfigManager> sConfigManager;
|
||||
|
||||
static sp<IAutomotiveDisplayProxyService> sDisplayProxyService;
|
||||
static std::unordered_map<uint8_t,
|
||||
uint64_t> sDisplayPortList;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
||||
@@ -34,7 +34,6 @@ using android::hardware::joinRpcThreadpool;
|
||||
|
||||
// Generated HIDL files
|
||||
using android::hardware::automotive::evs::V1_1::IEvsEnumerator;
|
||||
using android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
|
||||
// The namespace in which all our implementation code lives
|
||||
using namespace android::hardware::automotive::evs::V1_1::implementation;
|
||||
|
||||
@@ -54,9 +54,11 @@ static const float kNanoToSeconds = 0.000000001f;
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
|
||||
#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
|
||||
#include <system/camera_metadata.h>
|
||||
#include <ui/DisplayConfig.h>
|
||||
#include <ui/DisplayState.h>
|
||||
|
||||
#include <VtsHalHidlTargetTestBase.h>
|
||||
#include <VtsHalHidlTargetTestEnvBase.h>
|
||||
@@ -76,6 +78,8 @@ using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using ::android::hardware::graphics::common::V1_0::PixelFormat;
|
||||
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
|
||||
using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
|
||||
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
|
||||
/*
|
||||
* Plese note that this is different from what is defined in
|
||||
@@ -567,9 +571,30 @@ TEST_F(EvsHidlTest, CameraToDisplayRoundTrip) {
|
||||
// output format.
|
||||
Stream nullCfg = {};
|
||||
|
||||
// Request exclusive access to the EVS display
|
||||
sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
|
||||
// Request available display IDs
|
||||
uint8_t targetDisplayId = 0;
|
||||
pEnumerator->getDisplayIdList([&targetDisplayId](auto ids) {
|
||||
ASSERT_GT(ids.size(), 0);
|
||||
targetDisplayId = ids[0];
|
||||
});
|
||||
|
||||
// Request exclusive access to the first EVS display
|
||||
sp<IEvsDisplay_1_1> pDisplay = pEnumerator->openDisplay_1_1(targetDisplayId);
|
||||
ASSERT_NE(pDisplay, nullptr);
|
||||
ALOGI("Display %d is in use.", targetDisplayId);
|
||||
|
||||
// Get the display descriptor
|
||||
pDisplay->getDisplayInfo_1_1([](const auto& config, const auto& state) {
|
||||
android::DisplayConfig* pConfig = (android::DisplayConfig*)config.data();
|
||||
const auto width = pConfig->resolution.getWidth();
|
||||
const auto height = pConfig->resolution.getHeight();
|
||||
ALOGI(" Resolution: %dx%d", width, height);
|
||||
ASSERT_GT(width, 0);
|
||||
ASSERT_GT(height, 0);
|
||||
|
||||
android::ui::DisplayState* pState = (android::ui::DisplayState*)state.data();
|
||||
ASSERT_NE(pState->layerStack, -1);
|
||||
});
|
||||
|
||||
// Test each reported camera
|
||||
for (auto&& cam: cameraInfo) {
|
||||
@@ -1556,7 +1581,7 @@ TEST_F(EvsHidlTest, HighPriorityCameraClient) {
|
||||
Stream nullCfg = {};
|
||||
|
||||
// Request exclusive access to the EVS display
|
||||
sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
|
||||
sp<IEvsDisplay_1_0> pDisplay = pEnumerator->openDisplay();
|
||||
ASSERT_NE(pDisplay, nullptr);
|
||||
|
||||
// Test each reported camera
|
||||
@@ -1920,7 +1945,7 @@ TEST_F(EvsHidlTest, CameraUseStreamConfigToDisplay) {
|
||||
loadCameraList();
|
||||
|
||||
// Request exclusive access to the EVS display
|
||||
sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
|
||||
sp<IEvsDisplay_1_0> pDisplay = pEnumerator->openDisplay();
|
||||
ASSERT_NE(pDisplay, nullptr);
|
||||
|
||||
// Test each reported camera
|
||||
|
||||
Reference in New Issue
Block a user