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