Bug: 110364143 Test: mm -j64 Test: AImageReaderVendorTest Change-Id: Ic9343cc7fb10eb374197945b72be59e7ce8282fe Signed-off-by: Jayant Chowdhary <jchowdhary@google.com>gugelfrei
parent
dcfd26eeac
commit
6df26071a5
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 _NDK_CAMERA_WINDOW_TYPE_H
|
||||
#define _NDK_CAMERA_WINDOW_TYPE_H
|
||||
|
||||
/**
|
||||
* @addtogroup Camera
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file NdkCameraWindowType.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines an NDK API.
|
||||
* Do not remove methods.
|
||||
* Do not change method signatures.
|
||||
* Do not change the value of constants.
|
||||
* Do not change the size of any of the classes defined in here.
|
||||
* Do not reference types that are not part of the NDK.
|
||||
* Do not #include files that aren't part of the NDK.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file defines the window type used by NDK and the VNDK variants of the
|
||||
* camera2 NDK. This enables us to share the api definition headers and avoid
|
||||
* code duplication (since the VNDK variant doesn't use ANativeWindow unlike the
|
||||
* NDK variant).
|
||||
*/
|
||||
#ifdef __ANDROID_VNDK__
|
||||
#include <cutils/native_handle.h>
|
||||
typedef native_handle_t ACameraWindowType;
|
||||
#else
|
||||
#include <android/native_window.h>
|
||||
typedef ANativeWindow ACameraWindowType;
|
||||
#endif
|
||||
|
||||
#endif //_NDK_CAMERA_WINDOW_TYPE_H
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
struct ACaptureSessionOutput {
|
||||
explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false) :
|
||||
mWindow(window), mIsShared(isShared) {};
|
||||
|
||||
bool operator == (const ACaptureSessionOutput& other) const {
|
||||
return (mWindow == other.mWindow);
|
||||
}
|
||||
|
||||
bool operator != (const ACaptureSessionOutput& other) const {
|
||||
return mWindow != other.mWindow;
|
||||
}
|
||||
|
||||
bool operator < (const ACaptureSessionOutput& other) const {
|
||||
return mWindow < other.mWindow;
|
||||
}
|
||||
|
||||
bool operator > (const ACaptureSessionOutput& other) const {
|
||||
return mWindow > other.mWindow;
|
||||
}
|
||||
|
||||
android::acam::utils::native_handle_ptr_wrapper mWindow;
|
||||
std::set<android::acam::utils::native_handle_ptr_wrapper> mSharedWindows;
|
||||
bool mIsShared;
|
||||
int mRotation = CAMERA3_STREAM_ROTATION_0;
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 _ACAMERA_DEVICE_H
|
||||
#define _ACAMERA_DEVICE_H
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/List.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
|
||||
#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceCallback.h>
|
||||
#include <android/frameworks/cameraservice/device/2.0/types.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <media/stagefright/foundation/ALooper.h>
|
||||
#include <media/stagefright/foundation/AHandler.h>
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraCaptureSession.h>
|
||||
#include "ACameraMetadata.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace android {
|
||||
namespace acam {
|
||||
|
||||
using ICameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
|
||||
using ICameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
|
||||
using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
|
||||
using PhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
|
||||
using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
|
||||
using SubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo;
|
||||
using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
|
||||
using ErrorCode = frameworks::cameraservice::device::V2_0::ErrorCode;
|
||||
using FmqSizeOrMetadata = frameworks::cameraservice::device::V2_0::FmqSizeOrMetadata;
|
||||
using StreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
|
||||
using Status = frameworks::cameraservice::common::V2_0::Status;
|
||||
using ResultMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
|
||||
using RequestMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
|
||||
using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
|
||||
|
||||
using hardware::hidl_vec;
|
||||
using hardware::hidl_string;
|
||||
using utils::native_handle_ptr_wrapper;
|
||||
using utils::CaptureRequest;
|
||||
using utils::OutputConfigurationWrapper;
|
||||
|
||||
// Wrap ACameraCaptureFailure so it can be ref-counted
|
||||
struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure { };
|
||||
|
||||
class CameraDevice final : public RefBase {
|
||||
public:
|
||||
CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
|
||||
sp<ACameraMetadata> chars,
|
||||
ACameraDevice* wrapper);
|
||||
~CameraDevice();
|
||||
|
||||
inline const char* getId() const { return mCameraId.c_str(); }
|
||||
|
||||
camera_status_t createCaptureRequest(
|
||||
ACameraDevice_request_template templateId,
|
||||
ACaptureRequest** request) const;
|
||||
|
||||
camera_status_t createCaptureSession(
|
||||
const ACaptureSessionOutputContainer* outputs,
|
||||
const ACaptureRequest* sessionParameters,
|
||||
const ACameraCaptureSession_stateCallbacks* callbacks,
|
||||
/*out*/ACameraCaptureSession** session);
|
||||
|
||||
// Callbacks from camera service
|
||||
class ServiceCallback : public ICameraDeviceCallback {
|
||||
public:
|
||||
explicit ServiceCallback(CameraDevice* device) : mDevice(device) {}
|
||||
android::hardware::Return<void> onDeviceError(ErrorCode errorCode,
|
||||
const CaptureResultExtras& resultExtras) override;
|
||||
android::hardware::Return<void> onDeviceIdle() override;
|
||||
android::hardware::Return<void> onCaptureStarted(const CaptureResultExtras& resultExtras,
|
||||
uint64_t timestamp) override;
|
||||
android::hardware::Return<void> onResultReceived(const FmqSizeOrMetadata& result,
|
||||
const CaptureResultExtras& resultExtras,
|
||||
const hidl_vec<PhysicalCaptureResultInfo>& physicalResultInfos) override;
|
||||
android::hardware::Return<void> onRepeatingRequestError(uint64_t lastFrameNumber,
|
||||
int32_t stoppedSequenceId) override;
|
||||
private:
|
||||
const wp<CameraDevice> mDevice;
|
||||
};
|
||||
inline sp<ICameraDeviceCallback> getServiceCallback() {
|
||||
return mServiceCallback;
|
||||
};
|
||||
|
||||
// Camera device is only functional after remote being set
|
||||
void setRemoteDevice(sp<ICameraDeviceUser> remote);
|
||||
|
||||
bool setDeviceMetadataQueues();
|
||||
inline ACameraDevice* getWrapper() const { return mWrapper; };
|
||||
|
||||
private:
|
||||
friend ACameraCaptureSession;
|
||||
|
||||
camera_status_t checkCameraClosedOrErrorLocked() const;
|
||||
|
||||
// device goes into fatal error state after this
|
||||
void setCameraDeviceErrorLocked(camera_status_t error);
|
||||
|
||||
void disconnectLocked(sp<ACameraCaptureSession>& session); // disconnect from camera service
|
||||
|
||||
camera_status_t stopRepeatingLocked();
|
||||
|
||||
camera_status_t flushLocked(ACameraCaptureSession*);
|
||||
|
||||
camera_status_t waitUntilIdleLocked();
|
||||
|
||||
|
||||
camera_status_t captureLocked(sp<ACameraCaptureSession> session,
|
||||
/*optional*/ACameraCaptureSession_captureCallbacks* cbs,
|
||||
int numRequests, ACaptureRequest** requests,
|
||||
/*optional*/int* captureSequenceId);
|
||||
|
||||
camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
|
||||
/*optional*/ACameraCaptureSession_captureCallbacks* cbs,
|
||||
int numRequests, ACaptureRequest** requests,
|
||||
/*optional*/int* captureSequenceId);
|
||||
|
||||
camera_status_t submitRequestsLocked(
|
||||
sp<ACameraCaptureSession> session,
|
||||
/*optional*/ACameraCaptureSession_captureCallbacks* cbs,
|
||||
int numRequests, ACaptureRequest** requests,
|
||||
/*out*/int* captureSequenceId,
|
||||
bool isRepeating);
|
||||
|
||||
camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
|
||||
|
||||
camera_status_t allocateCaptureRequest(
|
||||
const ACaptureRequest* request, sp<CaptureRequest>& outReq);
|
||||
|
||||
static ACaptureRequest* allocateACaptureRequest(sp<CaptureRequest>& req);
|
||||
static void freeACaptureRequest(ACaptureRequest*);
|
||||
|
||||
// only For session to hold device lock
|
||||
// Always grab device lock before grabbing session lock
|
||||
void lockDeviceForSessionOps() const { mDeviceLock.lock(); };
|
||||
void unlockDevice() const { mDeviceLock.unlock(); };
|
||||
|
||||
// For capture session to notify its end of life
|
||||
void notifySessionEndOfLifeLocked(ACameraCaptureSession* session);
|
||||
|
||||
camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
|
||||
const ACaptureRequest* sessionParameters);
|
||||
|
||||
// Input message will be posted and cleared after this returns
|
||||
void postSessionMsgAndCleanup(sp<AMessage>& msg);
|
||||
|
||||
mutable Mutex mDeviceLock;
|
||||
const hidl_string mCameraId; // Camera ID
|
||||
const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app
|
||||
const sp<ACameraMetadata> mChars; // Camera characteristics
|
||||
const sp<ServiceCallback> mServiceCallback;
|
||||
ACameraDevice* mWrapper;
|
||||
|
||||
// stream id -> pair of (ACameraWindowType* from application, OutputConfiguration used for
|
||||
// camera service)
|
||||
std::map<int, std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> mConfiguredOutputs;
|
||||
|
||||
// TODO: maybe a bool will suffice for synchronous implementation?
|
||||
std::atomic_bool mClosing;
|
||||
inline bool isClosed() { return mClosing; }
|
||||
|
||||
bool mInError = false;
|
||||
camera_status_t mError = ACAMERA_OK;
|
||||
void onCaptureErrorLocked(
|
||||
ErrorCode errorCode,
|
||||
const CaptureResultExtras& resultExtras);
|
||||
|
||||
bool mIdle = true;
|
||||
// This will avoid a busy session being deleted before it's back to idle state
|
||||
sp<ACameraCaptureSession> mBusySession;
|
||||
|
||||
sp<ICameraDeviceUser> mRemote;
|
||||
|
||||
// Looper thread to handle callback to app
|
||||
sp<ALooper> mCbLooper;
|
||||
// definition of handler and message
|
||||
enum {
|
||||
// Device state callbacks
|
||||
kWhatOnDisconnected, // onDisconnected
|
||||
kWhatOnError, // onError
|
||||
// Session state callbacks
|
||||
kWhatSessionStateCb, // onReady, onActive
|
||||
// Capture callbacks
|
||||
kWhatCaptureStart, // onCaptureStarted
|
||||
kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted
|
||||
kWhatCaptureFail, // onCaptureFailed
|
||||
kWhatCaptureSeqEnd, // onCaptureSequenceCompleted
|
||||
kWhatCaptureSeqAbort, // onCaptureSequenceAborted
|
||||
kWhatCaptureBufferLost,// onCaptureBufferLost
|
||||
// Internal cleanup
|
||||
kWhatCleanUpSessions // Cleanup cached sp<ACameraCaptureSession>
|
||||
};
|
||||
static const char* kContextKey;
|
||||
static const char* kDeviceKey;
|
||||
static const char* kErrorCodeKey;
|
||||
static const char* kCallbackFpKey;
|
||||
static const char* kSessionSpKey;
|
||||
static const char* kCaptureRequestKey;
|
||||
static const char* kTimeStampKey;
|
||||
static const char* kCaptureResultKey;
|
||||
static const char* kCaptureFailureKey;
|
||||
static const char* kSequenceIdKey;
|
||||
static const char* kFrameNumberKey;
|
||||
static const char* kAnwKey;
|
||||
|
||||
class CallbackHandler : public AHandler {
|
||||
public:
|
||||
void onMessageReceived(const sp<AMessage> &msg) override;
|
||||
|
||||
private:
|
||||
// This handler will cache all capture session sp until kWhatCleanUpSessions
|
||||
// is processed. This is used to guarantee the last session reference is always
|
||||
// being removed in callback thread without holding camera device lock
|
||||
Vector<sp<ACameraCaptureSession>> mCachedSessions;
|
||||
};
|
||||
sp<CallbackHandler> mHandler;
|
||||
|
||||
/***********************************
|
||||
* Capture session related members *
|
||||
***********************************/
|
||||
// The current active session
|
||||
wp<ACameraCaptureSession> mCurrentSession;
|
||||
bool mFlushing = false;
|
||||
|
||||
int mNextSessionId = 0;
|
||||
// TODO: might need another looper/handler to handle callbacks from service
|
||||
|
||||
static const int REQUEST_ID_NONE = -1;
|
||||
int mRepeatingSequenceId = REQUEST_ID_NONE;
|
||||
|
||||
// sequence id -> last frame number map
|
||||
std::map<int32_t, int64_t> mSequenceLastFrameNumberMap;
|
||||
|
||||
struct CallbackHolder {
|
||||
CallbackHolder(sp<ACameraCaptureSession> session,
|
||||
const Vector<sp<CaptureRequest>>& requests,
|
||||
bool isRepeating,
|
||||
ACameraCaptureSession_captureCallbacks* cbs);
|
||||
|
||||
static ACameraCaptureSession_captureCallbacks fillCb(
|
||||
ACameraCaptureSession_captureCallbacks* cbs) {
|
||||
if (cbs != nullptr) {
|
||||
return *cbs;
|
||||
}
|
||||
return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
}
|
||||
|
||||
sp<ACameraCaptureSession> mSession;
|
||||
Vector<sp<CaptureRequest>> mRequests;
|
||||
const bool mIsRepeating;
|
||||
ACameraCaptureSession_captureCallbacks mCallbacks;
|
||||
};
|
||||
// sequence id -> callbacks map
|
||||
std::map<int, CallbackHolder> mSequenceCallbackMap;
|
||||
|
||||
static const int64_t NO_FRAMES_CAPTURED = -1;
|
||||
class FrameNumberTracker {
|
||||
public:
|
||||
// TODO: Called in onResultReceived and onCaptureErrorLocked
|
||||
void updateTracker(int64_t frameNumber, bool isError);
|
||||
inline int64_t getCompletedFrameNumber() { return mCompletedFrameNumber; }
|
||||
private:
|
||||
void update();
|
||||
void updateCompletedFrameNumber(int64_t frameNumber);
|
||||
|
||||
int64_t mCompletedFrameNumber = NO_FRAMES_CAPTURED;
|
||||
List<int64_t> mSkippedFrameNumbers;
|
||||
std::set<int64_t> mFutureErrorSet;
|
||||
};
|
||||
FrameNumberTracker mFrameNumberTracker;
|
||||
|
||||
void checkRepeatingSequenceCompleteLocked(const int sequenceId, const int64_t lastFrameNumber);
|
||||
void checkAndFireSequenceCompleteLocked();
|
||||
|
||||
// Misc variables
|
||||
int32_t mShadingMapSize[2]; // const after constructor
|
||||
int32_t mPartialResultCount; // const after constructor
|
||||
std::shared_ptr<ResultMetadataQueue> mCaptureRequestMetadataQueue = nullptr;
|
||||
std::shared_ptr<ResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
|
||||
};
|
||||
|
||||
} // namespace acam;
|
||||
} // namespace android;
|
||||
|
||||
/**
|
||||
* ACameraDevice opaque struct definition
|
||||
* Leave outside of android namespace because it's NDK struct
|
||||
*/
|
||||
struct ACameraDevice {
|
||||
ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
|
||||
sp<ACameraMetadata> chars) :
|
||||
mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {}
|
||||
|
||||
~ACameraDevice() {};
|
||||
|
||||
/*******************
|
||||
* NDK public APIs *
|
||||
*******************/
|
||||
inline const char* getId() const { return mDevice->getId(); }
|
||||
|
||||
camera_status_t createCaptureRequest(
|
||||
ACameraDevice_request_template templateId,
|
||||
ACaptureRequest** request) const {
|
||||
return mDevice->createCaptureRequest(templateId, request);
|
||||
}
|
||||
|
||||
camera_status_t createCaptureSession(
|
||||
const ACaptureSessionOutputContainer* outputs,
|
||||
const ACaptureRequest* sessionParameters,
|
||||
const ACameraCaptureSession_stateCallbacks* callbacks,
|
||||
/*out*/ACameraCaptureSession** session) {
|
||||
return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session);
|
||||
}
|
||||
|
||||
/***********************
|
||||
* Device interal APIs *
|
||||
***********************/
|
||||
inline android::sp<android::acam::ICameraDeviceCallback> getServiceCallback() {
|
||||
return mDevice->getServiceCallback();
|
||||
};
|
||||
|
||||
// Camera device is only functional after remote being set
|
||||
inline void setRemoteDevice(android::sp<android::acam::ICameraDeviceUser> remote) {
|
||||
mDevice->setRemoteDevice(remote);
|
||||
}
|
||||
inline bool setDeviceMetadataQueues() {
|
||||
return mDevice->setDeviceMetadataQueues();
|
||||
}
|
||||
private:
|
||||
android::sp<android::acam::CameraDevice> mDevice;
|
||||
};
|
||||
|
||||
#endif // _ACAMERA_DEVICE_H
|
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_NDEBUG 0
|
||||
#define LOG_TAG "ACameraManagerVendor"
|
||||
|
||||
#include <memory>
|
||||
#include "ndk_vendor/impl/ACameraManager.h"
|
||||
#include "ACameraMetadata.h"
|
||||
#include "ndk_vendor/impl/ACameraDevice.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <utils/Vector.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <VendorTagDescriptor.h>
|
||||
|
||||
using namespace android::acam;
|
||||
|
||||
namespace android {
|
||||
namespace acam {
|
||||
|
||||
using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
|
||||
|
||||
// Static member definitions
|
||||
const char* CameraManagerGlobal::kCameraIdKey = "CameraId";
|
||||
const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
|
||||
const char* CameraManagerGlobal::kContextKey = "CallbackContext";
|
||||
Mutex CameraManagerGlobal::sLock;
|
||||
CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr;
|
||||
|
||||
CameraManagerGlobal&
|
||||
CameraManagerGlobal::getInstance() {
|
||||
Mutex::Autolock _l(sLock);
|
||||
CameraManagerGlobal* instance = sInstance;
|
||||
if (instance == nullptr) {
|
||||
instance = new CameraManagerGlobal();
|
||||
sInstance = instance;
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
CameraManagerGlobal::~CameraManagerGlobal() {
|
||||
// clear sInstance so next getInstance call knows to create a new one
|
||||
Mutex::Autolock _sl(sLock);
|
||||
sInstance = nullptr;
|
||||
Mutex::Autolock _l(mLock);
|
||||
if (mCameraService != nullptr) {
|
||||
mCameraService->unlinkToDeath(mDeathNotifier);
|
||||
mCameraService->removeListener(mCameraServiceListener);
|
||||
}
|
||||
mDeathNotifier.clear();
|
||||
if (mCbLooper != nullptr) {
|
||||
mCbLooper->unregisterHandler(mHandler->id());
|
||||
mCbLooper->stop();
|
||||
}
|
||||
mCbLooper.clear();
|
||||
mHandler.clear();
|
||||
mCameraServiceListener.clear();
|
||||
mCameraService.clear();
|
||||
}
|
||||
|
||||
static bool isCameraServiceDisabled() {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("config.disable_cameraservice", value, "0");
|
||||
return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0);
|
||||
}
|
||||
|
||||
// TODO: Add back when vendor tags are supported for libcamera2ndk_vendor when
|
||||
// the HIDL interface supports querying by vendor id.
|
||||
|
||||
sp<ICameraService> CameraManagerGlobal::getCameraService() {
|
||||
Mutex::Autolock _l(mLock);
|
||||
if (mCameraService.get() == nullptr) {
|
||||
if (isCameraServiceDisabled()) {
|
||||
return mCameraService;
|
||||
}
|
||||
|
||||
sp<ICameraService> cameraServiceBinder;
|
||||
do {
|
||||
cameraServiceBinder = ICameraService::getService();
|
||||
if (cameraServiceBinder != nullptr) {
|
||||
break;
|
||||
}
|
||||
ALOGW("CameraService not published, waiting...");
|
||||
usleep(kCameraServicePollDelay);
|
||||
} while(true);
|
||||
if (mDeathNotifier == nullptr) {
|
||||
mDeathNotifier = new DeathNotifier(this);
|
||||
}
|
||||
cameraServiceBinder->linkToDeath(mDeathNotifier, 0);
|
||||
mCameraService = cameraServiceBinder;
|
||||
|
||||
// Setup looper thread to perfrom availiability callbacks
|
||||
if (mCbLooper == nullptr) {
|
||||
mCbLooper = new ALooper;
|
||||
mCbLooper->setName("C2N-mgr-looper");
|
||||
status_t err = mCbLooper->start(
|
||||
/*runOnCallingThread*/false,
|
||||
/*canCallJava*/ true,
|
||||
PRIORITY_DEFAULT);
|
||||
if (err != OK) {
|
||||
ALOGE("%s: Unable to start camera service listener looper: %s (%d)",
|
||||
__FUNCTION__, strerror(-err), err);
|
||||
mCbLooper.clear();
|
||||
return nullptr;
|
||||
}
|
||||
if (mHandler == nullptr) {
|
||||
mHandler = new CallbackHandler();
|
||||
}
|
||||
mCbLooper->registerHandler(mHandler);
|
||||
}
|
||||
|
||||
// register ICameraServiceListener
|
||||
if (mCameraServiceListener == nullptr) {
|
||||
mCameraServiceListener = new CameraServiceListener(this);
|
||||
}
|
||||
hidl_vec<CameraStatusAndId> cameraStatuses{};
|
||||
Status status = Status::NO_ERROR;
|
||||
auto remoteRet = mCameraService->addListener(mCameraServiceListener,
|
||||
[&status, &cameraStatuses](Status s,
|
||||
auto &retStatuses) {
|
||||
status = s;
|
||||
cameraStatuses = retStatuses;
|
||||
});
|
||||
if (!remoteRet.isOk() || status != Status::NO_ERROR) {
|
||||
ALOGE("Failed to add listener to camera service %s", remoteRet.description().c_str());
|
||||
}
|
||||
for (auto& c : cameraStatuses) {
|
||||
onStatusChangedLocked(c);
|
||||
}
|
||||
}
|
||||
return mCameraService;
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::DeathNotifier::serviceDied(uint64_t cookie, const wp<IBase> &who) {
|
||||
(void) cookie;
|
||||
(void) who;
|
||||
ALOGE("Camera service binderDied!");
|
||||
sp<CameraManagerGlobal> cm = mCameraManager.promote();
|
||||
if (cm != nullptr) {
|
||||
AutoMutex lock(cm->mLock);
|
||||
for (auto& pair : cm->mDeviceStatusMap) {
|
||||
CameraStatusAndId cameraStatusAndId;
|
||||
cameraStatusAndId.cameraId = pair.first;
|
||||
cameraStatusAndId.deviceStatus = pair.second;
|
||||
cm->onStatusChangedLocked(cameraStatusAndId);
|
||||
}
|
||||
cm->mCameraService.clear();
|
||||
// TODO: consider adding re-connect call here?
|
||||
}
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::registerAvailabilityCallback(
|
||||
const ACameraManager_AvailabilityCallbacks *callback) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
Callback cb(callback);
|
||||
auto pair = mCallbacks.insert(cb);
|
||||
// Send initial callbacks if callback is newly registered
|
||||
if (pair.second) {
|
||||
for (auto& pair : mDeviceStatusMap) {
|
||||
const hidl_string& cameraId = pair.first;
|
||||
CameraDeviceStatus status = pair.second;
|
||||
|
||||
sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
|
||||
ACameraManager_AvailabilityCallback cb = isStatusAvailable(status) ?
|
||||
callback->onCameraAvailable : callback->onCameraUnavailable;
|
||||
msg->setPointer(kCallbackFpKey, (void *) cb);
|
||||
msg->setPointer(kContextKey, callback->context);
|
||||
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
|
||||
msg->post();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::unregisterAvailabilityCallback(
|
||||
const ACameraManager_AvailabilityCallbacks *callback) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
Callback cb(callback);
|
||||
mCallbacks.erase(cb);
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::getCameraIdList(std::vector<hidl_string>* cameraIds) {
|
||||
// Ensure that we have initialized/refreshed the list of available devices
|
||||
auto cs = getCameraService();
|
||||
Mutex::Autolock _l(mLock);
|
||||
|
||||
for(auto& deviceStatus : mDeviceStatusMap) {
|
||||
if (deviceStatus.second == CameraDeviceStatus::STATUS_NOT_PRESENT ||
|
||||
deviceStatus.second == CameraDeviceStatus::STATUS_ENUMERATING) {
|
||||
continue;
|
||||
}
|
||||
cameraIds->push_back(deviceStatus.first);
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraManagerGlobal::validStatus(CameraDeviceStatus status) {
|
||||
switch (status) {
|
||||
case CameraDeviceStatus::STATUS_NOT_PRESENT:
|
||||
case CameraDeviceStatus::STATUS_PRESENT:
|
||||
case CameraDeviceStatus::STATUS_ENUMERATING:
|
||||
case CameraDeviceStatus::STATUS_NOT_AVAILABLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraManagerGlobal::isStatusAvailable(CameraDeviceStatus status) {
|
||||
switch (status) {
|
||||
case CameraDeviceStatus::STATUS_PRESENT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::CallbackHandler::onMessageReceived(
|
||||
const sp<AMessage> &msg) {
|
||||
switch (msg->what()) {
|
||||
case kWhatSendSingleCallback:
|
||||
{
|
||||
ACameraManager_AvailabilityCallback cb;
|
||||
void* context;
|
||||
AString cameraId;
|
||||
bool found = msg->findPointer(kCallbackFpKey, (void**) &cb);
|
||||
if (!found) {
|
||||
ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
found = msg->findPointer(kContextKey, &context);
|
||||
if (!found) {
|
||||
ALOGE("%s: Cannot find callback context!", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
found = msg->findString(kCameraIdKey, &cameraId);
|
||||
if (!found) {
|
||||
ALOGE("%s: Cannot find camera ID!", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
(*cb)(context, cameraId.c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hardware::Return<void> CameraManagerGlobal::CameraServiceListener::onStatusChanged(
|
||||
const CameraStatusAndId &statusAndId) {
|
||||
sp<CameraManagerGlobal> cm = mCameraManager.promote();
|
||||
if (cm != nullptr) {
|
||||
cm->onStatusChanged(statusAndId);
|
||||
} else {
|
||||
ALOGE("Cannot deliver status change. Global camera manager died");
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::onStatusChanged(
|
||||
const CameraStatusAndId &statusAndId) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
onStatusChangedLocked(statusAndId);
|
||||
}
|
||||
|
||||
void CameraManagerGlobal::onStatusChangedLocked(
|
||||
const CameraStatusAndId &statusAndId) {
|
||||
hidl_string cameraId = statusAndId.cameraId;
|
||||
CameraDeviceStatus status = statusAndId.deviceStatus;
|
||||
if (!validStatus(status)) {
|
||||
ALOGE("%s: Invalid status %d", __FUNCTION__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0);
|
||||
CameraDeviceStatus oldStatus = firstStatus ?
|
||||
status : // first status
|
||||
mDeviceStatusMap[cameraId];
|
||||
|
||||
if (!firstStatus &&
|
||||
isStatusAvailable(status) == isStatusAvailable(oldStatus)) {
|
||||
// No status update. No need to send callback
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through all registered callbacks
|
||||
mDeviceStatusMap[cameraId] = status;
|
||||
for (auto cb : mCallbacks) {
|
||||
sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
|
||||
ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ?
|
||||
cb.mAvailable : cb.mUnavailable;
|
||||
msg->setPointer(kCallbackFpKey, (void *) cbFp);
|
||||
msg->setPointer(kContextKey, cb.mContext);
|
||||
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
|
||||
msg->post();
|
||||
}
|
||||
if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) {
|
||||
mDeviceStatusMap.erase(cameraId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace acam
|
||||
} // namespace android
|
||||
|
||||
/**
|
||||
* ACameraManger Implementation
|
||||
*/
|
||||
camera_status_t
|
||||
ACameraManager::getCameraIdList(ACameraIdList** cameraIdList) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
|
||||
std::vector<hidl_string> idList;
|
||||
CameraManagerGlobal::getInstance().getCameraIdList(&idList);
|
||||
|
||||
int numCameras = idList.size();
|
||||
ACameraIdList *out = new ACameraIdList;
|
||||
if (!out) {
|
||||
ALOGE("Allocate memory for ACameraIdList failed!");
|
||||
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
out->numCameras = numCameras;
|
||||
out->cameraIds = new const char*[numCameras];
|
||||
if (!out->cameraIds) {
|
||||
ALOGE("Allocate memory for ACameraIdList failed!");
|
||||
deleteCameraIdList(out);
|
||||
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
for (int i = 0; i < numCameras; i++) {
|
||||
const char* src = idList[i].c_str();
|
||||
size_t dstSize = strlen(src) + 1;
|
||||
char* dst = new char[dstSize];
|
||||
if (!dst) {
|
||||
ALOGE("Allocate memory for ACameraIdList failed!");
|
||||
deleteCameraIdList(out);
|
||||
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
strlcpy(dst, src, dstSize);
|
||||
out->cameraIds[i] = dst;
|
||||
}
|
||||
*cameraIdList = out;
|
||||
return ACAMERA_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ACameraManager::deleteCameraIdList(ACameraIdList* cameraIdList) {
|
||||
if (cameraIdList != nullptr) {
|
||||
if (cameraIdList->cameraIds != nullptr) {
|
||||
for (int i = 0; i < cameraIdList->numCameras; i ++) {
|
||||
if (cameraIdList->cameraIds[i] != nullptr) {
|
||||
delete[] cameraIdList->cameraIds[i];
|
||||
}
|
||||
}
|
||||
delete[] cameraIdList->cameraIds;
|
||||
}
|
||||
delete cameraIdList;
|
||||
}
|
||||
}
|
||||
|
||||
camera_status_t ACameraManager::getCameraCharacteristics(
|
||||
const char *cameraIdStr, sp<ACameraMetadata> *characteristics) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
|
||||
sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
|
||||
if (cs == nullptr) {
|
||||
ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
|
||||
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
|
||||
}
|
||||
CameraMetadata rawMetadata;
|
||||
Status status = Status::NO_ERROR;
|
||||
auto serviceRet =
|
||||
cs->getCameraCharacteristics(cameraIdStr,
|
||||
[&status, &rawMetadata] (auto s ,
|
||||
const hidl_vec<uint8_t> &metadata) {
|
||||
status = s;
|
||||
if (status == Status::NO_ERROR) {
|
||||
utils::convertFromHidlCloned(metadata, &rawMetadata);
|
||||
}
|
||||
});
|
||||
if (!serviceRet.isOk() || status != Status::NO_ERROR) {
|
||||
ALOGE("Get camera characteristics from camera service failed");
|
||||
return ACAMERA_ERROR_UNKNOWN; // should not reach here
|
||||
}
|
||||
|
||||
*characteristics = new ACameraMetadata(
|
||||
rawMetadata.release(), ACameraMetadata::ACM_CHARACTERISTICS);
|
||||
return ACAMERA_OK;
|
||||
}
|
||||
|
||||
camera_status_t
|
||||
ACameraManager::openCamera(
|
||||
const char* cameraId,
|
||||
ACameraDevice_StateCallbacks* callback,
|
||||
/*out*/ACameraDevice** outDevice) {
|
||||
sp<ACameraMetadata> rawChars;
|
||||
camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars);
|
||||
Mutex::Autolock _l(mLock);
|
||||
if (ret != ACAMERA_OK) {
|
||||
ALOGE("%s: cannot get camera characteristics for camera %s. err %d",
|
||||
__FUNCTION__, cameraId, ret);
|
||||
return ACAMERA_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(rawChars));
|
||||
|
||||
sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
|
||||
if (cs == nullptr) {
|
||||
ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
|
||||
delete device;
|
||||
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
|
||||
}
|
||||
|
||||
sp<ICameraDeviceCallback> callbacks = device->getServiceCallback();
|
||||
sp<ICameraDeviceUser> deviceRemote;
|
||||
|
||||
// No way to get package name from native.
|
||||
// Send a zero length package name and let camera service figure it out from UID
|
||||
Status status = Status::NO_ERROR;
|
||||
auto serviceRet = cs->connectDevice(
|
||||
callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) {
|
||||
status = s;
|
||||
deviceRemote = device;
|
||||
});
|
||||
|
||||
if (!serviceRet.isOk() || status != Status::NO_ERROR) {
|
||||
ALOGE("%s: connect camera device failed", __FUNCTION__);
|
||||
// TODO: Convert serviceRet to camera_status_t
|
||||
delete device;
|
||||
return ACAMERA_ERROR_UNKNOWN;
|
||||
}
|
||||
if (deviceRemote == nullptr) {
|
||||
ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__);
|
||||
delete device;
|
||||
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
|
||||
}
|
||||
device->setRemoteDevice(deviceRemote);
|
||||
device->setDeviceMetadataQueues();
|
||||
*outDevice = device;
|
||||
return ACAMERA_OK;
|
||||
}
|
||||
|
||||
ACameraManager::~ACameraManager() {
|
||||
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 _ACAMERA_MANAGER_H
|
||||
#define _ACAMERA_MANAGER_H
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
|
||||
|
||||
#include <CameraMetadata.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <media/stagefright/foundation/ALooper.h>
|
||||
#include <media/stagefright/foundation/AHandler.h>
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace android {
|
||||
namespace acam {
|
||||
|
||||
using ICameraService = frameworks::cameraservice::service::V2_0::ICameraService;
|
||||
using CameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
|
||||
using ICameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener;
|
||||
using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
|
||||
using Status = frameworks::cameraservice::common::V2_0::Status;
|
||||
using VendorTagSection = frameworks::cameraservice::common::V2_0::VendorTagSection;
|
||||
using VendorTag = frameworks::cameraservice::common::V2_0::VendorTag;
|
||||
using IBase = android::hidl::base::V1_0::IBase;
|
||||
using android::hardware::hidl_string;
|
||||
using hardware::Void;
|
||||
|
||||
/**
|
||||
* Per-process singleton instance of CameraManger. Shared by all ACameraManager
|
||||
* instances. Created when first ACameraManager is created and destroyed when
|
||||
* all ACameraManager instances are deleted.
|
||||
*
|
||||
* TODO: maybe CameraManagerGlobal is better suited in libcameraclient?
|
||||
*/
|
||||
class CameraManagerGlobal final : public RefBase {
|
||||
public:
|
||||
static CameraManagerGlobal& getInstance();
|
||||
sp<ICameraService> getCameraService();
|
||||
|
||||
void registerAvailabilityCallback(
|
||||
const ACameraManager_AvailabilityCallbacks *callback);
|
||||
void unregisterAvailabilityCallback(
|
||||
const ACameraManager_AvailabilityCallbacks *callback);
|
||||
|
||||
/**
|
||||
* Return camera IDs that support camera2
|
||||
*/
|
||||
void getCameraIdList(std::vector<hidl_string> *cameraIds);
|
||||
|
||||
private:
|
||||
sp<ICameraService> mCameraService;
|
||||
const int kCameraServicePollDelay = 500000; // 0.5s
|
||||
Mutex mLock;
|
||||
class DeathNotifier : public android::hardware::hidl_death_recipient {
|
||||
public:
|
||||
explicit DeathNotifier(CameraManagerGlobal* cm) : mCameraManager(cm) {}
|
||||
protected:
|
||||
// IBinder::DeathRecipient implementation
|
||||
virtual void serviceDied(uint64_t cookie, const wp<IBase> &who);
|
||||
private:
|
||||
const wp<CameraManagerGlobal> mCameraManager;
|
||||
};
|
||||
sp<DeathNotifier> mDeathNotifier;
|
||||
|
||||
class CameraServiceListener final : public ICameraServiceListener {
|
||||
public:
|
||||
explicit CameraServiceListener(CameraManagerGlobal* cm) : mCameraManager(cm) {}
|
||||
android::hardware::Return<void> onStatusChanged(
|
||||
const CameraStatusAndId &statusAndId) override;
|
||||
|
||||
private:
|
||||
const wp<CameraManagerGlobal> mCameraManager;
|
||||
};
|
||||
sp<CameraServiceListener> mCameraServiceListener;
|
||||
|
||||
// Wrapper of ACameraManager_AvailabilityCallbacks so we can store it in std::set
|
||||
struct Callback {
|
||||
explicit Callback(const ACameraManager_AvailabilityCallbacks *callback) :
|
||||
mAvailable(callback->onCameraAvailable),
|
||||
mUnavailable(callback->onCameraUnavailable),
|
||||
mContext(callback->context) {}
|
||||
|
||||
bool operator == (const Callback& other) const {
|
||||
return (mAvailable == other.mAvailable &&
|
||||
mUnavailable == other.mUnavailable &&
|
||||
mContext == other.mContext);
|
||||
}
|
||||
bool operator != (const Callback& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator < (const Callback& other) const {
|
||||
if (*this == other) return false;
|
||||
if (mContext != other.mContext) return mContext < other.mContext;
|
||||
if (mAvailable != other.mAvailable) return mAvailable < other.mAvailable;
|
||||
return mUnavailable < other.mUnavailable;
|
||||
}
|
||||
bool operator > (const Callback& other) const {
|
||||
return (*this != other && !(*this < other));
|
||||
}
|
||||
ACameraManager_AvailabilityCallback mAvailable;
|
||||
ACameraManager_AvailabilityCallback mUnavailable;
|
||||
void* mContext;
|
||||
};
|
||||
std::set<Callback> mCallbacks;
|
||||
|
||||
// definition of handler and message
|
||||
enum {
|
||||
kWhatSendSingleCallback
|
||||
};
|
||||
static const char* kCameraIdKey;
|
||||
static const char* kCallbackFpKey;
|
||||
static const char* kContextKey;
|
||||
class CallbackHandler : public AHandler {
|
||||
public:
|
||||
CallbackHandler() {}
|
||||
void onMessageReceived(const sp<AMessage> &msg) override;
|
||||
};
|
||||
sp<CallbackHandler> mHandler;
|
||||
sp<ALooper> mCbLooper; // Looper thread where callbacks actually happen on
|
||||
|
||||
void onStatusChanged(const CameraStatusAndId &statusAndId);
|
||||
void onStatusChangedLocked(const CameraStatusAndId &statusAndId);
|
||||
// Utils for status
|
||||
static bool validStatus(CameraDeviceStatus status);
|
||||
static bool isStatusAvailable(CameraDeviceStatus status);
|
||||
|
||||
// The sort logic must match the logic in
|
||||
// libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
|
||||
struct CameraIdComparator {
|
||||
bool operator()(const hidl_string& a, const hidl_string& b) const {
|
||||
uint32_t aUint = 0, bUint = 0;
|
||||
bool aIsUint = base::ParseUint(a.c_str(), &aUint);
|
||||
bool bIsUint = base::ParseUint(b.c_str(), &bUint);
|
||||
|
||||
// Uint device IDs first
|
||||
if (aIsUint && bIsUint) {
|
||||
return aUint < bUint;
|
||||
} else if (aIsUint) {
|
||||
return true;
|
||||
} else if (bIsUint) {
|
||||
return false;
|
||||
}
|
||||
// Simple string compare if both id are not uint
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
// Map camera_id -> status
|
||||
std::map<hidl_string, CameraDeviceStatus, CameraIdComparator> mDeviceStatusMap;
|
||||
|
||||
// For the singleton instance
|
||||
static Mutex sLock;
|
||||
static CameraManagerGlobal* sInstance;
|
||||
CameraManagerGlobal() {};
|
||||
~CameraManagerGlobal();
|
||||
};
|
||||
|
||||
} // namespace acam;
|
||||
} // namespace android;
|
||||
|
||||
/**
|
||||
* ACameraManager opaque struct definition
|
||||
* Leave outside of android namespace because it's NDK struct
|
||||
*/
|
||||
struct ACameraManager {
|
||||
ACameraManager() :
|
||||
mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {}
|
||||
~ACameraManager();
|
||||
camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
|
||||
static void deleteCameraIdList(ACameraIdList* cameraIdList);
|
||||
|
||||
camera_status_t getCameraCharacteristics(
|
||||
const char* cameraId, android::sp<ACameraMetadata>* characteristics);
|
||||
|
||||
camera_status_t openCamera(const char* cameraId,
|
||||
ACameraDevice_StateCallbacks* callback,
|
||||
/*out*/ACameraDevice** device);
|
||||
|
||||
private:
|
||||
enum {
|
||||
kCameraIdListNotInit = -1
|
||||
};
|
||||
android::Mutex mLock;
|
||||
android::sp<android::acam::CameraManagerGlobal> mGlobalManager;
|
||||
};
|
||||
|
||||
#endif //_ACAMERA_MANAGER_H
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
struct ACameraOutputTarget {
|
||||
explicit ACameraOutputTarget(native_handle_t* window) : mWindow(window) {};
|
||||
|
||||
bool operator == (const ACameraOutputTarget& other) const {
|
||||
return mWindow == other.mWindow;
|
||||
}
|
||||
bool operator != (const ACameraOutputTarget& other) const {
|
||||
return mWindow != other.mWindow;
|
||||
}
|
||||
bool operator < (const ACameraOutputTarget& other) const {
|
||||
return mWindow < other.mWindow;
|
||||
}
|
||||
bool operator > (const ACameraOutputTarget& other) const {
|
||||
return mWindow > other.mWindow;
|
||||
}
|
||||
|
||||
android::acam::utils::native_handle_ptr_wrapper mWindow;
|
||||
};
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "ACameraVendorUtils"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace android {
|
||||
namespace acam {
|
||||
namespace utils {
|
||||
|
||||
// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest.
|
||||
frameworks::cameraservice::device::V2_0::CaptureRequest
|
||||
convertToHidl(const CaptureRequest *captureRequest) {
|
||||
frameworks::cameraservice::device::V2_0::CaptureRequest hCaptureRequest;
|
||||
hCaptureRequest.physicalCameraSettings = captureRequest->mCaptureRequest.physicalCameraSettings;
|
||||
hCaptureRequest.streamAndWindowIds = captureRequest->mCaptureRequest.streamAndWindowIds;
|
||||
return hCaptureRequest;
|
||||
}
|
||||
|
||||
HRotation convertToHidl(int rotation) {
|
||||
HRotation hRotation = HRotation::R0;
|
||||
switch(rotation) {
|
||||
case CAMERA3_STREAM_ROTATION_90:
|
||||
hRotation = HRotation::R90;
|
||||
break;
|
||||
case CAMERA3_STREAM_ROTATION_180:
|
||||
hRotation = HRotation::R180;
|
||||
break;
|
||||
case CAMERA3_STREAM_ROTATION_270:
|
||||
hRotation = HRotation::R270;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return hRotation;
|
||||
}
|
||||
|
||||
bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata) {
|
||||
const camera_metadata *buffer = (camera_metadata_t*)(metadata.data());
|
||||
size_t expectedSize = metadata.size();
|
||||
int ret = validate_camera_metadata_structure(buffer, &expectedSize);
|
||||
if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
|
||||
*rawMetadata = buffer;
|
||||
} else {
|
||||
ALOGE("%s: Malformed camera metadata received from caller", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: existing data in dst will be gone. Caller still owns the memory of src
|
||||
void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) {
|
||||
if (src == nullptr) {
|
||||
return;
|
||||
}
|
||||
size_t size = get_camera_metadata_size(src);
|
||||
ALOGE("Converting metadata size: %d", (int)size);
|
||||
dst->setToExternal((uint8_t *) src, size);
|
||||
return;
|
||||
}
|
||||
|
||||
TemplateId convertToHidl(ACameraDevice_request_template templateId) {
|
||||
switch(templateId) {
|
||||
case TEMPLATE_STILL_CAPTURE:
|
||||
return TemplateId::STILL_CAPTURE;
|
||||
case TEMPLATE_RECORD:
|
||||
return TemplateId::RECORD;
|
||||
case TEMPLATE_VIDEO_SNAPSHOT:
|
||||
return TemplateId::VIDEO_SNAPSHOT;
|
||||
case TEMPLATE_ZERO_SHUTTER_LAG:
|
||||
return TemplateId::ZERO_SHUTTER_LAG;
|
||||
case TEMPLATE_MANUAL:
|
||||
return TemplateId::MANUAL;
|
||||
default:
|
||||
return TemplateId::PREVIEW;
|
||||
}
|
||||
}
|
||||
|
||||
camera_status_t convertFromHidl(Status status) {
|
||||
camera_status_t ret = ACAMERA_OK;
|
||||
switch(status) {
|
||||
case Status::NO_ERROR:
|
||||
break;
|
||||
case Status::DISCONNECTED:
|
||||
ret = ACAMERA_ERROR_CAMERA_DISCONNECTED;
|
||||
break;
|
||||
case Status::CAMERA_IN_USE:
|
||||
ret = ACAMERA_ERROR_CAMERA_IN_USE;
|
||||
break;
|
||||
case Status::MAX_CAMERAS_IN_USE:
|
||||
ret = ACAMERA_ERROR_MAX_CAMERA_IN_USE;
|
||||
break;
|
||||
case Status::ILLEGAL_ARGUMENT:
|
||||
ret = ACAMERA_ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
case Status::DEPRECATED_HAL:
|
||||
// Should not reach here since we filtered legacy HALs earlier
|
||||
ret = ACAMERA_ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
case Status::DISABLED:
|
||||
ret = ACAMERA_ERROR_CAMERA_DISABLED;
|
||||
break;
|
||||
case Status::PERMISSION_DENIED:
|
||||
ret = ACAMERA_ERROR_PERMISSION_DENIED;
|
||||
break;
|
||||
case Status::INVALID_OPERATION:
|
||||
ret = ACAMERA_ERROR_INVALID_OPERATION;
|
||||
break;
|
||||
default:
|
||||
ret = ACAMERA_ERROR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2) {
|
||||
if (nh1->numFds !=0 || nh2->numFds !=0) {
|
||||
ALOGE("Invalid window native handles being compared");
|
||||
return false;
|
||||
}
|
||||
if (nh1->version != nh2->version || nh1->numFds != nh2->numFds ||
|
||||
nh1->numInts != nh2->numInts) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < nh1->numInts; i++) {
|
||||
if(nh1->data[i] != nh2->data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2) {
|
||||
if (isWindowNativeHandleEqual(nh1, nh2)) {
|
||||
return false;
|
||||
}
|
||||
if (nh1->numInts != nh2->numInts) {
|
||||
return nh1->numInts < nh2->numInts;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nh1->numInts; i++) {
|
||||
if (nh1->data[i] != nh2->data[i]) {
|
||||
return nh1->data[i] < nh2->data[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2) {
|
||||
return !isWindowNativeHandleLessThan(nh1, nh2) && !isWindowNativeHandleEqual(nh1, nh2);
|
||||
}
|
||||
|
||||
bool areWindowNativeHandlesEqual(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle> handles2) {
|
||||
if (handles1.size() != handles2.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < handles1.size(); i++) {
|
||||
if (!isWindowNativeHandleEqual(handles1[i], handles2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool areWindowNativeHandlesLessThan(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2) {
|
||||
if (handles1.size() != handles2.size()) {
|
||||
return handles1.size() < handles2.size();
|
||||
}
|
||||
for (int i = 0; i < handles1.size(); i++) {
|
||||
const native_handle_t *handle1 = handles1[i].getNativeHandle();
|
||||
const native_handle_t *handle2 = handles2[i].getNativeHandle();
|
||||
if (!isWindowNativeHandleEqual(handle1, handle2)) {
|
||||
return isWindowNativeHandleLessThan(handle1, handle2);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace acam
|
||||
} // namespace android
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
|
||||
#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
|
||||
#include <android/frameworks/cameraservice/device/2.0/types.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <CameraMetadata.h>
|
||||
#include <hardware/camera3.h>
|
||||
|
||||
#ifndef CAMERA_NDK_VENDOR_H
|
||||
#define CAMERA_NDK_VENDOR_H
|
||||
|
||||
using android::hardware::hidl_vec;
|
||||
using android::hardware::hidl_handle;
|
||||
|
||||
namespace android {
|
||||
namespace acam {
|
||||
namespace utils {
|
||||
|
||||
using CameraMetadata = hardware::camera::common::V1_0::helper::CameraMetadata;
|
||||
using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
|
||||
using Status = frameworks::cameraservice::common::V2_0::Status;
|
||||
using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
|
||||
using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
|
||||
using HRotation = frameworks::cameraservice::device::V2_0::OutputConfiguration::Rotation;
|
||||
using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
|
||||
|
||||
// Utility class so that CaptureRequest can be stored by sp<>
|
||||
struct CaptureRequest : public RefBase {
|
||||
frameworks::cameraservice::device::V2_0::CaptureRequest mCaptureRequest;
|
||||
std::vector<native_handle_t *> mSurfaceList;
|
||||
//Physical camera settings metadata is stored here, since the capture request
|
||||
//might not contain it. That's since, fmq might have consumed it.
|
||||
hidl_vec<PhysicalCameraSettings> mPhysicalCameraSettings;
|
||||
};
|
||||
|
||||
bool areWindowNativeHandlesEqual(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2);
|
||||
|
||||
bool areWindowNativeHandlesLessThan(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2);
|
||||
|
||||
bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2);
|
||||
|
||||
bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2);
|
||||
|
||||
// Convenience wrapper over isWindowNativeHandleLessThan and isWindowNativeHandleEqual
|
||||
bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2);
|
||||
|
||||
// Utility class so the native_handle_t can be compared with its contents instead
|
||||
// of just raw pointer comparisons.
|
||||
struct native_handle_ptr_wrapper {
|
||||
native_handle_t *mWindow = nullptr;
|
||||
|
||||
native_handle_ptr_wrapper(native_handle_t *nh) : mWindow(nh) { }
|
||||
|
||||
native_handle_ptr_wrapper() = default;
|
||||
|
||||
operator native_handle_t *() const { return mWindow; }
|
||||
|
||||
bool operator ==(const native_handle_ptr_wrapper other) const {
|
||||
return isWindowNativeHandleEqual(mWindow, other.mWindow);
|
||||
}
|
||||
|
||||
bool operator != (const native_handle_ptr_wrapper& other) const {
|
||||
return !isWindowNativeHandleEqual(mWindow, other.mWindow);
|
||||
}
|
||||
|
||||
bool operator < (const native_handle_ptr_wrapper& other) const {
|
||||
return isWindowNativeHandleLessThan(mWindow, other.mWindow);
|
||||
}
|
||||
|
||||
bool operator > (const native_handle_ptr_wrapper& other) const {
|
||||
return !isWindowNativeHandleGreaterThan(mWindow, other.mWindow);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Wrapper around OutputConfiguration. This is needed since HIDL
|
||||
// OutputConfiguration is auto-generated and marked final. Therefore, operator
|
||||
// overloads outside the class, will not get picked by clang while trying to
|
||||
// store OutputConfiguration in maps/sets.
|
||||
struct OutputConfigurationWrapper {
|
||||
OutputConfiguration mOutputConfiguration;
|
||||
|
||||
operator const OutputConfiguration &() const {
|
||||
return mOutputConfiguration;
|
||||
}
|
||||
|
||||
OutputConfigurationWrapper() = default;
|
||||
|
||||
OutputConfigurationWrapper(OutputConfiguration &outputConfiguration)
|
||||
: mOutputConfiguration((outputConfiguration)) { }
|
||||
|
||||
bool operator ==(const OutputConfiguration &other) const {
|
||||
const OutputConfiguration &self = mOutputConfiguration;
|
||||
return self.rotation == other.rotation && self.windowGroupId == other.windowGroupId &&
|
||||
self.physicalCameraId == other.physicalCameraId && self.width == other.width &&
|
||||
self.height == other.height && self.isDeferred == other.isDeferred &&
|
||||
areWindowNativeHandlesEqual(self.windowHandles, other.windowHandles);
|
||||
}
|
||||
|
||||
bool operator < (const OutputConfiguration &other) const {
|
||||
if (*this == other) {
|
||||
return false;
|
||||
}
|
||||
const OutputConfiguration &self = mOutputConfiguration;
|
||||
if (self.windowGroupId != other.windowGroupId) {
|
||||
return self.windowGroupId < other.windowGroupId;
|
||||
}
|
||||
|
||||
if (self.width != other.width) {
|
||||
return self.width < other.width;
|
||||
}
|
||||
|
||||
if (self.height != other.height) {
|
||||
return self.height < other.height;
|
||||
}
|
||||
|
||||
if (self.rotation != other.rotation) {
|
||||
return static_cast<uint32_t>(self.rotation) < static_cast<uint32_t>(other.rotation);
|
||||
}
|
||||
|
||||
if (self.isDeferred != other.isDeferred) {
|
||||
return self.isDeferred < other.isDeferred;
|
||||
}
|
||||
|
||||
if (self.physicalCameraId != other.physicalCameraId) {
|
||||
return self.physicalCameraId < other.physicalCameraId;
|
||||
}
|
||||
return areWindowNativeHandlesLessThan(self.windowHandles, other.windowHandles);
|
||||
}
|
||||
|
||||
bool operator != (const OutputConfiguration &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator > (const OutputConfiguration &other) const {
|
||||
return (*this != other) && !(*this < other);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest.
|
||||
frameworks::cameraservice::device::V2_0::CaptureRequest convertToHidl(
|
||||
const CaptureRequest *captureRequest);
|
||||
|
||||
HRotation convertToHidl(int rotation);
|
||||
|
||||
bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata);
|
||||
|
||||
// Note: existing data in dst will be gone. Caller still owns the memory of src
|
||||
void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst);
|
||||
|
||||
TemplateId convertToHidl(ACameraDevice_request_template templateId);
|
||||
|
||||
camera_status_t convertFromHidl(Status status);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace acam
|
||||
} // namespace android
|
||||
|
||||
#endif // CAMERA_NDK_VENDOR_H
|
@ -0,0 +1,525 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "AImageReaderVendorTest"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraCaptureSession.h>
|
||||
#include <media/NdkImage.h>
|
||||
#include <media/NdkImageReader.h>
|
||||
#include <cutils/native_handle.h>
|
||||
|
||||
//#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||
//#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr int kDummyFenceFd = -1;
|
||||
static constexpr int kCaptureWaitUs = 100 * 1000;
|
||||
static constexpr int kCaptureWaitRetry = 10;
|
||||
static constexpr int kTestImageWidth = 640;
|
||||
static constexpr int kTestImageHeight = 480;
|
||||
static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;
|
||||
|
||||
class CameraHelper {
|
||||
public:
|
||||
CameraHelper(native_handle_t* imgReaderAnw) : mImgReaderAnw(imgReaderAnw) {}
|
||||
~CameraHelper() { closeCamera(); }
|
||||
|
||||
int initCamera() {
|
||||
if (mImgReaderAnw == nullptr) {
|
||||
ALOGE("Cannot initialize camera before image reader get initialized.");
|
||||
return -1;
|
||||
}
|
||||
int ret;
|
||||
|
||||
mCameraManager = ACameraManager_create();
|
||||
if (mCameraManager == nullptr) {
|
||||
ALOGE("Failed to create ACameraManager.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("Failed to get cameraIdList: ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (mCameraIdList->numCameras < 1) {
|
||||
ALOGW("Device has no camera on board.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We always use the first camera.
|
||||
mCameraId = mCameraIdList->cameraIds[0];
|
||||
if (mCameraId == nullptr) {
|
||||
ALOGE("Failed to get cameraId.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice);
|
||||
if (ret != AMEDIA_OK || mDevice == nullptr) {
|
||||
ALOGE("Failed to open camera, ret=%d, mDevice=%p.", ret, mDevice);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId, &mCameraMetadata);
|
||||
if (ret != ACAMERA_OK || mCameraMetadata == nullptr) {
|
||||
ALOGE("Get camera %s characteristics failure. ret %d, metadata %p", mCameraId, ret,
|
||||
mCameraMetadata);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
|
||||
ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create capture session
|
||||
ret = ACaptureSessionOutputContainer_create(&mOutputs);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACaptureSessionOutputContainer_create failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACaptureSessionOutput_create failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Create capture request
|
||||
ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACameraOutputTarget_create failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mIsCameraReady = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) {
|
||||
ACameraMetadata_const_entry entry;
|
||||
ACameraMetadata_getConstEntry(
|
||||
mCameraMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
||||
for (uint32_t i = 0; i < entry.count; i++) {
|
||||
if (entry.data.u8[i] == cap) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isCameraReady() { return mIsCameraReady; }
|
||||
|
||||
void closeCamera() {
|
||||
// Destroy capture request
|
||||
if (mReqImgReaderOutput) {
|
||||
ACameraOutputTarget_free(mReqImgReaderOutput);
|
||||
mReqImgReaderOutput = nullptr;
|
||||
}
|
||||
if (mStillRequest) {
|
||||
ACaptureRequest_free(mStillRequest);
|
||||
mStillRequest = nullptr;
|
||||
}
|
||||
// Destroy capture session
|
||||
if (mSession != nullptr) {
|
||||
ACameraCaptureSession_close(mSession);
|
||||
mSession = nullptr;
|
||||
}
|
||||
if (mImgReaderOutput) {
|
||||
ACaptureSessionOutput_free(mImgReaderOutput);
|
||||
mImgReaderOutput = nullptr;
|
||||
}
|
||||
if (mOutputs) {
|
||||
ACaptureSessionOutputContainer_free(mOutputs);
|
||||
mOutputs = nullptr;
|
||||
}
|
||||
// Destroy camera device
|
||||
if (mDevice) {
|
||||
ACameraDevice_close(mDevice);
|
||||
mDevice = nullptr;
|
||||
}
|
||||
if (mCameraMetadata) {
|
||||
ACameraMetadata_free(mCameraMetadata);
|
||||
mCameraMetadata = nullptr;
|
||||
}
|
||||
// Destroy camera manager
|
||||
if (mCameraIdList) {
|
||||
ACameraManager_deleteCameraIdList(mCameraIdList);
|
||||
mCameraIdList = nullptr;
|
||||
}
|
||||
if (mCameraManager) {
|
||||
ACameraManager_delete(mCameraManager);
|
||||
mCameraManager = nullptr;
|
||||
}
|
||||
mIsCameraReady = false;
|
||||
}
|
||||
|
||||
int takePicture() {
|
||||
int seqId;
|
||||
return ACameraCaptureSession_capture(mSession, nullptr, 1, &mStillRequest, &seqId);
|
||||
}
|
||||
|
||||
static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {}
|
||||
|
||||
static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {}
|
||||
|
||||
static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
|
||||
|
||||
static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
|
||||
|
||||
static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
|
||||
|
||||
private:
|
||||
ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected,
|
||||
onDeviceError};
|
||||
ACameraCaptureSession_stateCallbacks mSessionCb{
|
||||
this, onSessionClosed, onSessionReady, onSessionActive};
|
||||
|
||||
native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
|
||||
|
||||
// Camera manager
|
||||
ACameraManager* mCameraManager = nullptr;
|
||||
ACameraIdList* mCameraIdList = nullptr;
|
||||
// Camera device
|
||||
ACameraMetadata* mCameraMetadata = nullptr;
|
||||
ACameraDevice* mDevice = nullptr;
|
||||
// Capture session
|
||||
ACaptureSessionOutputContainer* mOutputs = nullptr;
|
||||
ACaptureSessionOutput* mImgReaderOutput = nullptr;
|
||||
ACameraCaptureSession* mSession = nullptr;
|
||||
// Capture request
|
||||
ACaptureRequest* mStillRequest = nullptr;
|
||||
ACameraOutputTarget* mReqImgReaderOutput = nullptr;
|
||||
|
||||
bool mIsCameraReady = false;
|
||||
const char* mCameraId;
|
||||
};
|
||||
|
||||
class ImageReaderTestCase {
|
||||
public:
|
||||
ImageReaderTestCase(int32_t width,
|
||||
int32_t height,
|
||||
int32_t format,
|
||||
uint64_t usage,
|
||||
int32_t maxImages,
|
||||
bool async)
|
||||
: mWidth(width),
|
||||
mHeight(height),
|
||||
mFormat(format),
|
||||
mUsage(usage),
|
||||
mMaxImages(maxImages),
|
||||
mAsync(async) {}
|
||||
|
||||
~ImageReaderTestCase() {
|
||||
if (mImgReaderAnw) {
|
||||
AImageReader_delete(mImgReader);
|
||||
// No need to call native_handle_t_release on imageReaderAnw
|
||||
}
|
||||
}
|
||||
|
||||
int initImageReader() {
|
||||
if (mImgReader != nullptr || mImgReaderAnw != nullptr) {
|
||||
ALOGE("Cannot re-initalize image reader, mImgReader=%p, mImgReaderAnw=%p", mImgReader,
|
||||
mImgReaderAnw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
media_status_t ret = AImageReader_newWithUsage(
|
||||
mWidth, mHeight, mFormat, mUsage, mMaxImages, &mImgReader);
|
||||
if (ret != AMEDIA_OK || mImgReader == nullptr) {
|
||||
ALOGE("Failed to create new AImageReader, ret=%d, mImgReader=%p", ret, mImgReader);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = AImageReader_setImageListener(mImgReader, &mReaderAvailableCb);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("Failed to set image available listener, ret=%d.", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AImageReader_setBufferRemovedListener(mImgReader, &mReaderDetachedCb);
|
||||
if (ret != AMEDIA_OK) {
|
||||
ALOGE("Failed to set buffer detaching listener, ret=%d.", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AImageReader_getWindowNativeHandle(mImgReader, &mImgReaderAnw);
|
||||
if (ret != AMEDIA_OK || mImgReaderAnw == nullptr) {
|
||||
ALOGE("Failed to get native_handle_t from AImageReader, ret=%d, mImgReaderAnw=%p.", ret,
|
||||
mImgReaderAnw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
native_handle_t* getNativeWindow() { return mImgReaderAnw; }
|
||||
|
||||
int getAcquiredImageCount() {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mAcquiredImageCount;
|
||||
}
|
||||
|
||||
void HandleImageAvailable(AImageReader* reader) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
AImage* outImage = nullptr;
|
||||
media_status_t ret;
|
||||
|
||||
// Make sure AImage will be deleted automatically when it goes out of
|
||||
// scope.
|
||||
auto imageDeleter = [this](AImage* img) {
|
||||
if (mAsync) {
|
||||
AImage_deleteAsync(img, kDummyFenceFd);
|
||||
} else {
|
||||
AImage_delete(img);
|
||||
}
|
||||
};
|
||||
std::unique_ptr<AImage, decltype(imageDeleter)> img(nullptr, imageDeleter);
|
||||
|
||||
if (mAsync) {
|
||||
int outFenceFd = 0;
|
||||
// Verity that outFenceFd's value will be changed by
|
||||
// AImageReader_acquireNextImageAsync.
|
||||
ret = AImageReader_acquireNextImageAsync(reader, &outImage, &outFenceFd);
|
||||
if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd == 0) {
|
||||
ALOGE("Failed to acquire image, ret=%d, outIamge=%p, outFenceFd=%d.", ret, outImage,
|
||||
outFenceFd);
|
||||
return;
|
||||
}
|
||||
img.reset(outImage);
|
||||
} else {
|
||||
ret = AImageReader_acquireNextImage(reader, &outImage);
|
||||
if (ret != AMEDIA_OK || outImage == nullptr) {
|
||||
ALOGE("Failed to acquire image, ret=%d, outIamge=%p.", ret, outImage);
|
||||
return;
|
||||
}
|
||||
img.reset(outImage);
|
||||
}
|
||||
|
||||
AHardwareBuffer* outBuffer = nullptr;
|
||||
ret = AImage_getHardwareBuffer(img.get(), &outBuffer);
|
||||
if (ret != AMEDIA_OK || outBuffer == nullptr) {
|
||||
ALOGE("Faild to get hardware buffer, ret=%d, outBuffer=%p.", ret, outBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to release AHardwareBuffer, since we don't acquire additional
|
||||
// reference to it.
|
||||
AHardwareBuffer_Desc outDesc;
|
||||
AHardwareBuffer_describe(outBuffer, &outDesc);
|
||||
int32_t imageWidth = 0;
|
||||
int32_t imageHeight = 0;
|
||||
int32_t bufferWidth = static_cast<int32_t>(outDesc.width);
|
||||
int32_t bufferHeight = static_cast<int32_t>(outDesc.height);
|
||||
|
||||
AImage_getWidth(outImage, &imageWidth);
|
||||
AImage_getHeight(outImage, &imageHeight);
|
||||
if (imageWidth != mWidth || imageHeight != mHeight) {
|
||||
ALOGE("Mismatched output image dimension: expected=%dx%d, actual=%dx%d", mWidth,
|
||||
mHeight, imageWidth, imageHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFormat == AIMAGE_FORMAT_RGBA_8888 ||
|
||||
mFormat == AIMAGE_FORMAT_RGBX_8888 ||
|
||||
mFormat == AIMAGE_FORMAT_RGB_888 ||
|
||||
mFormat == AIMAGE_FORMAT_RGB_565 ||
|
||||
mFormat == AIMAGE_FORMAT_RGBA_FP16 ||
|
||||
mFormat == AIMAGE_FORMAT_YUV_420_888 ||
|
||||
mFormat == AIMAGE_FORMAT_Y8) {
|
||||
// Check output buffer dimension for certain formats. Don't do this for blob based
|
||||
// formats.
|
||||
if (bufferWidth != mWidth || bufferHeight != mHeight) {
|
||||
ALOGE("Mismatched output buffer dimension: expected=%dx%d, actual=%dx%d", mWidth,
|
||||
mHeight, bufferWidth, bufferHeight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((outDesc.usage & mUsage) != mUsage) {
|
||||
ALOGE("Mismatched output buffer usage: actual (%" PRIu64 "), expected (%" PRIu64 ")",
|
||||
outDesc.usage, mUsage);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* data = nullptr;
|
||||
int dataLength = 0;
|
||||
ret = AImage_getPlaneData(img.get(), 0, &data, &dataLength);
|
||||
if (mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) {
|
||||
// When we have CPU_READ_OFTEN usage bits, we can lock the image.
|
||||
if (ret != AMEDIA_OK || data == nullptr || dataLength < 0) {
|
||||
ALOGE("Failed to access CPU data, ret=%d, data=%p, dataLength=%d", ret, data,
|
||||
dataLength);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (ret != AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE || data != nullptr || dataLength != 0) {
|
||||
ALOGE("Shouldn't be able to access CPU data, ret=%d, data=%p, dataLength=%d", ret,
|
||||
data, dataLength);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Only increase mAcquiredImageCount if all checks pass.
|
||||
mAcquiredImageCount++;
|
||||
}
|
||||
|
||||
static void onImageAvailable(void* obj, AImageReader* reader) {
|
||||
ImageReaderTestCase* thiz = reinterpret_cast<ImageReaderTestCase*>(obj);
|
||||
thiz->HandleImageAvailable(reader);
|
||||
}
|
||||
|
||||
static void
|
||||
onBufferRemoved(void* /*obj*/, AImageReader* /*reader*/, AHardwareBuffer* /*buffer*/) {
|
||||
// No-op, just to check the listener can be set properly.
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mWidth;
|
||||
int32_t mHeight;
|
||||
int32_t mFormat;
|
||||
uint64_t mUsage;
|
||||
int32_t mMaxImages;
|
||||
bool mAsync;
|
||||
|
||||
std::mutex mMutex;
|
||||
int mAcquiredImageCount{0};
|
||||
|
||||
AImageReader* mImgReader = nullptr;
|
||||
native_handle_t* mImgReaderAnw = nullptr;
|
||||
|
||||
AImageReader_ImageListener mReaderAvailableCb{this, onImageAvailable};
|
||||
AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved};
|
||||
};
|
||||
|
||||
int takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) {
|
||||
int ret = 0;
|
||||
|
||||
ImageReaderTestCase testCase(
|
||||
kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages,
|
||||
readerAsync);
|
||||
ret = testCase.initImageReader();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
CameraHelper cameraHelper(testCase.getNativeWindow());
|
||||
ret = cameraHelper.initCamera();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!cameraHelper.isCameraReady()) {
|
||||
ALOGW("Camera is not ready after successful initialization. It's either due to camera on "
|
||||
"board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on "
|
||||
"board.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pictureCount; i++) {
|
||||
ret = cameraHelper.takePicture();
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep until all capture finished
|
||||
for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) {
|
||||
usleep(kCaptureWaitUs);
|
||||
if (testCase.getAcquiredImageCount() == pictureCount) {
|
||||
ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000,
|
||||
pictureCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return testCase.getAcquiredImageCount() == pictureCount ? 0 : -1;
|
||||
}
|
||||
|
||||
class AImageReaderWindowHandleTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
}
|
||||
void TearDown() override {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool testTakePicturesNative() {
|
||||
for (auto& readerUsage :
|
||||
{AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) {
|
||||
for (auto& readerMaxImages : {1, 4, 8}) {
|
||||
for (auto& readerAsync : {true, false}) {
|
||||
for (auto& pictureCount : {1, 4, 8}) {
|
||||
if (takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) {
|
||||
ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, "
|
||||
"async=%d, pictureCount=%d",
|
||||
readerUsage, readerMaxImages, readerAsync, pictureCount);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) {
|
||||
EXPECT_TRUE(testTakePicturesNative());
|
||||
}
|
||||
|
||||
} // namespace
|
Loading…
Reference in new issue