Merge "MediaPlayer2: replace canOffloadStream with public API."
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index e84eeef..60c9f85 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -70,3 +70,81 @@
],
version_script: "libcamera2ndk.map.txt",
}
+
+cc_library_shared {
+ name: "libcamera2ndk_vendor",
+ vendor_available: true,
+ srcs: [
+ "ndk_vendor/impl/ACameraDevice.cpp",
+ "ndk_vendor/impl/ACameraManager.cpp",
+ "ndk_vendor/impl/utils.cpp",
+ "impl/ACameraMetadata.cpp",
+ "impl/ACameraCaptureSession.cpp",
+ "NdkCameraMetadata.cpp",
+ "NdkCameraCaptureSession.cpp",
+ "NdkCameraManager.cpp",
+ "NdkCameraDevice.cpp",
+ "NdkCaptureRequest.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libcutils",
+ ],
+ local_include_dirs: [
+ ".",
+ "include",
+ "impl",
+ ],
+ cflags: [
+ "-fvisibility=hidden",
+ "-DEXPORT=__attribute__((visibility(\"default\")))",
+ "-D__ANDROID_VNDK__",
+ ],
+
+ shared_libs: [
+ "libhwbinder",
+ "libfmq",
+ "libhidlbase",
+ "libhardware",
+ "libnativewindow",
+ "liblog",
+ "libutils",
+ "libstagefright_foundation",
+ "libcutils",
+ "libcamera_metadata",
+ "libmediandk",
+ "android.frameworks.cameraservice.device@2.0",
+ "android.frameworks.cameraservice.common@2.0",
+ "android.frameworks.cameraservice.service@2.0",
+ ],
+
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ "libarect",
+ ],
+ product_variables: {
+ pdk: {
+ enabled: false,
+ },
+ },
+}
+
+cc_test {
+ name: "AImageReaderVendorTest",
+ vendor: true,
+ srcs: ["ndk_vendor/tests/AImageReaderVendorTest.cpp"],
+ shared_libs: [
+ "libhwbinder",
+ "libcamera2ndk_vendor",
+ "libmediandk",
+ "libnativewindow",
+ "libutils",
+ "libui",
+ "libcutils",
+ "liblog",
+ ],
+ cflags: [
+ "-D__ANDROID_VNDK__",
+ ],
+}
diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp
index 812a312..ef05e0b 100644
--- a/camera/ndk/NdkCameraDevice.cpp
+++ b/camera/ndk/NdkCameraDevice.cpp
@@ -24,7 +24,15 @@
#include <camera/NdkCameraDevice.h>
#include "impl/ACameraCaptureSession.h"
-using namespace android;
+using namespace android::acam;
+
+bool areWindowTypesEqual(ACameraWindowType *a, ACameraWindowType *b) {
+#ifdef __ANDROID_VNDK__
+ return utils::isWindowNativeHandleEqual(a, b);
+#else
+ return a == b;
+#endif
+}
EXPORT
camera_status_t ACameraDevice_close(ACameraDevice* device) {
@@ -96,7 +104,7 @@
EXPORT
camera_status_t ACaptureSessionOutput_create(
- ANativeWindow* window, /*out*/ACaptureSessionOutput** out) {
+ ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) {
ATRACE_CALL();
if (window == nullptr || out == nullptr) {
ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -109,7 +117,7 @@
EXPORT
camera_status_t ACaptureSessionSharedOutput_create(
- ANativeWindow* window, /*out*/ACaptureSessionOutput** out) {
+ ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) {
ATRACE_CALL();
if (window == nullptr || out == nullptr) {
ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -122,7 +130,7 @@
EXPORT
camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out,
- ANativeWindow* window) {
+ ACameraWindowType* window) {
ATRACE_CALL();
if ((window == nullptr) || (out == nullptr)) {
ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -134,7 +142,7 @@
__FUNCTION__);
return ACAMERA_ERROR_INVALID_OPERATION;
}
- if (out->mWindow == window) {
+ if (areWindowTypesEqual(out->mWindow, window)) {
ALOGE("%s: Error trying to add the same window associated with the output configuration",
__FUNCTION__);
return ACAMERA_ERROR_INVALID_PARAMETER;
@@ -147,7 +155,7 @@
EXPORT
camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *out,
- ANativeWindow* window) {
+ ACameraWindowType* window) {
ATRACE_CALL();
if ((window == nullptr) || (out == nullptr)) {
ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -159,7 +167,7 @@
__FUNCTION__);
return ACAMERA_ERROR_INVALID_OPERATION;
}
- if (out->mWindow == window) {
+ if (areWindowTypesEqual(out->mWindow, window)) {
ALOGE("%s: Error trying to remove the same window associated with the output configuration",
__FUNCTION__);
return ACAMERA_ERROR_INVALID_PARAMETER;
diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp
index f14a4c9..8742d9c 100644
--- a/camera/ndk/NdkCameraManager.cpp
+++ b/camera/ndk/NdkCameraManager.cpp
@@ -22,10 +22,15 @@
#include <utils/Trace.h>
#include <camera/NdkCameraManager.h>
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraManager.h"
+#else
#include "impl/ACameraManager.h"
+#endif
#include "impl/ACameraMetadata.h"
-using namespace android;
+using namespace android::acam;
EXPORT
ACameraManager* ACameraManager_create() {
diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp
index ddb69d7..c64de3e 100644
--- a/camera/ndk/NdkCaptureRequest.cpp
+++ b/camera/ndk/NdkCaptureRequest.cpp
@@ -27,7 +27,7 @@
EXPORT
camera_status_t ACameraOutputTarget_create(
- ANativeWindow* window, ACameraOutputTarget** out) {
+ ACameraWindowType* window, ACameraOutputTarget** out) {
ATRACE_CALL();
if (window == nullptr) {
ALOGE("%s: Error: input window is null", __FUNCTION__);
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index f60e5fd..fb72bdb 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -23,7 +23,7 @@
ACameraCaptureSession::~ACameraCaptureSession() {
ALOGV("~ACameraCaptureSession: %p notify device end of life", this);
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev != nullptr && !dev->isClosed()) {
dev->lockDeviceForSessionOps();
{
@@ -48,7 +48,7 @@
mClosedByApp = true;
}
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev != nullptr) {
dev->lockDeviceForSessionOps();
}
@@ -73,7 +73,7 @@
camera_status_t
ACameraCaptureSession::stopRepeating() {
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return ACAMERA_ERROR_SESSION_CLOSED;
@@ -91,7 +91,7 @@
camera_status_t
ACameraCaptureSession::abortCaptures() {
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return ACAMERA_ERROR_SESSION_CLOSED;
@@ -112,7 +112,7 @@
/*optional*/ACameraCaptureSession_captureCallbacks* cbs,
int numRequests, ACaptureRequest** requests,
/*optional*/int* captureSequenceId) {
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return ACAMERA_ERROR_SESSION_CLOSED;
@@ -133,7 +133,7 @@
/*optional*/ACameraCaptureSession_captureCallbacks* cbs,
int numRequests, ACaptureRequest** requests,
/*optional*/int* captureSequenceId) {
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return ACAMERA_ERROR_SESSION_CLOSED;
@@ -149,7 +149,7 @@
}
camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) {
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return ACAMERA_ERROR_SESSION_CLOSED;
@@ -168,7 +168,7 @@
ACameraDevice*
ACameraCaptureSession::getDevice() {
Mutex::Autolock _l(mSessionLock);
- sp<CameraDevice> dev = getDeviceSp();
+ sp<acam::CameraDevice> dev = getDeviceSp();
if (dev == nullptr) {
ALOGE("Error: Device associated with session %p has been closed!", this);
return nullptr;
@@ -182,9 +182,9 @@
mIsClosed = true;
}
-sp<CameraDevice>
+sp<acam::CameraDevice>
ACameraCaptureSession::getDeviceSp() {
- sp<CameraDevice> device = mDevice.promote();
+ sp<acam::CameraDevice> device = mDevice.promote();
if (device == nullptr || device->isClosed()) {
ALOGW("Device is closed but session %d is not notified", mId);
return nullptr;
diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h
index a2068e7..133c2c8 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -19,12 +19,17 @@
#include <set>
#include <hardware/camera3.h>
#include <camera/NdkCameraDevice.h>
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "ndk_vendor/impl/ACameraCaptureSessionVendor.h"
+#else
#include "ACameraDevice.h"
using namespace android;
struct ACaptureSessionOutput {
- explicit ACaptureSessionOutput(ANativeWindow* window, bool isShared = false) :
+ explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false) :
mWindow(window), mIsShared(isShared) {};
bool operator == (const ACaptureSessionOutput& other) const {
@@ -40,11 +45,12 @@
return mWindow > other.mWindow;
}
- ANativeWindow* mWindow;
- std::set<ANativeWindow *> mSharedWindows;
+ ACameraWindowType* mWindow;
+ std::set<ACameraWindowType *> mSharedWindows;
bool mIsShared;
int mRotation = CAMERA3_STREAM_ROTATION_0;
};
+#endif
struct ACaptureSessionOutputContainer {
std::set<ACaptureSessionOutput> mOutputs;
@@ -60,7 +66,7 @@
int id,
const ACaptureSessionOutputContainer* outputs,
const ACameraCaptureSession_stateCallbacks* cb,
- CameraDevice* device) :
+ android::acam::CameraDevice* device) :
mId(id), mOutput(*outputs), mUserSessionCallback(*cb),
mDevice(device) {}
@@ -97,18 +103,18 @@
ACameraDevice* getDevice();
private:
- friend class CameraDevice;
+ friend class android::acam::CameraDevice;
// Close session because app close camera device, camera device got ERROR_DISCONNECTED,
// or a new session is replacing this session.
void closeByDevice();
- sp<CameraDevice> getDeviceSp();
+ sp<android::acam::CameraDevice> getDeviceSp();
const int mId;
const ACaptureSessionOutputContainer mOutput;
const ACameraCaptureSession_stateCallbacks mUserSessionCallback;
- const wp<CameraDevice> mDevice;
+ const wp<android::acam::CameraDevice> mDevice;
bool mIsClosed = false;
bool mClosedByApp = false;
Mutex mSessionLock;
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index ac3be25..00da54e 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -27,9 +27,9 @@
#include "ACaptureRequest.h"
#include "ACameraCaptureSession.h"
-using namespace android;
-
namespace android {
+namespace acam {
+
// Static member definitions
const char* CameraDevice::kContextKey = "Context";
const char* CameraDevice::kDeviceKey = "Device";
@@ -1513,5 +1513,5 @@
return ret;
}
-
+} // namespace acam
} // namespace android
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 7d64081..8f56d3f 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -41,6 +41,7 @@
#include "ACameraMetadata.h"
namespace android {
+namespace acam {
// Wrap ACameraCaptureFailure so it can be ref-counted
struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {};
@@ -286,6 +287,7 @@
};
+} // namespace acam;
} // namespace android;
/**
@@ -295,7 +297,7 @@
struct ACameraDevice {
ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
sp<ACameraMetadata> chars) :
- mDevice(new CameraDevice(id, cb, chars, this)) {}
+ mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {}
~ACameraDevice() {};
@@ -331,7 +333,7 @@
}
private:
- android::sp<android::CameraDevice> mDevice;
+ android::sp<android::acam::CameraDevice> mDevice;
};
#endif // _ACAMERA_DEVICE_H
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index ee67677..7d6ecac 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -26,9 +26,10 @@
#include <stdlib.h>
#include <camera/VendorTagDescriptor.h>
-using namespace android;
+using namespace android::acam;
namespace android {
+namespace acam {
// Static member definitions
const char* CameraManagerGlobal::kCameraIdKey = "CameraId";
const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
@@ -345,6 +346,7 @@
}
}
+} // namespace acam
} // namespace android
/**
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index ce65769..55bfa7e 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -35,6 +35,7 @@
#include <map>
namespace android {
+namespace acam {
/**
* Per-process singleton instance of CameraManger. Shared by all ACameraManager
@@ -172,6 +173,7 @@
~CameraManagerGlobal();
};
+} // namespace acam;
} // namespace android;
/**
@@ -180,7 +182,7 @@
*/
struct ACameraManager {
ACameraManager() :
- mGlobalManager(&(android::CameraManagerGlobal::getInstance())) {}
+ mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {}
~ACameraManager();
camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
static void deleteCameraIdList(ACameraIdList* cameraIdList);
@@ -196,7 +198,7 @@
kCameraIdListNotInit = -1
};
android::Mutex mLock;
- android::sp<android::CameraManagerGlobal> mGlobalManager;
+ android::sp<android::acam::CameraManagerGlobal> mGlobalManager;
};
#endif //_ACAMERA_MANAGER_H
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index e76b80c..f21dbaf 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -22,7 +22,13 @@
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
+
+#ifdef __ANDROID_VNDK__
+#include <CameraMetadata.h>
+using CameraMetadata = android::hardware::camera::common::V1_0::helper::CameraMetadata;
+#else
#include <camera/CameraMetadata.h>
+#endif
#include <camera/NdkCameraMetadata.h>
diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h
index b11dafb..5c82ab7 100644
--- a/camera/ndk/impl/ACaptureRequest.h
+++ b/camera/ndk/impl/ACaptureRequest.h
@@ -21,8 +21,11 @@
using namespace android;
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACaptureRequestVendor.h"
+#else
struct ACameraOutputTarget {
- explicit ACameraOutputTarget(ANativeWindow* window) : mWindow(window) {};
+ explicit ACameraOutputTarget(ACameraWindowType* window) : mWindow(window) {};
bool operator == (const ACameraOutputTarget& other) const {
return mWindow == other.mWindow;
@@ -37,8 +40,9 @@
return mWindow > other.mWindow;
}
- ANativeWindow* mWindow;
+ ACameraWindowType* mWindow;
};
+#endif
struct ACameraOutputTargets {
std::set<ACameraOutputTarget> mOutputs;
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 5e0db60..1244582 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -35,10 +35,10 @@
#include <sys/cdefs.h>
#include <stdbool.h>
-#include <android/native_window.h>
#include "NdkCameraError.h"
#include "NdkCameraMetadata.h"
#include "NdkCaptureRequest.h"
+#include "NdkCameraWindowType.h"
#ifndef _NDK_CAMERA_CAPTURE_SESSION_H
#define _NDK_CAMERA_CAPTURE_SESSION_H
@@ -246,7 +246,7 @@
*/
typedef void (*ACameraCaptureSession_captureCallback_bufferLost)(
void* context, ACameraCaptureSession* session,
- ACaptureRequest* request, ANativeWindow* window, int64_t frameNumber);
+ ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber);
typedef struct ACameraCaptureSession_captureCallbacks {
/// optional application context.
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 7c13b34..4fe43d5 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -34,10 +34,10 @@
*/
#include <sys/cdefs.h>
-#include <android/native_window.h>
#include "NdkCameraError.h"
#include "NdkCaptureRequest.h"
#include "NdkCameraCaptureSession.h"
+#include "NdkCameraWindowType.h"
#ifndef _NDK_CAMERA_DEVICE_H
#define _NDK_CAMERA_DEVICE_H
@@ -345,7 +345,7 @@
* @see ACaptureSessionOutputContainer_add
*/
camera_status_t ACaptureSessionOutput_create(
- ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24);
+ ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24);
/**
* Free a ACaptureSessionOutput object.
@@ -694,7 +694,7 @@
* @see ACaptureSessionOutputContainer_add
*/
camera_status_t ACaptureSessionSharedOutput_create(
- ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28);
+ ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28);
/**
* Add a native window to shared ACaptureSessionOutput.
@@ -712,7 +712,7 @@
* ACaptureSessionOutput.</li></ul>
*/
camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output,
- ANativeWindow *anw) __INTRODUCED_IN(28);
+ ACameraWindowType *anw) __INTRODUCED_IN(28);
/**
* Remove a native window from shared ACaptureSessionOutput.
@@ -728,7 +728,7 @@
* ACaptureSessionOutput.</li></ul>
*/
camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output,
- ANativeWindow* anw) __INTRODUCED_IN(28);
+ ACameraWindowType* anw) __INTRODUCED_IN(28);
/**
* Create a new camera capture session similar to {@link ACameraDevice_createCaptureSession}. This
diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h
new file mode 100644
index 0000000..99f67e9
--- /dev/null
+++ b/camera/ndk/include/camera/NdkCameraWindowType.h
@@ -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
diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h
index 5340e76..136989a 100644
--- a/camera/ndk/include/camera/NdkCaptureRequest.h
+++ b/camera/ndk/include/camera/NdkCaptureRequest.h
@@ -35,9 +35,9 @@
#include <sys/cdefs.h>
-#include <android/native_window.h>
#include "NdkCameraError.h"
#include "NdkCameraMetadata.h"
+#include "NdkCameraWindowType.h"
#ifndef _NDK_CAPTURE_REQUEST_H
#define _NDK_CAPTURE_REQUEST_H
@@ -101,7 +101,7 @@
*
* @see ACaptureRequest_addTarget
*/
-camera_status_t ACameraOutputTarget_create(ANativeWindow* window,
+camera_status_t ACameraOutputTarget_create(ACameraWindowType* window,
ACameraOutputTarget** output) __INTRODUCED_IN(24);
/**
diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
new file mode 100644
index 0000000..8d9e90c
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.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;
+};
+
+
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
new file mode 100644
index 0000000..b86f854
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -0,0 +1,1594 @@
+/*
+ * 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 "ACameraDeviceVendor"
+
+#include <vector>
+#include <inttypes.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <CameraMetadata.h>
+
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "ACameraCaptureSession.h"
+#include "ACameraMetadata.h"
+#include "ACaptureRequest.h"
+#include "utils.h"
+
+using namespace android;
+
+namespace android {
+namespace acam {
+
+using HCameraMetadata = frameworks::cameraservice::device::V2_0::CameraMetadata;
+using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using hardware::Void;
+
+// Static member definitions
+const char* CameraDevice::kContextKey = "Context";
+const char* CameraDevice::kDeviceKey = "Device";
+const char* CameraDevice::kErrorCodeKey = "ErrorCode";
+const char* CameraDevice::kCallbackFpKey = "Callback";
+const char* CameraDevice::kSessionSpKey = "SessionSp";
+const char* CameraDevice::kCaptureRequestKey = "CaptureRequest";
+const char* CameraDevice::kTimeStampKey = "TimeStamp";
+const char* CameraDevice::kCaptureResultKey = "CaptureResult";
+const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
+const char* CameraDevice::kSequenceIdKey = "SequenceId";
+const char* CameraDevice::kFrameNumberKey = "FrameNumber";
+const char* CameraDevice::kAnwKey = "Anw";
+
+/**
+ * CameraDevice Implementation
+ */
+CameraDevice::CameraDevice(
+ const char* id,
+ ACameraDevice_StateCallbacks* cb,
+ sp<ACameraMetadata> chars,
+ ACameraDevice* wrapper) :
+ mCameraId(id),
+ mAppCallbacks(*cb),
+ mChars(std::move(chars)),
+ mServiceCallback(new ServiceCallback(this)),
+ mWrapper(wrapper),
+ mInError(false),
+ mError(ACAMERA_OK),
+ mIdle(true),
+ mCurrentSession(nullptr) {
+ mClosing = false;
+ // Setup looper thread to perfrom device callbacks to app
+ mCbLooper = new ALooper;
+ mCbLooper->setName("C2N-dev-looper");
+ status_t err = mCbLooper->start(
+ /*runOnCallingThread*/false,
+ /*canCallJava*/ true,
+ PRIORITY_DEFAULT);
+ if (err != OK) {
+ ALOGE("%s: Unable to start camera device callback looper: %s (%d)",
+ __FUNCTION__, strerror(-err), err);
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ }
+ mHandler = new CallbackHandler();
+ mCbLooper->registerHandler(mHandler);
+
+ const CameraMetadata& metadata = mChars->getInternalData();
+ camera_metadata_ro_entry entry = metadata.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
+ if (entry.count != 1) {
+ ALOGW("%s: bad count %zu for partial result count", __FUNCTION__, entry.count);
+ mPartialResultCount = 1;
+ } else {
+ mPartialResultCount = entry.data.i32[0];
+ }
+
+ entry = metadata.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ if (entry.count != 2) {
+ ALOGW("%s: bad count %zu for shading map size", __FUNCTION__, entry.count);
+ mShadingMapSize[0] = 0;
+ mShadingMapSize[1] = 0;
+ } else {
+ mShadingMapSize[0] = entry.data.i32[0];
+ mShadingMapSize[1] = entry.data.i32[1];
+ }
+}
+
+// Device close implementaiton
+CameraDevice::~CameraDevice() {
+ sp<ACameraCaptureSession> session = mCurrentSession.promote();
+ {
+ Mutex::Autolock _l(mDeviceLock);
+ if (!isClosed()) {
+ disconnectLocked(session);
+ }
+ mCurrentSession = nullptr;
+ if (mCbLooper != nullptr) {
+ mCbLooper->unregisterHandler(mHandler->id());
+ mCbLooper->stop();
+ }
+ }
+ mCbLooper.clear();
+ mHandler.clear();
+}
+
+void
+CameraDevice::postSessionMsgAndCleanup(sp<AMessage>& msg) {
+ msg->post();
+ msg.clear();
+ sp<AMessage> cleanupMsg = new AMessage(kWhatCleanUpSessions, mHandler);
+ cleanupMsg->post();
+}
+
+// TODO: cached created request?
+camera_status_t
+CameraDevice::createCaptureRequest(
+ ACameraDevice_request_template templateId,
+ ACaptureRequest** request) const {
+ Mutex::Autolock _l(mDeviceLock);
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+ if (mRemote == nullptr) {
+ return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+ }
+ CameraMetadata rawRequest;
+ Status status = Status::NO_ERROR;
+ auto remoteRet = mRemote->createDefaultRequest(
+ utils::convertToHidl(templateId),
+ [&status, &rawRequest](auto s, const hidl_vec<uint8_t> &metadata) {
+ status = s;
+ if (status == Status::NO_ERROR && utils::convertFromHidlCloned(metadata, &rawRequest)) {
+ } else {
+ ALOGE("%s: Couldn't create default request", __FUNCTION__);
+ }
+ });
+ if (!remoteRet.isOk()) {
+ ALOGE("%s: Transaction error while trying to create default request %s", __FUNCTION__,
+ remoteRet.description().c_str());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ if (status != Status::NO_ERROR) {
+ return utils::convertFromHidl(status);
+ }
+ ACaptureRequest* outReq = new ACaptureRequest();
+ outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST);
+ outReq->targets = new ACameraOutputTargets();
+ *request = outReq;
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::createCaptureSession(
+ const ACaptureSessionOutputContainer* outputs,
+ const ACaptureRequest* sessionParameters,
+ const ACameraCaptureSession_stateCallbacks* callbacks,
+ /*out*/ACameraCaptureSession** session) {
+ sp<ACameraCaptureSession> currentSession = mCurrentSession.promote();
+ Mutex::Autolock _l(mDeviceLock);
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ if (currentSession != nullptr) {
+ currentSession->closeByDevice();
+ stopRepeatingLocked();
+ }
+
+ // Create new session
+ ret = configureStreamsLocked(outputs, sessionParameters);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Fail to create new session. cannot configure streams");
+ return ret;
+ }
+
+ ACameraCaptureSession* newSession = new ACameraCaptureSession(
+ mNextSessionId++, outputs, callbacks, this);
+
+ // set new session as current session
+ newSession->incStrong((void *) ACameraDevice_createCaptureSession);
+ mCurrentSession = newSession;
+ mFlushing = false;
+ *session = newSession;
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::captureLocked(
+ sp<ACameraCaptureSession> session,
+ /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+ int numRequests, ACaptureRequest** requests,
+ /*optional*/int* captureSequenceId) {
+ return submitRequestsLocked(
+ session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+ sp<ACameraCaptureSession> session,
+ /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+ int numRequests, ACaptureRequest** requests,
+ /*optional*/int* captureSequenceId) {
+ return submitRequestsLocked(
+ session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
+ sp<CaptureRequest> &req) {
+ CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData();
+ const camera_metadata_t *camera_metadata = metadataCopy.getAndLock();
+ HCameraMetadata hCameraMetadata;
+ utils::convertToHidl(camera_metadata, &hCameraMetadata);
+ metadataCopy.unlock(camera_metadata);
+ req->mPhysicalCameraSettings.resize(1);
+ req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata));
+}
+
+camera_status_t
+CameraDevice::submitRequestsLocked(
+ sp<ACameraCaptureSession> session,
+ /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+ int numRequests, ACaptureRequest** requests,
+ /*optional*/int* captureSequenceId,
+ bool isRepeating) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
+ return ret;
+ }
+
+ // Form two vectors of capture request, one for internal tracking
+ std::vector<frameworks::cameraservice::device::V2_0::CaptureRequest> requestList;
+ Vector<sp<CaptureRequest>> requestsV;
+ requestsV.setCapacity(numRequests);
+ for (int i = 0; i < numRequests; i++) {
+ sp<CaptureRequest> req;
+ ret = allocateCaptureRequest(requests[i], req);
+ // We need to call this method since after submitRequestList is called,
+ // the request metadata queue might have removed the capture request
+ // metadata. Therefore we simply add the metadata to its wrapper class,
+ // so that it can be retrived later.
+ addRequestSettingsMetadata(requests[i], req);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Convert capture request to internal format failure! ret %d", ret);
+ return ret;
+ }
+ if (req->mCaptureRequest.streamAndWindowIds.size() == 0) {
+ ALOGE("Capture request without output target cannot be submitted!");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ requestList.push_back(utils::convertToHidl(req.get()));
+ requestsV.push_back(req);
+ }
+ if (isRepeating) {
+ ret = stopRepeatingLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+ return ret;
+ }
+ }
+
+ SubmitInfo info;
+ Status status;
+ auto remoteRet = mRemote->submitRequestList(requestList, isRepeating,
+ [&status, &info](auto s, auto &submitInfo) {
+ status = s;
+ info = submitInfo;
+ });
+ if (!remoteRet.isOk()) {
+ ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__,
+ remoteRet.description().c_str());
+ }
+ if (status != Status::NO_ERROR) {
+ return utils::convertFromHidl(status);
+ }
+ int32_t sequenceId = info.requestId;
+ int64_t lastFrameNumber = info.lastFrameNumber;
+ if (sequenceId < 0) {
+ ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
+ mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
+
+ if (isRepeating) {
+ // stopRepeating above should have cleanup repeating sequence id
+ if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ return ACAMERA_ERROR_CAMERA_DEVICE;
+ }
+ mRepeatingSequenceId = sequenceId;
+ } else {
+ mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+ }
+
+ if (mIdle) {
+ sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+ msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+ postSessionMsgAndCleanup(msg);
+ }
+ mIdle = false;
+ mBusySession = session;
+
+ if (captureSequenceId) {
+ *captureSequenceId = sequenceId;
+ }
+ return ACAMERA_OK;
+}
+
+camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ if (output == nullptr) {
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!output->mIsShared) {
+ ALOGE("Error output configuration is not shared");
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+
+ int32_t streamId = -1;
+ for (auto& kvPair : mConfiguredOutputs) {
+ if (utils::isWindowNativeHandleEqual(kvPair.second.first, output->mWindow)) {
+ streamId = kvPair.first;
+ break;
+ }
+ }
+ if (streamId < 0) {
+ ALOGE("Error: Invalid output configuration");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ OutputConfigurationWrapper outConfigW;
+ OutputConfiguration &outConfig = outConfigW.mOutputConfiguration;
+ outConfig.rotation = utils::convertToHidl(output->mRotation);
+ outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing.
+ outConfig.windowHandles.resize(output->mSharedWindows.size() + 1);
+ outConfig.windowHandles[0] = output->mWindow;
+ int i = 1;
+ for (auto& anw : output->mSharedWindows) {
+ outConfig.windowHandles[i++] = anw;
+ }
+
+ auto remoteRet = mRemote->updateOutputConfiguration(streamId, outConfig);
+ if (!remoteRet.isOk()) {
+ ALOGE("%s: Transaction error in updating OutputConfiguration: %s", __FUNCTION__,
+ remoteRet.description().c_str());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ switch (remoteRet) {
+ case Status::NO_ERROR:
+ break;
+ case Status::INVALID_OPERATION:
+ ALOGE("Camera device %s invalid operation", getId());
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ case Status::ALREADY_EXISTS:
+ ALOGE("Camera device %s output surface already exists", getId());
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ case Status::ILLEGAL_ARGUMENT:
+ ALOGE("Camera device %s invalid input argument", getId());
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ default:
+ ALOGE("Camera device %s failed to add shared output", getId());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ mConfiguredOutputs[streamId] = std::make_pair(output->mWindow, outConfigW);
+
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::allocateCaptureRequest(
+ const ACaptureRequest* request, /*out*/sp<CaptureRequest> &outReq) {
+ sp<CaptureRequest> req(new CaptureRequest());
+ req->mCaptureRequest.physicalCameraSettings.resize(1);
+ req->mCaptureRequest.physicalCameraSettings[0].id = mCameraId;
+ // TODO: Do we really need to copy the metadata here ?
+ CameraMetadata metadataCopy = request->settings->getInternalData();
+ const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock();
+ HCameraMetadata hCameraMetadata;
+ utils::convertToHidl(cameraMetadata, &hCameraMetadata);
+ metadataCopy.unlock(cameraMetadata);
+ if (request->settings != nullptr) {
+ if (hCameraMetadata.data() != nullptr &&
+ mCaptureRequestMetadataQueue != nullptr &&
+ mCaptureRequestMetadataQueue->write(
+ reinterpret_cast<const uint8_t *>(hCameraMetadata.data()),
+ hCameraMetadata.size())) {
+ // The metadata field of the union would've been destructued, so no need
+ // to re-size it.
+ req->mCaptureRequest.physicalCameraSettings[0].settings.fmqMetadataSize(
+ hCameraMetadata.size());
+ } else {
+ ALOGE("Fmq write capture result failed, falling back to hwbinder");
+ req->mCaptureRequest.physicalCameraSettings[0].settings.metadata(
+ std::move(hCameraMetadata));
+ }
+ }
+
+ std::vector<int32_t> requestStreamIdxList;
+ std::vector<int32_t> requestSurfaceIdxList;
+ for (auto outputTarget : request->targets->mOutputs) {
+ native_handle_t* anw = outputTarget.mWindow;
+ bool found = false;
+ req->mSurfaceList.push_back(anw);
+ // lookup stream/surface ID
+ for (const auto& kvPair : mConfiguredOutputs) {
+ int streamId = kvPair.first;
+ const OutputConfigurationWrapper& outConfig = kvPair.second.second;
+ const auto& windowHandles = outConfig.mOutputConfiguration.windowHandles;
+ for (int surfaceId = 0; surfaceId < (int) windowHandles.size(); surfaceId++) {
+ // If two native handles are equivalent, so are their surfaces.
+ if (utils::isWindowNativeHandleEqual(windowHandles[surfaceId].getNativeHandle(),
+ anw)) {
+ found = true;
+ requestStreamIdxList.push_back(streamId);
+ requestSurfaceIdxList.push_back(surfaceId);
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("Unconfigured output target %p in capture request!", anw);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ }
+ req->mCaptureRequest.streamAndWindowIds.resize(requestStreamIdxList.size());
+ for (int i = 0; i < requestStreamIdxList.size(); i++) {
+ req->mCaptureRequest.streamAndWindowIds[i].streamId = requestStreamIdxList[i];
+ req->mCaptureRequest.streamAndWindowIds[i].windowId = requestSurfaceIdxList[i];
+ }
+ outReq = req;
+ return ACAMERA_OK;
+}
+
+ACaptureRequest*
+CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req) {
+ ACaptureRequest* pRequest = new ACaptureRequest();
+ CameraMetadata clone;
+ utils::convertFromHidlCloned(req->mPhysicalCameraSettings[0].settings.metadata(), &clone);
+ pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+ pRequest->targets = new ACameraOutputTargets();
+ for (size_t i = 0; i < req->mSurfaceList.size(); i++) {
+ native_handle_t* anw = req->mSurfaceList[i];
+ ACameraOutputTarget outputTarget(anw);
+ pRequest->targets->mOutputs.insert(outputTarget);
+ }
+ return pRequest;
+}
+
+void
+CameraDevice::freeACaptureRequest(ACaptureRequest* req) {
+ if (req == nullptr) {
+ return;
+ }
+ req->settings.clear();
+ delete req->targets;
+ delete req;
+}
+
+void
+CameraDevice::notifySessionEndOfLifeLocked(ACameraCaptureSession* session) {
+ if (isClosed()) {
+ // Device is closing already. do nothing
+ return;
+ }
+
+ if (mCurrentSession != session) {
+ // Session has been replaced by other seesion or device is closed
+ return;
+ }
+ mCurrentSession = nullptr;
+
+ // Should not happen
+ if (!session->mIsClosed) {
+ ALOGE("Error: unclosed session %p reaches end of life!", session);
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ return;
+ }
+
+ // No new session, unconfigure now
+ camera_status_t ret = configureStreamsLocked(nullptr, nullptr);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret);
+ }
+}
+
+void
+CameraDevice::disconnectLocked(sp<ACameraCaptureSession>& session) {
+ if (mClosing.exchange(true)) {
+ // Already closing, just return
+ ALOGW("Camera device %s is already closing.", getId());
+ return;
+ }
+
+ if (mRemote != nullptr) {
+ auto ret = mRemote->disconnect();
+ if (!ret.isOk()) {
+ ALOGE("%s: Transaction error while disconnecting device %s", __FUNCTION__,
+ ret.description().c_str());
+ }
+ }
+ mRemote = nullptr;
+
+ if (session != nullptr) {
+ session->closeByDevice();
+ }
+}
+
+camera_status_t
+CameraDevice::stopRepeatingLocked() {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+ return ret;
+ }
+ if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+ int repeatingSequenceId = mRepeatingSequenceId;
+ mRepeatingSequenceId = REQUEST_ID_NONE;
+
+ int64_t lastFrameNumber;
+ Status status = Status::NO_ERROR;
+ auto remoteRet = mRemote->cancelRepeatingRequest(
+ [&status, &lastFrameNumber](Status s, auto frameNumber) {
+ status = s;
+ lastFrameNumber = frameNumber;
+ });
+ if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+ ALOGE("%s: Unable to cancel active repeating request", __FUNCTION__);
+ return utils::convertFromHidl(status);
+ }
+ checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
+ }
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::flushLocked(ACameraCaptureSession* session) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera %s abort captures failed! ret %d", getId(), ret);
+ return ret;
+ }
+
+ // This should never happen because creating a new session will close
+ // previous one and thus reject any API call from previous session.
+ // But still good to check here in case something unexpected happen.
+ if (mCurrentSession != session) {
+ ALOGE("Camera %s session %p is not current active session!", getId(), session);
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+
+ if (mFlushing) {
+ ALOGW("Camera %s is already aborting captures", getId());
+ return ACAMERA_OK;
+ }
+
+ mFlushing = true;
+
+ // Send onActive callback to guarantee there is always active->ready transition
+ sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+ msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+ postSessionMsgAndCleanup(msg);
+
+ // If device is already idling, send callback and exit early
+ if (mIdle) {
+ sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+ msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onReady);
+ postSessionMsgAndCleanup(msg);
+ mFlushing = false;
+ return ACAMERA_OK;
+ }
+
+ int64_t lastFrameNumber;
+ Status status;
+ auto remoteRet = mRemote->flush([&status, &lastFrameNumber](auto s, auto frameNumber) {
+ status = s;
+ lastFrameNumber = frameNumber;
+ });
+ if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+ ALOGE("%s: Abort captures failed", __FUNCTION__);
+ return utils::convertFromHidl(status);
+ }
+ if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+ checkRepeatingSequenceCompleteLocked(mRepeatingSequenceId, lastFrameNumber);
+ }
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::waitUntilIdleLocked() {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Wait until camera %s idle failed! ret %d", getId(), ret);
+ return ret;
+ }
+
+ if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+ ALOGE("Camera device %s won't go to idle when there is repeating request!", getId());
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+
+ auto remoteRet = mRemote->waitUntilIdle();
+ if (!remoteRet.isOk()) {
+ ALOGE("%s: Transaction waitUntilIdle failed", __FUNCTION__);
+ return utils::convertFromHidl(remoteRet);
+ }
+ return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
+ const ACaptureRequest* sessionParameters) {
+ ACaptureSessionOutputContainer emptyOutput;
+ if (outputs == nullptr) {
+ outputs = &emptyOutput;
+ }
+
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> outputSet;
+ for (auto outConfig : outputs->mOutputs) {
+ native_handle_t* anw = outConfig.mWindow;
+ OutputConfigurationWrapper outConfigInsertW;
+ OutputConfiguration &outConfigInsert = outConfigInsertW.mOutputConfiguration;
+ outConfigInsert.rotation = utils::convertToHidl(outConfig.mRotation);
+ outConfigInsert.windowGroupId = -1;
+ outConfigInsert.windowHandles.resize(outConfig.mSharedWindows.size() + 1);
+ outConfigInsert.windowHandles[0] = anw;
+ native_handle_ptr_wrapper wrap(anw);
+ outputSet.insert(std::make_pair(anw, outConfigInsertW));
+ }
+ std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> addSet = outputSet;
+ std::vector<int32_t> deleteList;
+
+ // Determine which streams need to be created, which to be deleted
+ for (auto& kvPair : mConfiguredOutputs) {
+ int32_t streamId = kvPair.first;
+ auto& outputPair = kvPair.second;
+ if (outputSet.count(outputPair)) {
+ deleteList.push_back(streamId); // Need to delete a no longer needed stream
+ } else {
+ addSet.erase(outputPair); // No need to add already existing stream
+ }
+ }
+
+ ret = stopRepeatingLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera device %s stop repeating failed, ret %d", getId(), ret);
+ return ret;
+ }
+
+ ret = waitUntilIdleLocked();
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera device %s wait until idle failed, ret %d", getId(), ret);
+ return ret;
+ }
+
+ // Send onReady to previous session
+ // CurrentSession will be updated after configureStreamLocked, so here
+ // mCurrentSession is the session to be replaced by a new session
+ if (!mIdle && mCurrentSession != nullptr) {
+ if (mBusySession != mCurrentSession) {
+ ALOGE("Current session != busy session");
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ return ACAMERA_ERROR_CAMERA_DEVICE;
+ }
+ sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+ msg->setPointer(kContextKey, mBusySession->mUserSessionCallback.context);
+ msg->setObject(kSessionSpKey, mBusySession);
+ msg->setPointer(kCallbackFpKey, (void*) mBusySession->mUserSessionCallback.onReady);
+ mBusySession.clear();
+ postSessionMsgAndCleanup(msg);
+ }
+ mIdle = true;
+
+ auto remoteRet = mRemote->beginConfigure();
+ if (!remoteRet.isOk()|| remoteRet != Status::NO_ERROR) {
+ ALOGE("Camera device %s begin configure failed", getId());
+ return utils::convertFromHidl(remoteRet);
+ }
+
+ // delete to-be-deleted streams
+ for (auto streamId : deleteList) {
+ remoteRet = mRemote->deleteStream(streamId);
+ if (!remoteRet.isOk() || remoteRet != Status::NO_ERROR) {
+ ALOGE("Camera device %s failed to remove stream %d", getId(), streamId);
+ return utils::convertFromHidl(remoteRet);
+ }
+ mConfiguredOutputs.erase(streamId);
+ }
+
+ // add new streams
+ for (auto outputPair : addSet) {
+ int streamId;
+ Status status;
+ auto ret = mRemote->createStream(outputPair.second,
+ [&status, &streamId](Status s, auto stream_id) {
+ status = s;
+ streamId = stream_id;
+ });
+ if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+ ALOGE("Camera device %s failed to create stream", getId());
+ return utils::convertFromHidl(status);
+ }
+ mConfiguredOutputs.insert(std::make_pair(streamId, outputPair));
+ }
+
+ CameraMetadata params;
+ HCameraMetadata hidlParams;
+ if ((sessionParameters != nullptr) && (sessionParameters->settings != nullptr)) {
+ params.append(sessionParameters->settings->getInternalData());
+ const camera_metadata_t *params_metadata = params.getAndLock();
+ utils::convertToHidl(params_metadata, &hidlParams);
+ params.unlock(params_metadata);
+ remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams);
+ }
+ if (!remoteRet.isOk()) {
+ ALOGE("Transaction error: endConfigure failed %s", remoteRet.description().c_str());
+ }
+
+ return utils::convertFromHidl(remoteRet);
+}
+
+void
+CameraDevice::setRemoteDevice(sp<ICameraDeviceUser> remote) {
+ Mutex::Autolock _l(mDeviceLock);
+ mRemote = remote;
+}
+
+bool
+CameraDevice::setDeviceMetadataQueues() {
+ if (mRemote == nullptr) {
+ ALOGE("mRemote must not be null while trying to fetch metadata queues");
+ return false;
+ }
+ std::shared_ptr<RequestMetadataQueue> &reqQueue = mCaptureRequestMetadataQueue;
+ auto ret =
+ mRemote->getCaptureRequestMetadataQueue(
+ [&reqQueue](const auto &mqDescriptor) {
+ reqQueue = std::make_shared<RequestMetadataQueue>(mqDescriptor);
+ if (!reqQueue->isValid() || reqQueue->availableToWrite() <=0) {
+ ALOGE("Empty fmq from cameraserver");
+ reqQueue = nullptr;
+ }
+ });
+ if (!ret.isOk()) {
+ ALOGE("Transaction error trying to get capture request metadata queue");
+ return false;
+ }
+ std::shared_ptr<ResultMetadataQueue> &resQueue = mCaptureResultMetadataQueue;
+ ret =
+ mRemote->getCaptureResultMetadataQueue(
+ [&resQueue](const auto &mqDescriptor) {
+ resQueue = std::make_shared<ResultMetadataQueue>(mqDescriptor);
+ if (!resQueue->isValid() || resQueue->availableToWrite() <=0) {
+ ALOGE("Empty fmq from cameraserver");
+ }
+ });
+ if (!ret.isOk()) {
+ ALOGE("Transaction error trying to get capture result metadata queue");
+ return false;
+ }
+ return true;
+}
+
+camera_status_t
+CameraDevice::checkCameraClosedOrErrorLocked() const {
+ if (mRemote == nullptr) {
+ ALOGE("%s: camera device already closed", __FUNCTION__);
+ return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+ }
+ if (mInError) {// triggered by onDeviceError
+ ALOGE("%s: camera device has encountered a serious error", __FUNCTION__);
+ return mError;
+ }
+ return ACAMERA_OK;
+}
+
+void
+CameraDevice::setCameraDeviceErrorLocked(camera_status_t error) {
+ mInError = true;
+ mError = error;
+ return;
+}
+
+void
+CameraDevice::FrameNumberTracker::updateTracker(int64_t frameNumber, bool isError) {
+ ALOGV("updateTracker frame %" PRId64 " isError %d", frameNumber, isError);
+ if (isError) {
+ mFutureErrorSet.insert(frameNumber);
+ } else if (frameNumber <= mCompletedFrameNumber) {
+ ALOGE("Frame number %" PRId64 " decreased! current fn %" PRId64,
+ frameNumber, mCompletedFrameNumber);
+ return;
+ } else {
+ if (frameNumber != mCompletedFrameNumber + 1) {
+ ALOGE("Frame number out of order. Expect %" PRId64 " but get %" PRId64,
+ mCompletedFrameNumber + 1, frameNumber);
+ // Do not assert as in java implementation
+ }
+ mCompletedFrameNumber = frameNumber;
+ }
+ update();
+}
+
+void
+CameraDevice::FrameNumberTracker::update() {
+ for (auto it = mFutureErrorSet.begin(); it != mFutureErrorSet.end();) {
+ int64_t errorFrameNumber = *it;
+ if (errorFrameNumber == mCompletedFrameNumber + 1) {
+ mCompletedFrameNumber++;
+ it = mFutureErrorSet.erase(it);
+ } else if (errorFrameNumber <= mCompletedFrameNumber) {
+ // This should not happen, but deal with it anyway
+ ALOGE("Completd frame number passed through current frame number!");
+ // erase the old error since it's no longer useful
+ it = mFutureErrorSet.erase(it);
+ } else {
+ // Normal requests hasn't catched up error frames, just break
+ break;
+ }
+ }
+ ALOGV("Update complete frame %" PRId64, mCompletedFrameNumber);
+}
+
+void
+CameraDevice::onCaptureErrorLocked(
+ ErrorCode errorCode,
+ const CaptureResultExtras& resultExtras) {
+ int sequenceId = resultExtras.requestId;
+ int64_t frameNumber = resultExtras.frameNumber;
+ int32_t burstId = resultExtras.burstId;
+ auto it = mSequenceCallbackMap.find(sequenceId);
+ if (it == mSequenceCallbackMap.end()) {
+ ALOGE("%s: Error: capture sequence index %d not found!",
+ __FUNCTION__, sequenceId);
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ return;
+ }
+
+ CallbackHolder cbh = (*it).second;
+ sp<ACameraCaptureSession> session = cbh.mSession;
+ if ((size_t) burstId >= cbh.mRequests.size()) {
+ ALOGE("%s: Error: request index %d out of bound (size %zu)",
+ __FUNCTION__, burstId, cbh.mRequests.size());
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ return;
+ }
+ sp<CaptureRequest> request = cbh.mRequests[burstId];
+
+ // Handle buffer error
+ if (errorCode == ErrorCode::CAMERA_BUFFER) {
+ int32_t streamId = resultExtras.errorStreamId;
+ ACameraCaptureSession_captureCallback_bufferLost onBufferLost =
+ cbh.mCallbacks.onCaptureBufferLost;
+ auto outputPairIt = mConfiguredOutputs.find(streamId);
+ if (outputPairIt == mConfiguredOutputs.end()) {
+ ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId);
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ return;
+ }
+
+ const auto& windowHandles = outputPairIt->second.second.mOutputConfiguration.windowHandles;
+ for (const auto& outHandle : windowHandles) {
+ for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) {
+ int32_t windowId = streamAndWindowId.windowId;
+ if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) {
+ native_handle_t* anw =
+ const_cast<native_handle_t *>(windowHandles[windowId].getNativeHandle());
+ ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64,
+ getId(), anw, frameNumber);
+
+ sp<AMessage> msg = new AMessage(kWhatCaptureBufferLost, mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) onBufferLost);
+ msg->setObject(kCaptureRequestKey, request);
+ msg->setPointer(kAnwKey, (void*) anw);
+ msg->setInt64(kFrameNumberKey, frameNumber);
+ postSessionMsgAndCleanup(msg);
+ }
+ }
+ }
+ } else { // Handle other capture failures
+ // Fire capture failure callback if there is one registered
+ ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed;
+ sp<CameraCaptureFailure> failure(new CameraCaptureFailure());
+ failure->frameNumber = frameNumber;
+ // TODO: refine this when implementing flush
+ failure->reason = CAPTURE_FAILURE_REASON_ERROR;
+ failure->sequenceId = sequenceId;
+ failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT);
+
+ sp<AMessage> msg = new AMessage(kWhatCaptureFail, mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) onError);
+ msg->setObject(kCaptureRequestKey, request);
+ msg->setObject(kCaptureFailureKey, failure);
+ postSessionMsgAndCleanup(msg);
+
+ // Update tracker
+ mFrameNumberTracker.updateTracker(frameNumber, /*isError*/true);
+ checkAndFireSequenceCompleteLocked();
+ }
+ return;
+}
+
+void CameraDevice::CallbackHandler::onMessageReceived(
+ const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatOnDisconnected:
+ case kWhatOnError:
+ case kWhatSessionStateCb:
+ case kWhatCaptureStart:
+ case kWhatCaptureResult:
+ case kWhatCaptureFail:
+ case kWhatCaptureSeqEnd:
+ case kWhatCaptureSeqAbort:
+ case kWhatCaptureBufferLost:
+ ALOGV("%s: Received msg %d", __FUNCTION__, msg->what());
+ break;
+ case kWhatCleanUpSessions:
+ mCachedSessions.clear();
+ return;
+ default:
+ ALOGE("%s:Error: unknown device callback %d", __FUNCTION__, msg->what());
+ return;
+ }
+ // Check the common part of all message
+ void* context;
+ bool found = msg->findPointer(kContextKey, &context);
+ if (!found) {
+ ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+ return;
+ }
+ switch (msg->what()) {
+ case kWhatOnDisconnected:
+ {
+ ACameraDevice* dev;
+ found = msg->findPointer(kDeviceKey, (void**) &dev);
+ if (!found || dev == nullptr) {
+ ALOGE("%s: Cannot find device pointer!", __FUNCTION__);
+ return;
+ }
+ ACameraDevice_StateCallback onDisconnected;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onDisconnected);
+ if (!found) {
+ ALOGE("%s: Cannot find onDisconnected!", __FUNCTION__);
+ return;
+ }
+ if (onDisconnected == nullptr) {
+ return;
+ }
+ (*onDisconnected)(context, dev);
+ break;
+ }
+ case kWhatOnError:
+ {
+ ACameraDevice* dev;
+ found = msg->findPointer(kDeviceKey, (void**) &dev);
+ if (!found || dev == nullptr) {
+ ALOGE("%s: Cannot find device pointer!", __FUNCTION__);
+ return;
+ }
+ ACameraDevice_ErrorStateCallback onError;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onError);
+ if (!found) {
+ ALOGE("%s: Cannot find onError!", __FUNCTION__);
+ return;
+ }
+ int errorCode;
+ found = msg->findInt32(kErrorCodeKey, &errorCode);
+ if (!found) {
+ ALOGE("%s: Cannot find error code!", __FUNCTION__);
+ return;
+ }
+ if (onError == nullptr) {
+ return;
+ }
+ (*onError)(context, dev, errorCode);
+ break;
+ }
+ case kWhatSessionStateCb:
+ case kWhatCaptureStart:
+ case kWhatCaptureResult:
+ case kWhatCaptureFail:
+ case kWhatCaptureSeqEnd:
+ case kWhatCaptureSeqAbort:
+ case kWhatCaptureBufferLost:
+ {
+ sp<RefBase> obj;
+ found = msg->findObject(kSessionSpKey, &obj);
+ if (!found || obj == nullptr) {
+ ALOGE("%s: Cannot find session pointer!", __FUNCTION__);
+ return;
+ }
+ sp<ACameraCaptureSession> session(static_cast<ACameraCaptureSession*>(obj.get()));
+ mCachedSessions.push(session);
+ sp<CaptureRequest> requestSp = nullptr;
+ switch (msg->what()) {
+ case kWhatCaptureStart:
+ case kWhatCaptureResult:
+ case kWhatCaptureFail:
+ case kWhatCaptureBufferLost:
+ found = msg->findObject(kCaptureRequestKey, &obj);
+ if (!found) {
+ ALOGE("%s: Cannot find capture request!", __FUNCTION__);
+ return;
+ }
+ requestSp = static_cast<CaptureRequest*>(obj.get());
+ break;
+ }
+
+ switch (msg->what()) {
+ case kWhatSessionStateCb:
+ {
+ ACameraCaptureSession_stateCallback onState;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onState);
+ if (!found) {
+ ALOGE("%s: Cannot find state callback!", __FUNCTION__);
+ return;
+ }
+ if (onState == nullptr) {
+ return;
+ }
+ (*onState)(context, session.get());
+ break;
+ }
+ case kWhatCaptureStart:
+ {
+ ACameraCaptureSession_captureCallback_start onStart;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onStart);
+ if (!found) {
+ ALOGE("%s: Cannot find capture start callback!", __FUNCTION__);
+ return;
+ }
+ if (onStart == nullptr) {
+ return;
+ }
+ int64_t timestamp;
+ found = msg->findInt64(kTimeStampKey, ×tamp);
+ if (!found) {
+ ALOGE("%s: Cannot find timestamp!", __FUNCTION__);
+ return;
+ }
+ ACaptureRequest* request = allocateACaptureRequest(requestSp);
+ (*onStart)(context, session.get(), request, timestamp);
+ freeACaptureRequest(request);
+ break;
+ }
+ case kWhatCaptureResult:
+ {
+ ACameraCaptureSession_captureCallback_result onResult;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onResult);
+ if (!found) {
+ ALOGE("%s: Cannot find capture result callback!", __FUNCTION__);
+ return;
+ }
+ if (onResult == nullptr) {
+ return;
+ }
+
+ found = msg->findObject(kCaptureResultKey, &obj);
+ if (!found) {
+ ALOGE("%s: Cannot find capture result!", __FUNCTION__);
+ return;
+ }
+ sp<ACameraMetadata> result(static_cast<ACameraMetadata*>(obj.get()));
+ ACaptureRequest* request = allocateACaptureRequest(requestSp);
+ (*onResult)(context, session.get(), request, result.get());
+ freeACaptureRequest(request);
+ break;
+ }
+ case kWhatCaptureFail:
+ {
+ ACameraCaptureSession_captureCallback_failed onFail;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onFail);
+ if (!found) {
+ ALOGE("%s: Cannot find capture fail callback!", __FUNCTION__);
+ return;
+ }
+ if (onFail == nullptr) {
+ return;
+ }
+
+ found = msg->findObject(kCaptureFailureKey, &obj);
+ if (!found) {
+ ALOGE("%s: Cannot find capture failure!", __FUNCTION__);
+ return;
+ }
+ sp<CameraCaptureFailure> failureSp(
+ static_cast<CameraCaptureFailure*>(obj.get()));
+ ACameraCaptureFailure* failure =
+ static_cast<ACameraCaptureFailure*>(failureSp.get());
+ ACaptureRequest* request = allocateACaptureRequest(requestSp);
+ (*onFail)(context, session.get(), request, failure);
+ freeACaptureRequest(request);
+ break;
+ }
+ case kWhatCaptureSeqEnd:
+ {
+ ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onSeqEnd);
+ if (!found) {
+ ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__);
+ return;
+ }
+ if (onSeqEnd == nullptr) {
+ return;
+ }
+ int seqId;
+ found = msg->findInt32(kSequenceIdKey, &seqId);
+ if (!found) {
+ ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+ return;
+ }
+ int64_t frameNumber;
+ found = msg->findInt64(kFrameNumberKey, &frameNumber);
+ if (!found) {
+ ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+ return;
+ }
+ (*onSeqEnd)(context, session.get(), seqId, frameNumber);
+ break;
+ }
+ case kWhatCaptureSeqAbort:
+ {
+ ACameraCaptureSession_captureCallback_sequenceAbort onSeqAbort;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onSeqAbort);
+ if (!found) {
+ ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__);
+ return;
+ }
+ if (onSeqAbort == nullptr) {
+ return;
+ }
+ int seqId;
+ found = msg->findInt32(kSequenceIdKey, &seqId);
+ if (!found) {
+ ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+ return;
+ }
+ (*onSeqAbort)(context, session.get(), seqId);
+ break;
+ }
+ case kWhatCaptureBufferLost:
+ {
+ ACameraCaptureSession_captureCallback_bufferLost onBufferLost;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onBufferLost);
+ if (!found) {
+ ALOGE("%s: Cannot find buffer lost callback!", __FUNCTION__);
+ return;
+ }
+ if (onBufferLost == nullptr) {
+ return;
+ }
+
+ native_handle_t* anw;
+ found = msg->findPointer(kAnwKey, (void**) &anw);
+ if (!found) {
+ ALOGE("%s: Cannot find native_handle_t!", __FUNCTION__);
+ return;
+ }
+
+ int64_t frameNumber;
+ found = msg->findInt64(kFrameNumberKey, &frameNumber);
+ if (!found) {
+ ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+ return;
+ }
+
+ ACaptureRequest* request = allocateACaptureRequest(requestSp);
+ (*onBufferLost)(context, session.get(), request, anw, frameNumber);
+ freeACaptureRequest(request);
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+ sp<ACameraCaptureSession> session,
+ const Vector<sp<CaptureRequest> >& requests,
+ bool isRepeating,
+ ACameraCaptureSession_captureCallbacks* cbs) :
+ mSession(session), mRequests(requests),
+ mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {}
+
+void
+CameraDevice::checkRepeatingSequenceCompleteLocked(
+ const int sequenceId, const int64_t lastFrameNumber) {
+ ALOGV("Repeating seqId %d lastFrameNumer %" PRId64, sequenceId, lastFrameNumber);
+ if (lastFrameNumber == NO_FRAMES_CAPTURED) {
+ if (mSequenceCallbackMap.count(sequenceId) == 0) {
+ ALOGW("No callback found for sequenceId %d", sequenceId);
+ return;
+ }
+ // remove callback holder from callback map
+ auto cbIt = mSequenceCallbackMap.find(sequenceId);
+ CallbackHolder cbh = cbIt->second;
+ mSequenceCallbackMap.erase(cbIt);
+ // send seq aborted callback
+ sp<AMessage> msg = new AMessage(kWhatCaptureSeqAbort, mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, cbh.mSession);
+ msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted);
+ msg->setInt32(kSequenceIdKey, sequenceId);
+ postSessionMsgAndCleanup(msg);
+ } else {
+ // Use mSequenceLastFrameNumberMap to track
+ mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+
+ // Last frame might have arrived. Check now
+ checkAndFireSequenceCompleteLocked();
+ }
+}
+
+void
+CameraDevice::checkAndFireSequenceCompleteLocked() {
+ int64_t completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
+ auto it = mSequenceLastFrameNumberMap.begin();
+ while (it != mSequenceLastFrameNumberMap.end()) {
+ int sequenceId = it->first;
+ int64_t lastFrameNumber = it->second;
+ bool seqCompleted = false;
+ bool hasCallback = true;
+
+ if (mRemote == nullptr) {
+ ALOGW("Camera %s closed while checking sequence complete", getId());
+ return;
+ }
+
+ // Check if there is callback for this sequence
+ // This should not happen because we always register callback (with nullptr inside)
+ if (mSequenceCallbackMap.count(sequenceId) == 0) {
+ ALOGW("No callback found for sequenceId %d", sequenceId);
+ hasCallback = false;
+ }
+
+ if (lastFrameNumber <= completedFrameNumber) {
+ ALOGV("seq %d reached last frame %" PRId64 ", completed %" PRId64,
+ sequenceId, lastFrameNumber, completedFrameNumber);
+ seqCompleted = true;
+ }
+
+ if (seqCompleted && hasCallback) {
+ // remove callback holder from callback map
+ auto cbIt = mSequenceCallbackMap.find(sequenceId);
+ CallbackHolder cbh = cbIt->second;
+ mSequenceCallbackMap.erase(cbIt);
+ // send seq complete callback
+ sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, cbh.mSession);
+ msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted);
+ msg->setInt32(kSequenceIdKey, sequenceId);
+ msg->setInt64(kFrameNumberKey, lastFrameNumber);
+
+ // Clear the session sp before we send out the message
+ // This will guarantee the rare case where the message is processed
+ // before cbh goes out of scope and causing we call the session
+ // destructor while holding device lock
+ cbh.mSession.clear();
+ postSessionMsgAndCleanup(msg);
+ }
+
+ // No need to track sequence complete if there is no callback registered
+ if (seqCompleted || !hasCallback) {
+ it = mSequenceLastFrameNumberMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+/**
+ * Camera service callback implementation
+ */
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onDeviceError(
+ ErrorCode errorCode,
+ const CaptureResultExtras& resultExtras) {
+ ALOGD("Device error received, code %d, frame number %" PRId64 ", request ID %d, subseq ID %d",
+ errorCode, resultExtras.frameNumber, resultExtras.requestId, resultExtras.burstId);
+ auto ret = Void();
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+
+ sp<ACameraCaptureSession> session = dev->mCurrentSession.promote();
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->mRemote == nullptr) {
+ return ret; // device has been closed
+ }
+ switch (errorCode) {
+ case ErrorCode::CAMERA_DISCONNECTED:
+ {
+ // Camera is disconnected, close the session and expect no more callbacks
+ if (session != nullptr) {
+ session->closeByDevice();
+ }
+ dev->mCurrentSession = nullptr;
+ sp<AMessage> msg = new AMessage(kWhatOnDisconnected, dev->mHandler);
+ msg->setPointer(kContextKey, dev->mAppCallbacks.context);
+ msg->setPointer(kDeviceKey, (void*) dev->getWrapper());
+ msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onDisconnected);
+ msg->post();
+ break;
+ }
+ default:
+ ALOGE("Unknown error from camera device: %d", errorCode);
+ [[fallthrough]];
+ case ErrorCode::CAMERA_DEVICE:
+ case ErrorCode::CAMERA_SERVICE:
+ {
+ int32_t errorVal = ::ERROR_CAMERA_DEVICE;
+ // We keep this switch since this block might be encountered with
+ // more than just 2 states. The default fallthrough could have us
+ // handling more unmatched error cases.
+ switch (errorCode) {
+ case ErrorCode::CAMERA_DEVICE:
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ break;
+ case ErrorCode::CAMERA_SERVICE:
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ errorVal = ::ERROR_CAMERA_SERVICE;
+ break;
+ default:
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_UNKNOWN);
+ break;
+ }
+ sp<AMessage> msg = new AMessage(kWhatOnError, dev->mHandler);
+ msg->setPointer(kContextKey, dev->mAppCallbacks.context);
+ msg->setPointer(kDeviceKey, (void*) dev->getWrapper());
+ msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onError);
+ msg->setInt32(kErrorCodeKey, errorVal);
+ msg->post();
+ break;
+ }
+ case ErrorCode::CAMERA_REQUEST:
+ case ErrorCode::CAMERA_RESULT:
+ case ErrorCode::CAMERA_BUFFER:
+ dev->onCaptureErrorLocked(errorCode, resultExtras);
+ break;
+ }
+ return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onDeviceIdle() {
+ ALOGV("Camera is now idle");
+ auto ret = Void();
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->isClosed() || dev->mRemote == nullptr) {
+ return ret;
+ }
+
+ if (dev->mIdle) {
+ // Already in idle state. Possibly other thread did waitUntilIdle
+ return ret;
+ }
+
+ if (dev->mCurrentSession != nullptr) {
+ ALOGE("onDeviceIdle sending state cb");
+ if (dev->mBusySession != dev->mCurrentSession) {
+ ALOGE("Current session != busy session");
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+ return ret;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatSessionStateCb, dev->mHandler);
+ msg->setPointer(kContextKey, dev->mBusySession->mUserSessionCallback.context);
+ msg->setObject(kSessionSpKey, dev->mBusySession);
+ msg->setPointer(kCallbackFpKey, (void*) dev->mBusySession->mUserSessionCallback.onReady);
+ // Make sure we clear the sp first so the session destructor can
+ // only happen on handler thread (where we don't hold device/session lock)
+ dev->mBusySession.clear();
+ dev->postSessionMsgAndCleanup(msg);
+ }
+ dev->mIdle = true;
+ dev->mFlushing = false;
+ return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onCaptureStarted(
+ const CaptureResultExtras& resultExtras,
+ uint64_t timestamp) {
+ auto ret = Void();
+
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->isClosed() || dev->mRemote == nullptr) {
+ return ret;
+ }
+
+ int32_t sequenceId = resultExtras.requestId;
+ int32_t burstId = resultExtras.burstId;
+
+ auto it = dev->mSequenceCallbackMap.find(sequenceId);
+ if (it != dev->mSequenceCallbackMap.end()) {
+ CallbackHolder cbh = (*it).second;
+ ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted;
+ sp<ACameraCaptureSession> session = cbh.mSession;
+ if ((size_t) burstId >= cbh.mRequests.size()) {
+ ALOGE("%s: Error: request index %d out of bound (size %zu)",
+ __FUNCTION__, burstId, cbh.mRequests.size());
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ }
+ sp<CaptureRequest> request = cbh.mRequests[burstId];
+ sp<AMessage> msg = new AMessage(kWhatCaptureStart, dev->mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) onStart);
+ msg->setObject(kCaptureRequestKey, request);
+ msg->setInt64(kTimeStampKey, timestamp);
+ dev->postSessionMsgAndCleanup(msg);
+ }
+ return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onResultReceived(
+ const FmqSizeOrMetadata& resultMetadata,
+ const CaptureResultExtras& resultExtras,
+ const hidl_vec<PhysicalCaptureResultInfo>& physicalResultInfos) {
+ (void) physicalResultInfos;
+ auto ret = Void();
+
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+ int32_t sequenceId = resultExtras.requestId;
+ int64_t frameNumber = resultExtras.frameNumber;
+ int32_t burstId = resultExtras.burstId;
+ bool isPartialResult = (resultExtras.partialResultCount < dev->mPartialResultCount);
+
+ if (!isPartialResult) {
+ ALOGV("SeqId %d frame %" PRId64 " result arrive.", sequenceId, frameNumber);
+ }
+
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->mRemote == nullptr) {
+ return ret; // device has been disconnected
+ }
+
+ if (dev->isClosed()) {
+ if (!isPartialResult) {
+ dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false);
+ }
+ // early return to avoid callback sent to closed devices
+ return ret;
+ }
+
+ CameraMetadata metadataCopy;
+ HCameraMetadata hCameraMetadata;
+ bool converted = false;
+ if (resultMetadata.getDiscriminator() ==
+ FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+ hCameraMetadata.resize(resultMetadata.fmqMetadataSize());
+ bool read = dev->mCaptureResultMetadataQueue->read(hCameraMetadata.data(),
+ resultMetadata.fmqMetadataSize());
+ if (!read) {
+ ALOGE("%s capture request settings could't be read from fmq",
+ __FUNCTION__);
+ return ret;
+ }
+ // TODO: Do we actually need to clone here ?
+ converted = utils::convertFromHidlCloned(hCameraMetadata, &metadataCopy);
+
+ } else {
+ converted = utils::convertFromHidlCloned(resultMetadata.metadata(), &metadataCopy);
+ }
+
+ if (!converted) {
+ ALOGE("%s result metadata couldn't be converted", __FUNCTION__);
+ return ret;
+ }
+
+ metadataCopy.update(ANDROID_LENS_INFO_SHADING_MAP_SIZE, dev->mShadingMapSize, /*data_count*/2);
+ metadataCopy.update(ANDROID_SYNC_FRAME_NUMBER, &frameNumber, /*data_count*/1);
+
+ auto it = dev->mSequenceCallbackMap.find(sequenceId);
+ if (it != dev->mSequenceCallbackMap.end()) {
+ CallbackHolder cbh = (*it).second;
+ ACameraCaptureSession_captureCallback_result onResult = isPartialResult ?
+ cbh.mCallbacks.onCaptureProgressed :
+ cbh.mCallbacks.onCaptureCompleted;
+ sp<ACameraCaptureSession> session = cbh.mSession;
+ if ((size_t) burstId >= cbh.mRequests.size()) {
+ ALOGE("%s: Error: request index %d out of bound (size %zu)",
+ __FUNCTION__, burstId, cbh.mRequests.size());
+ dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ }
+ sp<CaptureRequest> request = cbh.mRequests[burstId];
+ sp<ACameraMetadata> result(new ACameraMetadata(
+ metadataCopy.release(), ACameraMetadata::ACM_RESULT));
+
+ sp<AMessage> msg = new AMessage(kWhatCaptureResult, dev->mHandler);
+ msg->setPointer(kContextKey, cbh.mCallbacks.context);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void*) onResult);
+ msg->setObject(kCaptureRequestKey, request);
+ msg->setObject(kCaptureResultKey, result);
+ dev->postSessionMsgAndCleanup(msg);
+ }
+
+ if (!isPartialResult) {
+ dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false);
+ dev->checkAndFireSequenceCompleteLocked();
+ }
+
+ return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onRepeatingRequestError(
+ uint64_t lastFrameNumber, int32_t stoppedSequenceId) {
+ auto ret = Void();
+
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+
+ Mutex::Autolock _l(dev->mDeviceLock);
+
+ int repeatingSequenceId = dev->mRepeatingSequenceId;
+ if (stoppedSequenceId == repeatingSequenceId) {
+ dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+ }
+
+ dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
+
+ return ret;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
new file mode 100644
index 0000000..01a219f
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -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
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
new file mode 100644
index 0000000..f395b44
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -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() {
+
+}
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h
new file mode 100644
index 0000000..c8d640f
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h
@@ -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
diff --git a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
new file mode 100644
index 0000000..ed67615
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.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;
+};
diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp
new file mode 100644
index 0000000..7193006
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/utils.cpp
@@ -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
diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h
new file mode 100644
index 0000000..2f1006d
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -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
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
new file mode 100644
index 0000000..b1e501b
--- /dev/null
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -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
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 7d3fbea..7803ccc 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -191,7 +191,7 @@
format->setFloat("frame-rate", displayFps);
format->setInt32("i-frame-interval", 10);
- sp<ALooper> looper = new ALooper;
+ sp<android::ALooper> looper = new android::ALooper;
looper->setName("screenrecord_looper");
looper->start();
ALOGV("Creating codec");
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 980d1d2..a463ec5 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -138,7 +138,7 @@
CHECK(!stateByTrack.isEmpty());
- int64_t startTimeUs = ALooper::GetNowUs();
+ int64_t startTimeUs = android::ALooper::GetNowUs();
int64_t startTimeRender = -1;
for (size_t i = 0; i < stateByTrack.size(); ++i) {
@@ -307,7 +307,7 @@
}
}
- int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
+ int64_t elapsedTimeUs = android::ALooper::GetNowUs() - startTimeUs;
for (size_t i = 0; i < stateByTrack.size(); ++i) {
CodecState *state = &stateByTrack.editValueAt(i);
@@ -400,7 +400,7 @@
ProcessState::self()->startThreadPool();
- sp<ALooper> looper = new ALooper;
+ sp<android::ALooper> looper = new android::ALooper;
looper->start();
sp<SurfaceComposerClient> composerClient;
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index 630de25..f0ee0e1 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -310,7 +310,7 @@
}
static int decode(
- const sp<ALooper> &looper,
+ const sp<android::ALooper> &looper,
const char *path,
const sp<Surface> &surface,
bool renderSurface,
@@ -465,7 +465,7 @@
filterState->mSignalledInputEOS = false;
filterState->mSawOutputEOS = false;
- int64_t startTimeUs = ALooper::GetNowUs();
+ int64_t startTimeUs = android::ALooper::GetNowUs();
int64_t startTimeRender = -1;
for (size_t i = 0; i < stateByTrack.size(); ++i) {
@@ -643,7 +643,7 @@
useTimestamp, &startTimeRender);
}
- int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
+ int64_t elapsedTimeUs = android::ALooper::GetNowUs() - startTimeUs;
for (size_t i = 0; i < stateByTrack.size(); ++i) {
CodecState *state = &stateByTrack.editValueAt(i);
@@ -737,7 +737,7 @@
ProcessState::self()->startThreadPool();
- android::sp<ALooper> looper = new ALooper;
+ android::sp<android::ALooper> looper = new android::ALooper;
looper->start();
android::sp<SurfaceComposerClient> composerClient;
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 0d331df..28a218c 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -716,7 +716,7 @@
gWriteMP4 = false;
gDisplayHistogram = false;
- sp<ALooper> looper;
+ sp<android::ALooper> looper;
int res;
while ((res = getopt(argc, argv, "vhaqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) {
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index eda7b61..4d0a4cc 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -8,12 +8,14 @@
],
shared_libs: [
+ "libbinder_ndk",
"liblog",
"libmediaextractor",
"libmediandk",
],
static_libs: [
+ "libaudioutils",
"libFLAC",
"libstagefright_foundation",
"libstagefright_metadatautils",
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index be6d9fd..d54aaef 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -24,6 +24,8 @@
// libFLAC parser
#include "FLAC/stream_decoder.h"
+#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
+#include <audio_utils/primitives.h>
#include <media/MediaExtractorPluginApi.h>
#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -35,9 +37,20 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MetaDataUtils.h>
#include <media/stagefright/MediaBufferBase.h>
+#include <private/android_filesystem_config.h> // for AID_MEDIA
+#include <system/audio.h>
namespace android {
+// MediaServer is capable of handling float extractor output, but general processes
+// may not be able to do so.
+// TODO: Improve API to set extractor float output.
+// (Note: duplicated with WAVExtractor.cpp)
+static inline bool shouldExtractorOutputFloat(int bitsPerSample)
+{
+ return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+}
+
class FLACParser;
class FLACSource : public MediaTrackHelperV2 {
@@ -45,7 +58,8 @@
public:
FLACSource(
DataSourceHelper *dataSource,
- AMediaFormat *meta);
+ AMediaFormat *meta,
+ bool outputFloat);
virtual media_status_t start();
virtual media_status_t stop();
@@ -60,6 +74,7 @@
private:
DataSourceHelper *mDataSource;
AMediaFormat *mTrackMetadata;
+ const bool mOutputFloat;
FLACParser *mParser;
bool mInitCheck;
bool mStarted;
@@ -76,11 +91,12 @@
public:
enum {
- kMaxChannels = 8,
+ kMaxChannels = FCC_8,
};
explicit FLACParser(
DataSourceHelper *dataSource,
+ bool outputFloat,
// If metadata pointers aren't provided, we don't fill them
AMediaFormat *fileMetadata = 0,
AMediaFormat *trackMetadata = 0);
@@ -120,6 +136,7 @@
private:
DataSourceHelper *mDataSource;
+ const bool mOutputFloat;
AMediaFormat *mFileMetadata;
AMediaFormat *mTrackMetadata;
bool mInitCheck;
@@ -170,6 +187,7 @@
const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
void metadataCallback(const FLAC__StreamMetadata *metadata);
void errorCallback(FLAC__StreamDecoderErrorStatus status);
+ size_t getOutputSampleSize() const { return mOutputFloat ? sizeof(float) : sizeof(int16_t); }
// FLAC parser callbacks as C-callable functions
static FLAC__StreamDecoderReadStatus read_callback(
@@ -381,122 +399,51 @@
mErrorStatus = status;
}
-// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved.
+// Copy samples from FLAC native 32-bit non-interleaved to 16-bit signed
+// or 32-bit float interleaved.
// These are candidates for optimization if needed.
-
-static void copyMono8(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
+static void copyTo16Signed(
+ short *dst,
+ const int *const *src,
unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i] << 8;
- }
-}
-
-static void copyStereo8(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
- unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i] << 8;
- *dst++ = src[1][i] << 8;
- }
-}
-
-static void copyMultiCh8(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
+ unsigned nChannels,
+ unsigned bitsPerSample) {
+ const unsigned leftShift = 16 - bitsPerSample;
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
- *dst++ = src[c][i] << 8;
+ *dst++ = src[c][i] << leftShift;
}
}
}
-static void copyMono16(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
+static void copyToFloat(
+ float *dst,
+ const int *const *src,
unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i];
- }
-}
-
-static void copyStereo16(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
- unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i];
- *dst++ = src[1][i];
- }
-}
-
-static void copyMultiCh16(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
+ unsigned nChannels,
+ unsigned bitsPerSample) {
+ const unsigned leftShift = 32 - bitsPerSample;
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
- *dst++ = src[c][i];
+ *dst++ = float_from_i32(src[c][i] << leftShift);
}
}
}
-// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
-
-static void copyMono24(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
- unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i] >> 8;
- }
-}
-
-static void copyStereo24(
- int16_t *dst,
- const int * src[FLACParser::kMaxChannels],
- unsigned nSamples,
- unsigned /* nChannels */) {
- for (unsigned i = 0; i < nSamples; ++i) {
- *dst++ = src[0][i] >> 8;
- *dst++ = src[1][i] >> 8;
- }
-}
-
-static void copyMultiCh24(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
- for (unsigned i = 0; i < nSamples; ++i) {
- for (unsigned c = 0; c < nChannels; ++c) {
- *dst++ = src[c][i] >> 8;
- }
- }
-}
-
-static void copyTrespass(
- int16_t * /* dst */,
- const int *[FLACParser::kMaxChannels] /* src */,
- unsigned /* nSamples */,
- unsigned /* nChannels */) {
- TRESPASS();
-}
-
// FLACParser
FLACParser::FLACParser(
DataSourceHelper *dataSource,
+ bool outputFloat,
AMediaFormat *fileMetadata,
AMediaFormat *trackMetadata)
: mDataSource(dataSource),
+ mOutputFloat(outputFloat),
mFileMetadata(fileMetadata),
mTrackMetadata(trackMetadata),
mInitCheck(false),
mMaxBufferSize(0),
mGroup(NULL),
- mCopy(copyTrespass),
mDecoder(NULL),
mCurrentPos(0LL),
mEOF(false),
@@ -590,29 +537,6 @@
ALOGE("unsupported sample rate %u", getSampleRate());
return NO_INIT;
}
- // configure the appropriate copy function, defaulting to trespass
- static const struct {
- unsigned mChannels;
- unsigned mBitsPerSample;
- void (*mCopy)(int16_t *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
- } table[] = {
- { 1, 8, copyMono8 },
- { 2, 8, copyStereo8 },
- { 8, 8, copyMultiCh8 },
- { 1, 16, copyMono16 },
- { 2, 16, copyStereo16 },
- { 8, 16, copyMultiCh16 },
- { 1, 24, copyMono24 },
- { 2, 24, copyStereo24 },
- { 8, 24, copyMultiCh24 },
- };
- for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) {
- if (table[i].mChannels >= getChannels() &&
- table[i].mBitsPerSample == getBitsPerSample()) {
- mCopy = table[i].mCopy;
- break;
- }
- }
// populate track metadata
if (mTrackMetadata != 0) {
AMediaFormat_setString(mTrackMetadata,
@@ -623,8 +547,6 @@
AMEDIAFORMAT_KEY_SAMPLE_RATE, getSampleRate());
AMediaFormat_setInt32(mTrackMetadata,
AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, getBitsPerSample());
- AMediaFormat_setInt32(mTrackMetadata,
- AMEDIAFORMAT_KEY_PCM_ENCODING, kAudioEncodingPcm16bit);
// sample rate is non-zero, so division by zero not possible
AMediaFormat_setInt64(mTrackMetadata,
AMEDIAFORMAT_KEY_DURATION, (getTotalSamples() * 1000000LL) / getSampleRate());
@@ -644,7 +566,7 @@
{
CHECK(mGroup == NULL);
mGroup = new MediaBufferGroup;
- mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(int16_t);
+ mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize();
mGroup->add_buffer(MediaBufferBase::Create(mMaxBufferSize));
}
@@ -697,12 +619,24 @@
if (err != OK) {
return NULL;
}
- size_t bufferSize = blocksize * getChannels() * sizeof(int16_t);
+ const size_t bufferSize = blocksize * getChannels() * getOutputSampleSize();
CHECK(bufferSize <= mMaxBufferSize);
- int16_t *data = (int16_t *) buffer->data();
buffer->set_range(0, bufferSize);
// copy PCM from FLAC write buffer to our media buffer, with interleaving
- (*mCopy)(data, mWriteBuffer, blocksize, getChannels());
+ const unsigned bitsPerSample = getBitsPerSample();
+ if (mOutputFloat) {
+ copyToFloat(reinterpret_cast<float*>(buffer->data()),
+ mWriteBuffer,
+ blocksize,
+ getChannels(),
+ bitsPerSample);
+ } else {
+ copyTo16Signed(reinterpret_cast<short*>(buffer->data()),
+ mWriteBuffer,
+ blocksize,
+ getChannels(),
+ bitsPerSample);
+ }
// fill in buffer metadata
CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number;
@@ -716,17 +650,16 @@
FLACSource::FLACSource(
DataSourceHelper *dataSource,
- AMediaFormat *trackMetadata)
+ AMediaFormat *trackMetadata,
+ bool outputFloat)
: mDataSource(dataSource),
mTrackMetadata(trackMetadata),
- mParser(0),
- mInitCheck(false),
+ mOutputFloat(outputFloat),
+ mParser(new FLACParser(mDataSource, outputFloat)),
+ mInitCheck(mParser->initCheck()),
mStarted(false)
{
ALOGV("FLACSource::FLACSource");
- // re-use the same track metadata passed into constructor from FLACExtractor
- mParser = new FLACParser(mDataSource);
- mInitCheck = mParser->initCheck();
}
FLACSource::~FLACSource()
@@ -762,7 +695,12 @@
media_status_t FLACSource::getFormat(AMediaFormat *meta)
{
- return AMediaFormat_copy(meta, mTrackMetadata);
+ const media_status_t status = AMediaFormat_copy(meta, mTrackMetadata);
+ if (status == OK) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+ mOutputFloat ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+ }
+ return status;
}
media_status_t FLACSource::read(
@@ -804,7 +742,7 @@
// FLACParser will fill in the metadata for us
mFileMetadata = AMediaFormat_new();
mTrackMetadata = AMediaFormat_new();
- mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata);
+ mParser = new FLACParser(mDataSource, false /* outputFloat */, mFileMetadata, mTrackMetadata);
mInitCheck = mParser->initCheck();
}
@@ -827,7 +765,9 @@
if (mInitCheck != OK || index > 0) {
return NULL;
}
- return new FLACSource(mDataSource, mTrackMetadata);
+
+ return new FLACSource(
+ mDataSource, mTrackMetadata, shouldExtractorOutputFloat(mParser->getBitsPerSample()));
}
media_status_t FLACExtractor::getTrackMetaData(
@@ -836,7 +776,13 @@
if (mInitCheck != OK || index > 0) {
return AMEDIA_ERROR_UNKNOWN;
}
- return AMediaFormat_copy(meta, mTrackMetadata);
+ const media_status_t status = AMediaFormat_copy(meta, mTrackMetadata);
+ if (status == OK) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+ shouldExtractorOutputFloat(mParser->getBitsPerSample())
+ ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+ }
+ return status;
}
media_status_t FLACExtractor::getMetaData(AMediaFormat *meta)
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index b362b4a..47f64d7 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -465,6 +465,7 @@
AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
+ // elst has to be processed only the first time this function is called
track->has_elst = false;
if (track->elst_segment_duration > INT64_MAX) {
@@ -472,27 +473,24 @@
}
int64_t segment_duration = track->elst_segment_duration;
int64_t media_time = track->elst_media_time;
- int64_t halfscale = mHeaderTimescale / 2;
+ int64_t halfscale = track->timescale / 2;
+
ALOGV("segment_duration = %" PRId64 ", media_time = %" PRId64
- ", halfscale = %" PRId64 ", timescale = %d",
- segment_duration,
- media_time,
- halfscale,
- mHeaderTimescale);
+ ", halfscale = %" PRId64 ", mdhd_timescale = %d, track_timescale = %u",
+ segment_duration, media_time,
+ halfscale, mHeaderTimescale, track->timescale);
+
+ if (samplerate != track->timescale){
+ ALOGV("samplerate:%d, track->timescale and samplerate are different!", samplerate);
+ }
int64_t delay;
- // delay = ((media_time * samplerate) + halfscale) / mHeaderTimescale;
+ // delay = ((media_time * samplerate) + halfscale) / track->timescale;
if (__builtin_mul_overflow(media_time, samplerate, &delay) ||
__builtin_add_overflow(delay, halfscale, &delay) ||
- (delay /= mHeaderTimescale, false) ||
- /* the calculated delay should be small, but some apps
- * appear to write a bogus edit list that causes a really
- * large delay, resulting in playback problems.
- * Ignore such edit lists.
- * (4096 is enough to drop 4 full samples)
- */
- delay > 4096 ||
- delay < 0) {
+ (delay /= track->timescale, false) ||
+ delay > INT32_MAX ||
+ delay < INT32_MIN) {
ALOGW("ignoring edit list with bogus values");
return;
}
@@ -508,33 +506,43 @@
int64_t segment_end;
int64_t padding;
- // padding = scaled_duration - ((segment_duration + media_time) * 1000000);
- if (__builtin_add_overflow(segment_duration, media_time, &segment_end) ||
- __builtin_mul_overflow(segment_end, 1000000, &segment_end) ||
- __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
+ int64_t segment_duration_e6;
+ int64_t media_time_scaled_e6;
+ int64_t media_time_scaled;
+ // padding = scaled_duration - ((segment_duration * 1000000) +
+ // ((media_time * mHeaderTimeScale * 1000000)/track->timescale) )
+ // segment_duration is based on timescale in movie header box(mdhd)
+ // media_time is based on timescale track header/media timescale
+ if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) ||
+ __builtin_mul_overflow(media_time, mHeaderTimescale, &media_time_scaled) ||
+ __builtin_mul_overflow(media_time_scaled, 1000000, &media_time_scaled_e6)) {
+ return;
+ }
+ media_time_scaled_e6 /= track->timescale;
+ if(__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end) ||
+ __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
return;
}
ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding);
-
+ int64_t paddingsamples = 0;
if (padding < 0) {
// track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is)
// might be slightly shorter than the segment duration, which would make the
// padding negative. Clamp to zero.
padding = 0;
- }
-
- int64_t paddingsamples;
- int64_t halfscale_e6;
- int64_t timescale_e6;
- // paddingsamples = ((padding * samplerate) + (halfscale * 1000000))
- // / (mHeaderTimescale * 1000000);
- if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
- __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) ||
- __builtin_mul_overflow(mHeaderTimescale, 1000000, ×cale_e6) ||
- __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
- (paddingsamples /= timescale_e6, false) ||
- paddingsamples > INT32_MAX) {
- return;
+ } else {
+ int64_t halfscale_e6;
+ int64_t timescale_e6;
+ // paddingsamples = ((padding * samplerate) + (halfscale * 1000000))
+ // / (mHeaderTimescale * 1000000);
+ if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
+ __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) ||
+ __builtin_mul_overflow(mHeaderTimescale, 1000000, ×cale_e6) ||
+ __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
+ (paddingsamples /= timescale_e6, false) ||
+ paddingsamples > INT32_MAX) {
+ return;
+ }
}
ALOGV("paddingsamples = %" PRId64, paddingsamples);
AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index d8b4144..185bb32 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -7,12 +7,14 @@
],
shared_libs: [
+ "libbinder_ndk",
"liblog",
"libmediaextractor",
"libmediandk",
],
static_libs: [
+ "libaudioutils",
"libfifo",
"libstagefright_foundation",
],
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index ddc7031..86500ef 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -20,6 +20,7 @@
#include "WAVExtractor.h"
+#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
#include <audio_utils/primitives.h>
#include <media/DataSourceBase.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -27,13 +28,26 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <private/android_filesystem_config.h> // for AID_MEDIA
+#include <system/audio.h>
#include <utils/String8.h>
#include <cutils/bitops.h>
#define CHANNEL_MASK_USE_CHANNEL_ORDER 0
+// NOTE: This code assumes the device processor is little endian.
+
namespace android {
+// MediaServer is capable of handling float extractor output, but general processes
+// may not be able to do so.
+// TODO: Improve API to set extractor float output.
+// (Note: duplicated with FLACExtractor.cpp)
+static inline bool shouldExtractorOutputFloat(int bitsPerSample)
+{
+ return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+}
+
enum {
WAVE_FORMAT_PCM = 0x0001,
WAVE_FORMAT_IEEE_FLOAT = 0x0003,
@@ -59,7 +73,7 @@
DataSourceHelper *dataSource,
AMediaFormat *meta,
uint16_t waveFormat,
- int32_t bitsPerSample,
+ bool outputFloat,
off64_t offset, size_t size);
virtual media_status_t start();
@@ -80,6 +94,7 @@
DataSourceHelper *mDataSource;
AMediaFormat *mMeta;
uint16_t mWaveFormat;
+ const bool mOutputFloat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -126,7 +141,7 @@
return new WAVSource(
mDataSource, mTrackMeta,
- mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
+ mWaveFormat, shouldExtractorOutputFloat(mBitsPerSample), mDataOffset, mDataSize);
}
media_status_t WAVExtractor::getTrackMetaData(
@@ -136,7 +151,13 @@
return AMEDIA_ERROR_UNKNOWN;
}
- return AMediaFormat_copy(meta, mTrackMeta);
+ const media_status_t status = AMediaFormat_copy(meta, mTrackMeta);
+ if (status == OK) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+ shouldExtractorOutputFloat(mBitsPerSample)
+ ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+ }
+ return status;
}
status_t WAVExtractor::init() {
@@ -199,13 +220,13 @@
mNumChannels = U16_LE_AT(&formatSpec[2]);
- if (mNumChannels < 1 || mNumChannels > 8) {
+ if (mNumChannels < 1 || mNumChannels > FCC_8) {
ALOGE("Unsupported number of channels (%d)", mNumChannels);
return AMEDIA_ERROR_UNSUPPORTED;
}
if (mWaveFormat != WAVE_FORMAT_EXTENSIBLE) {
- if (mNumChannels != 1 && mNumChannels != 2) {
+ if (mNumChannels != 1 && mNumChannels != FCC_2) {
ALOGW("More than 2 channels (%d) in non-WAVE_EXT, unknown channel mask",
mNumChannels);
}
@@ -312,9 +333,6 @@
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, mChannelMask);
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, mSampleRate);
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, mBitsPerSample);
- AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_PCM_ENCODING,
- kAudioEncodingPcm16bit);
-
int64_t durationUs = 0;
if (mWaveFormat == WAVE_FORMAT_MSGSM) {
// 65 bytes decode to 320 8kHz samples
@@ -353,22 +371,19 @@
DataSourceHelper *dataSource,
AMediaFormat *meta,
uint16_t waveFormat,
- int32_t bitsPerSample,
+ bool outputFloat,
off64_t offset, size_t size)
: mDataSource(dataSource),
mMeta(meta),
mWaveFormat(waveFormat),
- mSampleRate(0),
- mNumChannels(0),
- mBitsPerSample(bitsPerSample),
+ mOutputFloat(outputFloat),
mOffset(offset),
mSize(size),
mStarted(false),
mGroup(NULL) {
CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate));
CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels));
-
- AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, kMaxFrameSize);
+ CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample));
}
WAVSource::~WAVSource() {
@@ -385,11 +400,6 @@
// some WAV files may have large audio buffers that use shared memory transfer.
mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize);
- if (mBitsPerSample == 8) {
- // As a temporary buffer for 8->16 bit conversion.
- mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize));
- }
-
mCurrentPos = mOffset;
mStarted = true;
@@ -413,7 +423,13 @@
media_status_t WAVSource::getFormat(AMediaFormat *meta) {
ALOGV("WAVSource::getFormat");
- return AMediaFormat_copy(meta, mMeta);
+ const media_status_t status = AMediaFormat_copy(meta, mMeta);
+ if (status == OK) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, kMaxFrameSize);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+ mOutputFloat ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+ }
+ return status;
}
media_status_t WAVSource::read(
@@ -449,12 +465,16 @@
return AMEDIA_ERROR_UNKNOWN;
}
- // make sure that maxBytesToRead is multiple of 3, in 24-bit case
- size_t maxBytesToRead =
- mBitsPerSample == 8 ? kMaxFrameSize / 2 :
- (mBitsPerSample == 24 ? 3*(kMaxFrameSize/3): kMaxFrameSize);
+ // maxBytesToRead may be reduced so that in-place data conversion will fit in buffer size.
+ const size_t bufferSize = buffer->size();
+ size_t maxBytesToRead;
+ if (mOutputFloat) { // destination is float at 4 bytes per sample, source may be less.
+ maxBytesToRead = (mBitsPerSample / 8) * (bufferSize / 4);
+ } else { // destination is int16_t at 2 bytes per sample, only source of 8 bits is less.
+ maxBytesToRead = mBitsPerSample == 8 ? bufferSize / 2 : bufferSize;
+ }
- size_t maxBytesAvailable =
+ const size_t maxBytesAvailable =
(mCurrentPos - mOffset >= (off64_t)mSize)
? 0 : mSize - (mCurrentPos - mOffset);
@@ -490,38 +510,57 @@
// TODO: add capability to return data as float PCM instead of 16 bit PCM.
if (mWaveFormat == WAVE_FORMAT_PCM) {
- if (mBitsPerSample == 8) {
- // Convert 8-bit unsigned samples to 16-bit signed.
-
- // Create new buffer with 2 byte wide samples
- MediaBufferBase *tmp;
- CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
- tmp->set_range(0, 2 * n);
-
- memcpy_to_i16_from_u8((int16_t *)tmp->data(), (const uint8_t *)buffer->data(), n);
- buffer->release();
- buffer = tmp;
- } else if (mBitsPerSample == 24) {
- // Convert 24-bit signed samples to 16-bit signed in place
- const size_t numSamples = n / 3;
-
- memcpy_to_i16_from_p24((int16_t *)buffer->data(), (const uint8_t *)buffer->data(), numSamples);
- buffer->set_range(0, 2 * numSamples);
- } else if (mBitsPerSample == 32) {
- // Convert 32-bit signed samples to 16-bit signed in place
- const size_t numSamples = n / 4;
-
- memcpy_to_i16_from_i32((int16_t *)buffer->data(), (const int32_t *)buffer->data(), numSamples);
- buffer->set_range(0, 2 * numSamples);
+ if (mOutputFloat) {
+ float *fdest = (float *)buffer->data();
+ switch (mBitsPerSample) {
+ case 8: {
+ buffer->set_range(0, 4 * n);
+ memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), n);
+ } break;
+ case 16: {
+ const size_t numSamples = n / 2;
+ buffer->set_range(0, 4 * numSamples);
+ memcpy_to_float_from_i16(fdest, (const int16_t *)buffer->data(), numSamples);
+ } break;
+ case 24: {
+ const size_t numSamples = n / 3;
+ buffer->set_range(0, 4 * numSamples);
+ memcpy_to_float_from_p24(fdest, (const uint8_t *)buffer->data(), numSamples);
+ } break;
+ case 32: { // buffer range is correct
+ const size_t numSamples = n / 4;
+ memcpy_to_float_from_i32(fdest, (const int32_t *)buffer->data(), numSamples);
+ } break;
+ }
+ } else {
+ int16_t *idest = (int16_t *)buffer->data();
+ switch (mBitsPerSample) {
+ case 8: {
+ buffer->set_range(0, 2 * n);
+ memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), n);
+ } break;
+ case 16:
+ break; // no translation needed
+ case 24: {
+ const size_t numSamples = n / 3;
+ buffer->set_range(0, 2 * numSamples);
+ memcpy_to_i16_from_p24(idest, (const uint8_t *)buffer->data(), numSamples);
+ } break;
+ case 32: {
+ const size_t numSamples = n / 4;
+ buffer->set_range(0, 2 * numSamples);
+ memcpy_to_i16_from_i32(idest, (const int32_t *)buffer->data(), numSamples);
+ } break;
+ }
}
} else if (mWaveFormat == WAVE_FORMAT_IEEE_FLOAT) {
- if (mBitsPerSample == 32) {
- // Convert 32-bit float samples to 16-bit signed in place
+ if (!mOutputFloat) { // mBitsPerSample == 32
+ int16_t *idest = (int16_t *)buffer->data();
const size_t numSamples = n / 4;
-
- memcpy_to_i16_from_float((int16_t *)buffer->data(), (const float *)buffer->data(), numSamples);
+ memcpy_to_i16_from_float(idest, (const float *)buffer->data(), numSamples);
buffer->set_range(0, 2 * numSamples);
}
+ // Note: if output encoding is float, no need to convert if source is float.
}
int64_t timeStampUs = 0;
diff --git a/media/libaaudio/Android.bp b/media/libaaudio/Android.bp
index f00f7a8..4857008 100644
--- a/media/libaaudio/Android.bp
+++ b/media/libaaudio/Android.bp
@@ -32,6 +32,7 @@
cc_library_headers {
name: "libaaudio_headers",
export_include_dirs: ["include"],
+ version_script: "libaaudio.map.txt",
}
subdirs = ["*"]
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index 64deb60..9711b86 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -34,12 +34,123 @@
// Tag for machine readable results as property = value pairs
#define LOOPBACK_RESULT_TAG "RESULT: "
-#define LOOPBACK_SAMPLE_RATE 48000
-#define MILLIS_PER_SECOND 1000
+constexpr int32_t kDefaultSampleRate = 48000;
+constexpr int32_t kMillisPerSecond = 1000;
+constexpr int32_t kMinLatencyMillis = 4; // arbitrary and very low
+constexpr int32_t kMaxLatencyMillis = 400; // arbitrary and generous
+constexpr double kMaxEchoGain = 10.0; // based on experiments, otherwise too noisy
+constexpr double kMinimumConfidence = 0.5;
-#define MAX_ZEROTH_PARTIAL_BINS 40
-constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy
+static void printAudioScope(float sample) {
+ const int maxStars = 80; // arbitrary, fits on one line
+ char c = '*';
+ if (sample < -1.0) {
+ sample = -1.0;
+ c = '$';
+ } else if (sample > 1.0) {
+ sample = 1.0;
+ c = '$';
+ }
+ int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
+ for (int i = 0; i < numSpaces; i++) {
+ putchar(' ');
+ }
+ printf("%c\n", c);
+}
+
+/*
+
+FIR filter designed with
+http://t-filter.appspot.com
+
+sampling frequency: 48000 Hz
+
+* 0 Hz - 8000 Hz
+ gain = 1.2
+ desired ripple = 5 dB
+ actual ripple = 5.595266169703693 dB
+
+* 12000 Hz - 20000 Hz
+ gain = 0
+ desired attenuation = -40 dB
+ actual attenuation = -37.58691566571914 dB
+
+*/
+
+#define FILTER_TAP_NUM 11
+
+static const float sFilterTaps8000[FILTER_TAP_NUM] = {
+ -0.05944219353343189f,
+ -0.07303434839503208f,
+ -0.037690487672689066f,
+ 0.1870480506596512f,
+ 0.3910337357836833f,
+ 0.5333672385425637f,
+ 0.3910337357836833f,
+ 0.1870480506596512f,
+ -0.037690487672689066f,
+ -0.07303434839503208f,
+ -0.05944219353343189f
+};
+
+class LowPassFilter {
+public:
+
+ /*
+ * Filter one input sample.
+ * @return filtered output
+ */
+ float filter(float input) {
+ float output = 0.0f;
+ mX[mCursor] = input;
+ // Index backwards over x.
+ int xIndex = mCursor + FILTER_TAP_NUM;
+ // Write twice so we avoid having to wrap in the middle of the convolution.
+ mX[xIndex] = input;
+ for (int i = 0; i < FILTER_TAP_NUM; i++) {
+ output += sFilterTaps8000[i] * mX[xIndex--];
+ }
+ if (++mCursor >= FILTER_TAP_NUM) {
+ mCursor = 0;
+ }
+ return output;
+ }
+
+ /**
+ * @return true if PASSED
+ */
+ bool test() {
+ // Measure the impulse of the filter at different phases so we exercise
+ // all the wraparound cases in the FIR.
+ for (int offset = 0; offset < (FILTER_TAP_NUM * 2); offset++ ) {
+ // printf("LowPassFilter: cursor = %d\n", mCursor);
+ // Offset by one each time.
+ if (filter(0.0f) != 0.0f) {
+ printf("ERROR: filter should return 0.0 before impulse response\n");
+ return false;
+ }
+ for (int i = 0; i < FILTER_TAP_NUM; i++) {
+ float output = filter((i == 0) ? 1.0f : 0.0f); // impulse
+ if (output != sFilterTaps8000[i]) {
+ printf("ERROR: filter should return impulse response\n");
+ return false;
+ }
+ }
+ for (int i = 0; i < FILTER_TAP_NUM; i++) {
+ if (filter(0.0f) != 0.0f) {
+ printf("ERROR: filter should return 0.0 after impulse response\n");
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+private:
+ float mX[FILTER_TAP_NUM * 2]{}; // twice as big as needed to avoid wrapping
+ int32_t mCursor = 0;
+};
// A narrow impulse seems to have better immunity against over estimating the
// latency due to detecting subharmonics by the auto-correlator.
@@ -78,6 +189,12 @@
int64_t mSeed = 99887766;
};
+
+typedef struct LatencyReport_s {
+ double latencyInFrames;
+ double confidence;
+} LatencyReport;
+
static double calculateCorrelation(const float *a,
const float *b,
int windowSize)
@@ -101,127 +218,75 @@
return correlation;
}
-static int calculateCorrelations(const float *haystack, int haystackSize,
- const float *needle, int needleSize,
- float *results, int resultSize)
-{
- int maxCorrelations = haystackSize - needleSize;
- int numCorrelations = std::min(maxCorrelations, resultSize);
-
- for (int ic = 0; ic < numCorrelations; ic++) {
- double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
- results[ic] = correlation;
- }
-
- return numCorrelations;
-}
-
-/*==========================================================================================*/
-/**
- * Scan until we get a correlation of a single scan that goes over the tolerance level,
- * peaks then drops back down.
- */
-static double findFirstMatch(const float *haystack, int haystackSize,
- const float *needle, int needleSize, double threshold )
-{
- int ic;
- // How many correlations can we calculate?
- int numCorrelations = haystackSize - needleSize;
- double maxCorrelation = 0.0;
- int peakIndex = -1;
- double location = -1.0;
- const double backThresholdScaler = 0.5;
-
- for (ic = 0; ic < numCorrelations; ic++) {
- double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
-
- if( (correlation > maxCorrelation) ) {
- maxCorrelation = correlation;
- peakIndex = ic;
- }
-
- //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
- // ic, correlation, maxSum );
- // Are we past what we were looking for?
- if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) {
- location = peakIndex;
- break;
- }
- }
-
- return location;
-}
-
-typedef struct LatencyReport_s {
- double latencyInFrames;
- double confidence;
-} LatencyReport;
-
-// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
-// Using first echo instead of the original impulse for a better match.
-static int measureLatencyFromEchos(const float *haystack, int haystackSize,
- const float *needle, int needleSize,
- LatencyReport *report) {
- const double threshold = 0.1;
- // printf("%s: haystackSize = %d, needleSize = %d\n", __func__, haystackSize, needleSize);
-
- // Find first peak
- int first = (int) (findFirstMatch(haystack,
- haystackSize,
- needle,
- needleSize,
- threshold) + 0.5);
-
- // Use first echo as the needle for the other echos because
- // it will be more similar.
- needle = &haystack[first];
- int again = (int) (findFirstMatch(haystack,
- haystackSize,
- needle,
- needleSize,
- threshold) + 0.5);
- first = again;
-
+static int measureLatencyFromEchos(const float *data,
+ int32_t numFloats,
+ int32_t sampleRate,
+ LatencyReport *report) {
// Allocate results array
- int remaining = haystackSize - first;
- const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value
- int numCorrelations = std::min(remaining, maxReasonableLatencyFrames);
- float *correlations = new float[numCorrelations];
- float *harmonicSums = new float[numCorrelations](); // set to zero
+ const int minReasonableLatencyFrames = sampleRate * kMinLatencyMillis / kMillisPerSecond;
+ const int maxReasonableLatencyFrames = sampleRate * kMaxLatencyMillis / kMillisPerSecond;
+ int32_t maxCorrelationSize = maxReasonableLatencyFrames * 3;
+ int numCorrelations = std::min(numFloats, maxCorrelationSize);
+ float *correlations = new float[numCorrelations]{};
+ float *harmonicSums = new float[numCorrelations]{};
- // Generate correlation for every position.
- numCorrelations = calculateCorrelations(&haystack[first], remaining,
- needle, needleSize,
- correlations, numCorrelations);
+ // Perform sliding auto-correlation.
+ // Skip first frames to avoid huge peak at zero offset.
+ for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
+ int32_t remaining = numFloats - i;
+ float correlation = (float) calculateCorrelation(&data[i], data, remaining);
+ correlations[i] = correlation;
+ // printf("correlation[%d] = %f\n", ic, correlation);
+ }
- // Add higher harmonics mapped onto lower harmonics.
- // This reinforces the "fundamental" echo.
+ // Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
+ // Add higher harmonics mapped onto lower harmonics. This reinforces the "fundamental" echo.
const int numEchoes = 8;
for (int partial = 1; partial < numEchoes; partial++) {
- for (int i = 0; i < numCorrelations; i++) {
+ for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
harmonicSums[i / partial] += correlations[i] / partial;
}
}
// Find highest peak in correlation array.
float maxCorrelation = 0.0;
- float sumOfPeaks = 0.0;
int peakIndex = 0;
- const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
- for (int i = skip; i < numCorrelations; i++) {
+ for (int i = 0; i < numCorrelations; i++) {
if (harmonicSums[i] > maxCorrelation) {
maxCorrelation = harmonicSums[i];
- sumOfPeaks += maxCorrelation;
peakIndex = i;
// printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
}
}
-
report->latencyInFrames = peakIndex;
- if (sumOfPeaks < 0.0001) {
+/*
+ {
+ int32_t topPeak = peakIndex * 7 / 2;
+ for (int i = 0; i < topPeak; i++) {
+ float sample = harmonicSums[i];
+ printf("%4d: %7.5f ", i, sample);
+ printAudioScope(sample);
+ }
+ }
+*/
+
+ // Calculate confidence.
+ if (maxCorrelation < 0.001) {
report->confidence = 0.0;
} else {
- report->confidence = maxCorrelation / sumOfPeaks;
+ // Compare peak to average value around peak.
+ int32_t numSamples = std::min(numCorrelations, peakIndex * 2);
+ if (numSamples <= 0) {
+ report->confidence = 0.0;
+ } else {
+ double sum = 0.0;
+ for (int i = 0; i < numSamples; i++) {
+ sum += harmonicSums[i];
+ }
+ const double average = sum / numSamples;
+ const double ratio = average / maxCorrelation; // will be < 1.0
+ report->confidence = 1.0 - sqrt(ratio);
+ }
}
delete[] correlations;
@@ -317,7 +382,9 @@
}
assert(info.channels == 1);
+ assert(info.format == SF_FORMAT_FLOAT);
+ setSampleRate(info.samplerate);
allocate(info.frames);
mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
@@ -325,11 +392,49 @@
return mFrameCounter;
}
+ /**
+ * Square the samples so they are all positive and so the peaks are emphasized.
+ */
+ void square() {
+ for (int i = 0; i < mFrameCounter; i++) {
+ const float sample = mData[i];
+ mData[i] = sample * sample;
+ }
+ }
+
+ /**
+ * Low pass filter the recording using a simple FIR filter.
+ * Note that the lowpass filter cutoff tracks the sample rate.
+ * That is OK because the impulse width is a fixed number of samples.
+ */
+ void lowPassFilter() {
+ for (int i = 0; i < mFrameCounter; i++) {
+ mData[i] = mLowPassFilter.filter(mData[i]);
+ }
+ }
+
+ /**
+ * Remove DC offset using a one-pole one-zero IIR filter.
+ */
+ void dcBlocker() {
+ const float R = 0.996; // narrow notch at zero Hz
+ float x1 = 0.0;
+ float y1 = 0.0;
+ for (int i = 0; i < mFrameCounter; i++) {
+ const float x = mData[i];
+ const float y = x - x1 + (R * y1);
+ mData[i] = y;
+ y1 = y;
+ x1 = x;
+ }
+ }
+
private:
- float *mData = nullptr;
- int32_t mFrameCounter = 0;
- int32_t mMaxFrames = 0;
- int32_t mSampleRate = 48000; // common default
+ float *mData = nullptr;
+ int32_t mFrameCounter = 0;
+ int32_t mMaxFrames = 0;
+ int32_t mSampleRate = kDefaultSampleRate; // common default
+ LowPassFilter mLowPassFilter;
};
// ====================================================================================
@@ -349,8 +454,12 @@
virtual void printStatus() {};
- virtual int getResult() {
- return -1;
+ int32_t getResult() {
+ return mResult;
+ }
+
+ void setResult(int32_t result) {
+ mResult = result;
}
virtual bool isDone() {
@@ -379,7 +488,7 @@
static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
float peak = 0.0f;
for (int i = 0; i < numFrames; i++) {
- float pos = fabs(*inputData);
+ const float pos = fabs(*inputData);
if (pos > peak) {
peak = pos;
}
@@ -390,7 +499,8 @@
private:
- int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
+ int32_t mSampleRate = kDefaultSampleRate;
+ int32_t mResult = 0;
};
class PeakDetector {
@@ -409,24 +519,6 @@
float mPrevious = 0.0f;
};
-
-static void printAudioScope(float sample) {
- const int maxStars = 80; // arbitrary, fits on one line
- char c = '*';
- if (sample < -1.0) {
- sample = -1.0;
- c = '$';
- } else if (sample > 1.0) {
- sample = 1.0;
- c = '$';
- }
- int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
- for (int i = 0; i < numSpaces; i++) {
- putchar(' ');
- }
- printf("%c\n", c);
-}
-
// ====================================================================================
/**
* Measure latency given a loopback stream data.
@@ -447,17 +539,13 @@
}
void reset() override {
- mDownCounter = 200;
+ mDownCounter = getSampleRate() / 2;
mLoopCounter = 0;
mMeasuredLoopGain = 0.0f;
mEchoGain = 1.0f;
mState = STATE_INITIAL_SILENCE;
}
- virtual int getResult() {
- return mState == STATE_DONE ? 0 : -1;
- }
-
virtual bool isDone() {
return mState == STATE_DONE || mState == STATE_FAILED;
}
@@ -470,34 +558,57 @@
return mEchoGain;
}
- void report() override {
+ bool testLowPassFilter() {
+ LowPassFilter filter;
+ return filter.test();
+ }
+ void report() override {
printf("EchoAnalyzer ---------------\n");
+ if (getResult() != 0) {
+ printf(LOOPBACK_RESULT_TAG "result = %d\n", getResult());
+ return;
+ }
+
+ // printf("LowPassFilter test %s\n", testLowPassFilter() ? "PASSED" : "FAILED");
+
printf(LOOPBACK_RESULT_TAG "measured.gain = %8f\n", mMeasuredLoopGain);
printf(LOOPBACK_RESULT_TAG "echo.gain = %8f\n", mEchoGain);
printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
printf(LOOPBACK_RESULT_TAG "test.state.name = %8s\n", convertStateToText(mState));
+
if (mState == STATE_WAITING_FOR_SILENCE) {
printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
- }
- if (mMeasuredLoopGain >= 0.9999) {
+ setResult(ERROR_NOISY);
+ } else if (mMeasuredLoopGain >= 0.9999) {
printf(" ERROR - clipping, turn down volume slightly\n");
+ setResult(ERROR_CLIPPING);
+ } else if (mState != STATE_DONE && mState != STATE_GATHERING_ECHOS) {
+ printf("WARNING - Bad state. Check volume on device.\n");
+ setResult(ERROR_INVALID_STATE);
} else {
- const float *needle = s_Impulse;
- int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
- float *haystack = mAudioRecording.getData();
- int haystackSize = mAudioRecording.size();
- measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport);
- if (mLatencyReport.confidence < 0.01) {
- printf(" ERROR - confidence too low = %f\n", mLatencyReport.confidence);
- } else {
- double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
- printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n",
- mLatencyReport.latencyInFrames);
- printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n",
- latencyMillis);
- printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n",
- mLatencyReport.confidence);
+ // Cleanup the signal to improve the auto-correlation.
+ mAudioRecording.dcBlocker();
+ mAudioRecording.square();
+ mAudioRecording.lowPassFilter();
+
+ printf("Please wait several seconds for auto-correlation to complete.\n");
+ measureLatencyFromEchos(mAudioRecording.getData(),
+ mAudioRecording.size(),
+ getSampleRate(),
+ &mLatencyReport);
+
+ double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
+ / getSampleRate();
+ printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n",
+ mLatencyReport.latencyInFrames);
+ printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n",
+ latencyMillis);
+ printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n",
+ mLatencyReport.confidence);
+ if (mLatencyReport.confidence < kMinimumConfidence) {
+ printf(" ERROR - confidence too low!\n");
+ setResult(ERROR_CONFIDENCE);
}
}
}
@@ -523,6 +634,11 @@
sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
}
+ // @return number of frames for a typical block of processing
+ int32_t getBlockFrames() {
+ return getSampleRate() / 8;
+ }
+
void process(float *inputData, int inputChannelCount,
float *outputData, int outputChannelCount,
int numFrames) override {
@@ -540,10 +656,11 @@
for (int i = 0; i < numSamples; i++) {
outputData[i] = 0;
}
- if (mDownCounter-- <= 0) {
+ mDownCounter -= numFrames;
+ if (mDownCounter <= 0) {
nextState = STATE_MEASURING_GAIN;
//printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
- mDownCounter = 8;
+ mDownCounter = getBlockFrames() * 2;
}
break;
@@ -552,15 +669,16 @@
peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
// If we get several in a row then go to next state.
if (peak > mPulseThreshold) {
- if (mDownCounter-- <= 0) {
+ mDownCounter -= numFrames;
+ if (mDownCounter <= 0) {
//printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
// mLoopCounter, peak);
- mDownCounter = 8;
+ mDownCounter = getBlockFrames();
mMeasuredLoopGain = peak; // assumes original pulse amplitude is one
mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
// Calculate gain that will give us a nice decaying echo.
mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
- if (mEchoGain > MAX_ECHO_GAIN) {
+ if (mEchoGain > kMaxEchoGain) {
printf("ERROR - loop gain too low. Increase the volume.\n");
nextState = STATE_FAILED;
} else {
@@ -568,7 +686,7 @@
}
}
} else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
- mDownCounter = 8;
+ mDownCounter = getBlockFrames();
}
break;
@@ -581,13 +699,14 @@
peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
// If we get several in a row then go to next state.
if (peak < mSilenceThreshold) {
- if (mDownCounter-- <= 0) {
+ mDownCounter -= numFrames;
+ if (mDownCounter <= 0) {
nextState = STATE_SENDING_PULSE;
//printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
- mDownCounter = 8;
+ mDownCounter = getBlockFrames();
}
} else {
- mDownCounter = 8;
+ mDownCounter = getBlockFrames();
}
break;
@@ -620,11 +739,11 @@
}
if (numWritten < numFrames) {
nextState = STATE_DONE;
- //printf("%5d: switch to STATE_DONE\n", mLoopCounter);
}
break;
case STATE_DONE:
+ case STATE_FAILED:
default:
break;
}
@@ -638,11 +757,22 @@
}
int load(const char *fileName) override {
- return mAudioRecording.load(fileName);
+ int result = mAudioRecording.load(fileName);
+ setSampleRate(mAudioRecording.getSampleRate());
+ mState = STATE_DONE;
+ return result;
}
private:
+ enum error_code {
+ ERROR_OK = 0,
+ ERROR_NOISY = -99,
+ ERROR_CLIPPING,
+ ERROR_CONFIDENCE,
+ ERROR_INVALID_STATE
+ };
+
enum echo_state {
STATE_INITIAL_SILENCE,
STATE_MEASURING_GAIN,
@@ -708,10 +838,6 @@
class SineAnalyzer : public LoopbackProcessor {
public:
- virtual int getResult() {
- return mState == STATE_LOCKED ? 0 : -1;
- }
-
void report() override {
printf("SineAnalyzer ------------------\n");
printf(LOOPBACK_RESULT_TAG "peak.amplitude = %8f\n", mPeakAmplitude);
@@ -724,10 +850,9 @@
float signalToNoiseDB = 10.0 * log(signalToNoise);
printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
if (signalToNoiseDB < MIN_SNRATIO_DB) {
- printf("WARNING - signal to noise ratio is too low! < %d dB\n", MIN_SNRATIO_DB);
+ printf("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.\n", MIN_SNRATIO_DB);
+ setResult(ERROR_NOISY);
}
- printf(LOOPBACK_RESULT_TAG "phase.offset = %8.5f\n", mPhaseOffset);
- printf(LOOPBACK_RESULT_TAG "ref.phase = %8.5f\n", mPhase);
printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
printf(LOOPBACK_RESULT_TAG "sine.period = %8d\n", mSinePeriod);
printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
@@ -736,10 +861,15 @@
bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
if (!gotLock) {
printf("ERROR - failed to lock on reference sine tone\n");
+ setResult(ERROR_NO_LOCK);
} else {
// Only print if meaningful.
printf(LOOPBACK_RESULT_TAG "glitch.count = %8d\n", mGlitchCount);
printf(LOOPBACK_RESULT_TAG "max.glitch = %8f\n", mMaxGlitchDelta);
+ if (mGlitchCount > 0) {
+ printf("ERROR - number of glitches > 0\n");
+ setResult(ERROR_GLITCHES);
+ }
}
}
@@ -913,6 +1043,13 @@
private:
+ enum error_code {
+ OK,
+ ERROR_NO_LOCK = -80,
+ ERROR_GLITCHES,
+ ERROR_NOISY
+ };
+
enum sine_state_t {
STATE_IDLE,
STATE_MEASURE_NOISE,
@@ -963,8 +1100,6 @@
sine_state_t mState = STATE_IDLE;
};
-
-#undef LOOPBACK_SAMPLE_RATE
#undef LOOPBACK_RESULT_TAG
#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 124efd8..2a02b20 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -35,11 +35,14 @@
#include "AAudioExampleUtils.h"
#include "LoopbackAnalyzer.h"
+// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
+#define APP_VERSION "0.4.00"
+
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
#define FILENAME_ALL "/data/loopback_all.wav"
#define FILENAME_ECHOS "/data/loopback_echos.wav"
-#define APP_VERSION "0.3.00"
+#define FILENAME_PROCESSED "/data/loopback_processed.wav"
constexpr int kLogPeriodMillis = 1000;
constexpr int kNumInputChannels = 1;
@@ -173,7 +176,8 @@
int64_t inputFramesWritten = AAudioStream_getFramesWritten(myData->inputStream);
int64_t inputFramesRead = AAudioStream_getFramesRead(myData->inputStream);
int64_t framesAvailable = inputFramesWritten - inputFramesRead;
- actualFramesRead = readFormattedData(myData, numFrames);
+
+ actualFramesRead = readFormattedData(myData, numFrames); // READ
if (actualFramesRead < 0) {
result = AAUDIO_CALLBACK_RESULT_STOP;
} else {
@@ -193,6 +197,7 @@
}
myData->insufficientReadCount++;
myData->insufficientReadFrames += numFrames - actualFramesRead; // deficit
+ // printf("Error insufficientReadCount = %d\n",(int)myData->insufficientReadCount);
}
int32_t numSamples = actualFramesRead * myData->actualInputChannelCount;
@@ -355,6 +360,9 @@
printf("%s - Audio loopback using AAudio V" APP_VERSION "\n", argv[0]);
+ // Use LOW_LATENCY as the default to match input default.
+ argParser.setPerformanceMode(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (argParser.parseArg(arg)) {
@@ -403,7 +411,7 @@
}
int32_t requestedDuration = argParser.getDurationSeconds();
- int32_t requestedDurationMillis = requestedDuration * MILLIS_PER_SECOND;
+ int32_t requestedDurationMillis = requestedDuration * kMillisPerSecond;
int32_t timeMillis = 0;
int32_t recordingDuration = std::min(60 * 5, requestedDuration);
@@ -420,9 +428,11 @@
loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS);
- printf("main() read %d mono samples from %s on Android device\n", read, FILENAME_ECHOS);
+ printf("main() read %d mono samples from %s on Android device, rate = %d\n",
+ read, FILENAME_ECHOS,
+ loopbackData.loopbackProcessor->getSampleRate());
loopbackData.loopbackProcessor->report();
- return 0;
+ goto report_result;
}
break;
default:
@@ -580,45 +590,6 @@
printf("input error = %d = %s\n",
loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
- if (loopbackData.inputError == AAUDIO_OK) {
- if (testMode == TEST_SINE_MAGNITUDE) {
- printAudioGraph(loopbackData.audioRecording, 200);
- // Print again so we don't have to scroll past waveform.
- printf("OUTPUT Stream ----------------------------------------\n");
- argParser.compareWithStream(outputStream);
- printf("INPUT Stream ----------------------------------------\n");
- argParser.compareWithStream(inputStream);
- }
-
- loopbackData.loopbackProcessor->report();
- }
-
- {
- int32_t framesRead = AAudioStream_getFramesRead(inputStream);
- int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
- printf("Callback Results ---------------------------------------- INPUT\n");
- printf(" input overruns = %8d\n", AAudioStream_getXRunCount(inputStream));
- printf(" framesWritten = %8d\n", framesWritten);
- printf(" framesRead = %8d\n", framesRead);
- printf(" myFramesRead = %8d\n", (int) loopbackData.framesReadTotal);
- printf(" written - read = %8d\n", (int) (framesWritten - framesRead));
- printf(" insufficient # = %8d\n", (int) loopbackData.insufficientReadCount);
- if (loopbackData.insufficientReadCount > 0) {
- printf(" insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
- }
- }
- {
- int32_t framesRead = AAudioStream_getFramesRead(outputStream);
- int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
- printf("Callback Results ---------------------------------------- OUTPUT\n");
- printf(" output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
- printf(" myFramesWritten = %8d\n", (int) loopbackData.framesWrittenTotal);
- printf(" framesWritten = %8d\n", framesWritten);
- printf(" framesRead = %8d\n", framesRead);
- printf(" min numFrames = %8d\n", (int) loopbackData.minNumFrames);
- printf(" max numFrames = %8d\n", (int) loopbackData.maxNumFrames);
- }
-
written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
@@ -631,10 +602,46 @@
written, FILENAME_ALL);
}
- if (loopbackData.loopbackProcessor->getResult() < 0) {
- printf("ERROR: LOOPBACK PROCESSING FAILED. Maybe because the volume was too low.\n");
- result = loopbackData.loopbackProcessor->getResult();
+ if (loopbackData.inputError == AAUDIO_OK) {
+ if (testMode == TEST_SINE_MAGNITUDE) {
+ printAudioGraph(loopbackData.audioRecording, 200);
+ }
+
+ loopbackData.loopbackProcessor->report();
}
+
+ {
+ int32_t framesRead = AAudioStream_getFramesRead(inputStream);
+ int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
+ const int64_t framesAvailable = framesWritten - framesRead;
+ printf("Callback Results ---------------------------------------- INPUT\n");
+ printf(" input overruns = %8d\n", AAudioStream_getXRunCount(inputStream));
+ printf(" framesWritten = %8d\n", framesWritten);
+ printf(" framesRead = %8d\n", framesRead);
+ printf(" myFramesRead = %8d\n", (int) loopbackData.framesReadTotal);
+ printf(" written - read = %8d\n", (int) framesAvailable);
+ printf(" insufficient # = %8d\n", (int) loopbackData.insufficientReadCount);
+ if (loopbackData.insufficientReadCount > 0) {
+ printf(" insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
+ }
+ int32_t actualInputCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
+ if (framesAvailable > 2 * actualInputCapacity) {
+ printf(" WARNING: written - read > 2*capacity !\n");
+ }
+ }
+
+ {
+ int32_t framesRead = AAudioStream_getFramesRead(outputStream);
+ int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
+ printf("Callback Results ---------------------------------------- OUTPUT\n");
+ printf(" output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
+ printf(" myFramesWritten = %8d\n", (int) loopbackData.framesWrittenTotal);
+ printf(" framesWritten = %8d\n", framesWritten);
+ printf(" framesRead = %8d\n", framesRead);
+ printf(" min numFrames = %8d\n", (int) loopbackData.minNumFrames);
+ printf(" max numFrames = %8d\n", (int) loopbackData.maxNumFrames);
+ }
+
if (loopbackData.insufficientReadCount > 3) {
printf("ERROR: LOOPBACK PROCESSING FAILED. insufficientReadCount too high\n");
result = AAUDIO_ERROR_UNAVAILABLE;
@@ -646,13 +653,23 @@
delete[] loopbackData.inputFloatData;
delete[] loopbackData.inputShortData;
+report_result:
+ written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED);
+ if (written > 0) {
+ printf("main() wrote %8d processed samples to \"%s\" on Android device\n",
+ written, FILENAME_PROCESSED);
+ }
+
+ if (loopbackData.loopbackProcessor->getResult() < 0) {
+ result = loopbackData.loopbackProcessor->getResult();
+ }
printf(RESULT_TAG "result = %d \n", result); // machine readable
printf("result is %s\n", AAudio_convertResultToText(result)); // human readable
if (result != AAUDIO_OK) {
- printf("FAILURE\n");
+ printf("TEST FAILED\n");
return EXIT_FAILURE;
} else {
- printf("SUCCESS\n");
+ printf("TEST PASSED\n");
return EXIT_SUCCESS;
}
}
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index ece9e6a..a5dc55f 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -130,10 +130,12 @@
}
int32_t getBufferCapacity() const {
+ printf("%s() returns %d\n", __func__, mBufferCapacity);
return mBufferCapacity;
}
void setBufferCapacity(int32_t frames) {
+ printf("%s(%d)\n", __func__, frames);
mBufferCapacity = frames;
}
@@ -197,7 +199,7 @@
* @param builder
*/
void applyParameters(AAudioStreamBuilder *builder) const {
- AAudioStreamBuilder_setBufferCapacityInFrames(builder, mBufferCapacity);
+ AAudioStreamBuilder_setBufferCapacityInFrames(builder, getBufferCapacity());
AAudioStreamBuilder_setChannelCount(builder, mChannelCount);
AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
AAudioStreamBuilder_setFormat(builder, mFormat);
@@ -281,7 +283,7 @@
if (strlen(arg) > 2) {
policy = atoi(&arg[2]);
}
- if (!AAudio_setMMapPolicy(policy)) {
+ if (AAudio_setMMapPolicy(policy) != AAUDIO_OK) {
printf("ERROR: invalid MMAP policy mode %i\n", policy);
}
} break;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 8b35a85..96fccae 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -810,6 +810,13 @@
if (t != 0) {
if (!isOffloaded_l()) {
t->pause();
+ } else if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) {
+ const sp<AudioTrackThread> t = mAudioTrackThread;
+ if (t != 0) {
+ // causes wake up of the playback thread, that will callback the client for
+ // EVENT_STREAM_END in processAudioBuffer()
+ t->wake();
+ }
}
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
diff --git a/media/libaudioprocessing/AudioResamplerSinc.cpp b/media/libaudioprocessing/AudioResamplerSinc.cpp
index 320b8cf..5a03a0d 100644
--- a/media/libaudioprocessing/AudioResamplerSinc.cpp
+++ b/media/libaudioprocessing/AudioResamplerSinc.cpp
@@ -19,6 +19,7 @@
#define __STDC_CONSTANT_MACROS
#include <malloc.h>
+#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 135c9b6..d3de01e 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -478,7 +478,7 @@
// caller responsible for the returned string
bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
Prop *prop = findProp(name);
- if (prop == NULL || prop->mType != kTypeDouble) {
+ if (prop == NULL || prop->mType != kTypeCString) {
return false;
}
if (value != NULL) {
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index cb139c9..d42bb1e 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -34,7 +34,7 @@
callback_t cbf, // Offload
void* user, // Offload
size_t frameCount, // bufferSizeInBytes
- audio_session_t sessionId, // AudioTrack
+ int32_t sessionId, // AudioTrack
const jobject attributes, // AudioAttributes
float maxRequiredSpeed) { // bufferSizeInBytes
@@ -496,11 +496,11 @@
return env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
}
-audio_session_t JAudioTrack::getAudioSessionId() {
+int32_t JAudioTrack::getAudioSessionId() {
JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
- return (audio_session_t) sessionId;
+ return sessionId;
}
status_t JAudioTrack::setPreferredDevice(jobject device) {
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
index 7457d84..98a3e75 100644
--- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -59,7 +59,7 @@
return NO_ERROR;
}
-MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
+MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(int32_t sessionId, uid_t uid, int pid,
const jobject attributes)
: mCallback(nullptr),
mCallbackCookie(nullptr),
@@ -623,14 +623,14 @@
data->unlock();
}
-audio_session_t MediaPlayer2AudioOutput::getSessionId() const {
+int32_t MediaPlayer2AudioOutput::getSessionId() const {
Mutex::Autolock lock(mLock);
return mSessionId;
}
-void MediaPlayer2AudioOutput::setSessionId(const audio_session_t id) {
+void MediaPlayer2AudioOutput::setSessionId(const int32_t sessionId) {
Mutex::Autolock lock(mLock);
- mSessionId = id;
+ mSessionId = sessionId;
}
uint32_t MediaPlayer2AudioOutput::getSampleRate() const {
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 13cf85a..8ea70ef 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -114,7 +114,7 @@
callback_t cbf,
void* user,
size_t frameCount = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
+ int32_t sessionId = AUDIO_SESSION_ALLOCATE,
const jobject pAttributes = NULL,
float maxRequiredSpeed = 1.0f);
@@ -341,7 +341,7 @@
jobject getRoutedDevice();
/* Returns the ID of the audio session this AudioTrack belongs to. */
- audio_session_t getAudioSessionId();
+ int32_t getAudioSessionId();
/* Sets the preferred audio device to use for output of this AudioTrack.
*
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
index fc020ca..bda4f61 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -38,7 +38,7 @@
class CallbackData;
public:
- MediaPlayer2AudioOutput(audio_session_t sessionId,
+ MediaPlayer2AudioOutput(int32_t sessionId,
uid_t uid,
int pid,
const jobject attributes);
@@ -57,8 +57,8 @@
virtual status_t getTimestamp(AudioTimestamp &ts) const;
virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
- virtual audio_session_t getSessionId() const;
- virtual void setSessionId(const audio_session_t id);
+ virtual int32_t getSessionId() const;
+ virtual void setSessionId(const int32_t id);
virtual uint32_t getSampleRate() const;
virtual int64_t getBufferDurationInUs() const;
@@ -117,7 +117,7 @@
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
size_t mFrameSize;
- audio_session_t mSessionId;
+ int32_t mSessionId;
uid_t mUid;
int mPid;
float mSendLevel;
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 07a7946..4b19e38 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -94,7 +94,7 @@
virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
- virtual audio_session_t getSessionId() const = 0;
+ virtual int32_t getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
virtual uint32_t getSampleRate() const = 0;
virtual int64_t getBufferDurationInUs() const = 0;
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index d881813..4bc1a4a 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -55,7 +55,7 @@
public:
~MediaPlayer2();
- static sp<MediaPlayer2> Create();
+ static sp<MediaPlayer2> Create(int32_t sessionId);
static status_t DumpAll(int fd, const Vector<String16>& args);
void disconnect();
@@ -96,8 +96,8 @@
void notify(int64_t srcId, int msg, int ext1, int ext2,
const PlayerMessage *obj = NULL);
status_t invoke(const PlayerMessage &request, PlayerMessage *reply);
- status_t setAudioSessionId(audio_session_t sessionId);
- audio_session_t getAudioSessionId();
+ status_t setAudioSessionId(int32_t sessionId);
+ int32_t getAudioSessionId();
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
status_t setAudioAttributes(const jobject attributes);
@@ -116,7 +116,7 @@
status_t dump(int fd, const Vector<String16>& args);
private:
- MediaPlayer2();
+ MediaPlayer2(int32_t sessionId);
bool init();
// Disconnect from the currently connected ANativeWindow.
@@ -150,7 +150,7 @@
float mVolume;
int mVideoWidth;
int mVideoHeight;
- audio_session_t mAudioSessionId;
+ int32_t mAudioSessionId;
sp<JObjectHolder> mAudioAttributes;
float mSendLevel;
sp<ANativeWindowWrapper> mConnectedWindow;
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 04a6f68..921a5b7 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -218,8 +218,8 @@
} // anonymous namespace
//static
-sp<MediaPlayer2> MediaPlayer2::Create() {
- sp<MediaPlayer2> player = new MediaPlayer2();
+sp<MediaPlayer2> MediaPlayer2::Create(int32_t sessionId) {
+ sp<MediaPlayer2> player = new MediaPlayer2(sessionId);
if (!player->init()) {
return NULL;
@@ -236,7 +236,7 @@
return dumpPlayers(fd, args);
}
-MediaPlayer2::MediaPlayer2() {
+MediaPlayer2::MediaPlayer2(int32_t sessionId) {
ALOGV("constructor");
mSrcId = 0;
mLockThreadId = 0;
@@ -251,20 +251,17 @@
mLoop = false;
mVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
- mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
- AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
// TODO: get pid and uid from JAVA
mPid = IPCThreadState::self()->getCallingPid();
mUid = IPCThreadState::self()->getCallingUid();
- mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid, mPid, NULL /*attributes*/);
+ mAudioOutput = new MediaPlayer2AudioOutput(sessionId, mUid, mPid, NULL /*attributes*/);
}
MediaPlayer2::~MediaPlayer2() {
ALOGV("destructor");
- AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
removePlayer(this);
}
@@ -891,7 +888,7 @@
return OK;
}
-status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId) {
+status_t MediaPlayer2::setAudioSessionId(int32_t sessionId) {
ALOGV("MediaPlayer2::setAudioSessionId(%d)", sessionId);
Mutex::Autolock _l(mLock);
if (!(mCurrentState & MEDIA_PLAYER2_IDLE)) {
@@ -901,20 +898,18 @@
if (sessionId < 0) {
return BAD_VALUE;
}
- if (sessionId != mAudioSessionId) {
- AudioSystem::acquireAudioSessionId(sessionId, -1);
- AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
- mAudioSessionId = sessionId;
- }
- if (mAudioOutput != NULL && mAudioSessionId != mAudioOutput->getSessionId()) {
+ if (mAudioOutput != NULL && sessionId != mAudioOutput->getSessionId()) {
mAudioOutput->setSessionId(sessionId);
}
return NO_ERROR;
}
-audio_session_t MediaPlayer2::getAudioSessionId() {
+int32_t MediaPlayer2::getAudioSessionId() {
Mutex::Autolock _l(mLock);
- return mAudioSessionId;
+ if (mAudioOutput != NULL) {
+ return mAudioOutput->getSessionId();
+ }
+ return 0;
}
status_t MediaPlayer2::setAuxEffectSendLevel(float level) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 57a0198..b258332 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/MediaClock.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -87,6 +89,20 @@
// static
const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
+static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) {
+ switch (pcmEncoding) {
+ case kAudioEncodingPcmFloat:
+ return AUDIO_FORMAT_PCM_FLOAT;
+ case kAudioEncodingPcm16bit:
+ return AUDIO_FORMAT_PCM_16_BIT;
+ case kAudioEncodingPcm8bit:
+ return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
+ default:
+ ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
+ return AUDIO_FORMAT_INVALID;
+ }
+}
+
NuPlayer::Renderer::Renderer(
const sp<MediaPlayerBase::AudioSink> &sink,
const sp<MediaClock> &mediaClock,
@@ -1877,8 +1893,13 @@
int32_t sampleRate;
CHECK(format->findInt32("sample-rate", &sampleRate));
+ // read pcm encoding from MediaCodec output format, if available
+ int32_t pcmEncoding;
+ audio_format_t audioFormat =
+ format->findInt32(KEY_PCM_ENCODING, &pcmEncoding) ?
+ audioFormatFromEncoding(pcmEncoding) : AUDIO_FORMAT_PCM_16_BIT;
+
if (offloadingAudio()) {
- audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT;
AString mime;
CHECK(format->findString("mime", &mime));
status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str());
@@ -1980,7 +2001,7 @@
const PcmInfo info = {
(audio_channel_mask_t)channelMask,
(audio_output_flags_t)pcmFlags,
- AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat
+ audioFormat,
numChannels,
sampleRate
};
@@ -2019,7 +2040,7 @@
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
- AUDIO_FORMAT_PCM_16_BIT,
+ audioFormat,
0 /* bufferCount - unused */,
mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,
mUseAudioCallback ? this : NULL,
@@ -2077,4 +2098,3 @@
}
} // namespace android
-
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 17507cd..3dae8f7 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -34,7 +34,7 @@
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer2;
import android.media.MediaPlayer2.EventCallback;
-import android.media.MediaPlayer2Impl;
+import android.media.MediaPlayer2;
import android.media.PlaybackParams;
import android.media.SessionToken2;
import android.media.SubtitleData;
@@ -719,7 +719,7 @@
final Context context = mInstance.getContext();
Log.d(TAG, "openVideo(): creating new MediaPlayer2 instance.");
- mMediaPlayer = new MediaPlayer2Impl(context);
+ mMediaPlayer = new MediaPlayer2(context);
mSurfaceView.setMediaPlayer(mMediaPlayer);
mTextureView.setMediaPlayer(mMediaPlayer);
mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 5555acf..f833cf7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3185,7 +3185,6 @@
if (mType == OFFLOAD || mType == DIRECT) {
mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
}
- audio_utils::Statistics<double> downstreamLatencyStatMs(0.999 /* alpha */);
audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
while (!exitPending())
@@ -3215,7 +3214,7 @@
downstreamPatchHandle = swPatches[0].getPatchHandle();
}
if (downstreamPatchHandle != lastDownstreamPatchHandle) {
- downstreamLatencyStatMs.reset();
+ mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = downstreamPatchHandle;
}
if (status == OK) {
@@ -3229,14 +3228,14 @@
if (latencyMs < minLatency) latencyMs = minLatency;
else if (latencyMs > maxLatency) latencyMs = maxLatency;
}
- downstreamLatencyStatMs.add(latencyMs);
+ mDownstreamLatencyStatMs.add(latencyMs);
}
mAudioFlinger->mLock.unlock();
}
} else {
if (lastDownstreamPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
// our device is no longer AUDIO_DEVICE_OUT_BUS, reset patch handle and stats.
- downstreamLatencyStatMs.reset();
+ mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
}
}
@@ -3285,10 +3284,10 @@
(long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
// Note: Downstream latency only added if timestamp correction enabled.
- if (downstreamLatencyStatMs.getN() > 0) { // we have latency info.
+ if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
const int64_t newPosition =
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- - int64_t(downstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+ - int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
// prevent retrograde
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
newPosition,
@@ -3747,6 +3746,15 @@
uint64_t position64;
if (mOutput->getPresentationPosition(&position64, ×tamp.mTime) == OK) {
timestamp.mPosition = (uint32_t)position64;
+ if (mDownstreamLatencyStatMs.getN() > 0) {
+ const uint32_t positionOffset =
+ (uint32_t)(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+ if (positionOffset > timestamp.mPosition) {
+ timestamp.mPosition = 0;
+ } else {
+ timestamp.mPosition -= positionOffset;
+ }
+ }
return NO_ERROR;
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 61f7baf..49fc234 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1082,6 +1082,8 @@
static const size_t kFastMixerLogSize = 8 * 1024;
sp<NBLog::Writer> mFastMixerNBLogWriter;
+ // Downstream patch latency, available if mDownstreamLatencyStatMs.getN() > 0.
+ audio_utils::Statistics<double> mDownstreamLatencyStatMs{0.999};
public:
virtual bool hasFastMixer() const = 0;