Merge remote-tracking branch 'goog/stage-aosp-master' into HEAD
diff --git a/camera/Android.bp b/camera/Android.bp
index 7ff3e89..c76ae50 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -21,7 +21,6 @@
export_aidl_headers: true,
local_include_dirs: ["aidl"],
include_dirs: [
- "frameworks/base/core/java",
"frameworks/native/aidl/gui",
],
},
@@ -32,9 +31,11 @@
// include libcamera_client, at the path "aidl/package/path/BnFoo.h"
"aidl/android/hardware/ICameraService.aidl",
"aidl/android/hardware/ICameraServiceListener.aidl",
+ "aidl/android/hardware/ICameraServiceProxy.aidl",
"aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
"aidl/android/hardware/camera2/ICameraDeviceUser.aidl",
+
// Source for camera interface parcelables, and manually-written interfaces
"Camera.cpp",
"CameraMetadata.cpp",
@@ -43,7 +44,6 @@
"CameraParameters2.cpp",
"ICamera.cpp",
"ICameraClient.cpp",
- "ICameraServiceProxy.cpp",
"ICameraRecordingProxy.cpp",
"ICameraRecordingProxyListener.cpp",
"camera2/CaptureRequest.cpp",
@@ -61,13 +61,17 @@
"libbinder",
"libgui",
"libcamera_metadata",
+ "libnativewindow",
],
include_dirs: [
"system/media/private/camera/include",
"frameworks/native/include/media/openmax",
],
- export_include_dirs: ["include/camera"],
+ export_include_dirs: [
+ "include",
+ "include/camera"
+ ],
export_shared_lib_headers: ["libcamera_metadata"],
cflags: [
@@ -75,4 +79,5 @@
"-Wall",
"-Wextra",
],
+
}
diff --git a/camera/ICameraServiceProxy.cpp b/camera/ICameraServiceProxy.cpp
deleted file mode 100644
index a9d0836..0000000
--- a/camera/ICameraServiceProxy.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 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 "BpCameraServiceProxy"
-
-#include <stdint.h>
-
-#include <binder/Parcel.h>
-
-#include <camera/ICameraServiceProxy.h>
-
-namespace android {
-
-class BpCameraServiceProxy: public BpInterface<ICameraServiceProxy> {
-public:
- explicit BpCameraServiceProxy(const sp<IBinder>& impl)
- : BpInterface<ICameraServiceProxy>(impl) {}
-
- virtual void pingForUserUpdate() {
- Parcel data;
- data.writeInterfaceToken(ICameraServiceProxy::getInterfaceDescriptor());
- remote()->transact(BnCameraServiceProxy::PING_FOR_USER_UPDATE, data, nullptr,
- IBinder::FLAG_ONEWAY);
- }
-
- virtual void notifyCameraState(String16 cameraId, CameraState newCameraState) {
- Parcel data;
- data.writeInterfaceToken(ICameraServiceProxy::getInterfaceDescriptor());
- data.writeString16(cameraId);
- data.writeInt32(newCameraState);
- remote()->transact(BnCameraServiceProxy::NOTIFY_CAMERA_STATE, data, nullptr,
- IBinder::FLAG_ONEWAY);
- }
-
-};
-
-
-IMPLEMENT_META_INTERFACE(CameraServiceProxy, "android.hardware.ICameraServiceProxy");
-
-status_t BnCameraServiceProxy::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch(code) {
- case PING_FOR_USER_UPDATE: {
- CHECK_INTERFACE(ICameraServiceProxy, data, reply);
- pingForUserUpdate();
- return NO_ERROR;
- } break;
- case NOTIFY_CAMERA_STATE: {
- CHECK_INTERFACE(ICameraServiceProxy, data, reply);
- String16 cameraId = data.readString16();
- CameraState newCameraState =
- static_cast<CameraState>(data.readInt32());
- notifyCameraState(cameraId, newCameraState);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-}; // namespace android
diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
index 0e654d5..5dc23eb 100644
--- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
@@ -31,7 +31,23 @@
oneway void pingForUserUpdate();
/**
- * Update the status of a camera device
+ * Values for notifyCameraState newCameraState
*/
- oneway void notifyCameraState(String cameraId, int newCameraState);
+ const int CAMERA_STATE_OPEN = 0;
+ const int CAMERA_STATE_ACTIVE = 1;
+ const int CAMERA_STATE_IDLE = 2;
+ const int CAMERA_STATE_CLOSED = 3;
+
+ /**
+ * Values for notifyCameraState facing
+ */
+ const int CAMERA_FACING_BACK = 0;
+ const int CAMERA_FACING_FRONT = 1;
+ const int CAMERA_FACING_EXTERNAL = 2;
+
+ /**
+ * Update the status of a camera device.
+ */
+ oneway void notifyCameraState(String cameraId, int facing, int newCameraState,
+ String clientName);
}
diff --git a/camera/include/camera/ICameraServiceProxy.h b/camera/include/camera/ICameraServiceProxy.h
deleted file mode 100644
index 2613c01..0000000
--- a/camera/include/camera/ICameraServiceProxy.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 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 ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
-#define ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/**
- * Interface from native camera service to managed-side camera service proxy.
- *
- * Keep in sync with frameworks/base/core/java/android/hardware/ICameraServiceProxy.aidl
- *
- */
-class ICameraServiceProxy : public IInterface {
-public:
- enum {
- PING_FOR_USER_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
- NOTIFY_CAMERA_STATE
- };
-
- enum CameraState {
- CAMERA_STATE_OPEN,
- CAMERA_STATE_ACTIVE,
- CAMERA_STATE_IDLE,
- CAMERA_STATE_CLOSED
- };
-
- DECLARE_META_INTERFACE(CameraServiceProxy);
-
- virtual void pingForUserUpdate() = 0;
- virtual void notifyCameraState(String16 cameraId, CameraState newCameraState) = 0;
-};
-
-class BnCameraServiceProxy: public BnInterface<ICameraServiceProxy>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-
-
-}; // namespace android
-
-#endif // ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 8b76cdf..629d75a 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -115,11 +115,13 @@
* <p>The mode control selects how the image data is converted from the
* sensor's native color into linear sRGB color.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_color_correction_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When auto-white balance (AWB) is enabled with ACAMERA_CONTROL_AWB_MODE, this
* control is overridden by the AWB routine. When AWB is disabled, the
@@ -164,17 +166,19 @@
* @see ACAMERA_COLOR_CORRECTION_TRANSFORM
* @see ACAMERA_CONTROL_AWB_MODE
*/
- ACAMERA_COLOR_CORRECTION_MODE = // byte (enum)
+ ACAMERA_COLOR_CORRECTION_MODE = // byte (acamera_metadata_enum_android_color_correction_mode_t)
ACAMERA_COLOR_CORRECTION_START,
/**
* <p>A color transform matrix to use to transform
* from sensor RGB color space to output linear sRGB color space.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is either set by the camera device when the request
* ACAMERA_COLOR_CORRECTION_MODE is not TRANSFORM_MATRIX, or
@@ -196,11 +200,13 @@
* <p>Gains applying to Bayer raw color channels for
* white-balance.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>These per-channel gains are either set by the camera device
* when the request ACAMERA_COLOR_CORRECTION_MODE is not
@@ -221,11 +227,13 @@
/**
* <p>Mode of operation for the chromatic aberration correction algorithm.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_color_correction_aberration_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Chromatic (color) aberration is caused by the fact that different wavelengths of light
* can not focus on the same point after exiting from the lens. This metadata defines
@@ -239,7 +247,7 @@
* applying aberration correction.</p>
* <p>LEGACY devices will always be in FAST mode.</p>
*/
- ACAMERA_COLOR_CORRECTION_ABERRATION_MODE = // byte (enum)
+ ACAMERA_COLOR_CORRECTION_ABERRATION_MODE = // byte (acamera_metadata_enum_android_color_correction_aberration_mode_t)
ACAMERA_COLOR_CORRECTION_START + 3,
/**
* <p>List of aberration correction modes for ACAMERA_COLOR_CORRECTION_ABERRATION_MODE that are
@@ -247,10 +255,12 @@
*
* @see ACAMERA_COLOR_CORRECTION_ABERRATION_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This key lists the valid modes for ACAMERA_COLOR_CORRECTION_ABERRATION_MODE. If no
* aberration correction modes are available for a device, this list will solely include
@@ -269,11 +279,13 @@
* <p>The desired setting for the camera device's auto-exposure
* algorithm's antibanding compensation.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_antibanding_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Some kinds of lighting fixtures, such as some fluorescent
* lights, flicker at the rate of the power supply frequency
@@ -310,17 +322,19 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_STATISTICS_SCENE_FLICKER
*/
- ACAMERA_CONTROL_AE_ANTIBANDING_MODE = // byte (enum)
+ ACAMERA_CONTROL_AE_ANTIBANDING_MODE = // byte (acamera_metadata_enum_android_control_ae_antibanding_mode_t)
ACAMERA_CONTROL_START,
/**
* <p>Adjustment to auto-exposure (AE) target image
* brightness.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The adjustment is measured as a count of steps, with the
* step size defined by ACAMERA_CONTROL_AE_COMPENSATION_STEP and the
@@ -350,11 +364,13 @@
* <p>Whether auto-exposure (AE) is currently locked to its latest
* calculated values.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_lock_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When set to <code>true</code> (ON), the AE algorithm is locked to its latest parameters,
* and will not change exposure settings until the lock is set to <code>false</code> (OFF).</p>
@@ -398,17 +414,19 @@
* @see ACAMERA_SENSOR_EXPOSURE_TIME
* @see ACAMERA_SENSOR_SENSITIVITY
*/
- ACAMERA_CONTROL_AE_LOCK = // byte (enum)
+ ACAMERA_CONTROL_AE_LOCK = // byte (acamera_metadata_enum_android_control_ae_lock_t)
ACAMERA_CONTROL_START + 2,
/**
* <p>The desired mode for the camera device's
* auto-exposure routine.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control is only effective if ACAMERA_CONTROL_MODE is
* AUTO.</p>
@@ -436,16 +454,18 @@
* @see ACAMERA_SENSOR_FRAME_DURATION
* @see ACAMERA_SENSOR_SENSITIVITY
*/
- ACAMERA_CONTROL_AE_MODE = // byte (enum)
+ ACAMERA_CONTROL_AE_MODE = // byte (acamera_metadata_enum_android_control_ae_mode_t)
ACAMERA_CONTROL_START + 3,
/**
* <p>List of metering areas to use for auto-exposure adjustment.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[5*area_count]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Not available if android.control.maxRegionsAe is 0.
* Otherwise will always be present.</p>
@@ -486,11 +506,13 @@
* adjust the capture frame rate to maintain good
* exposure.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of ACAMERA_SENSOR_EXPOSURE_TIME and
@@ -505,11 +527,13 @@
* <p>Whether the camera device will trigger a precapture
* metering sequence when it processes this request.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_precapture_trigger_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This entry is normally set to IDLE, or is not
* included at all in the request settings. When included and
@@ -563,17 +587,19 @@
* @see ACAMERA_CONTROL_AF_TRIGGER
* @see ACAMERA_CONTROL_CAPTURE_INTENT
*/
- ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER = // byte (enum)
+ ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER = // byte (acamera_metadata_enum_android_control_ae_precapture_trigger_t)
ACAMERA_CONTROL_START + 6,
/**
* <p>Whether auto-focus (AF) is currently enabled, and what
* mode it is set to.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_af_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Only effective if ACAMERA_CONTROL_MODE = AUTO and the lens is not fixed focus
* (i.e. <code>ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE > 0</code>). Also note that
@@ -590,16 +616,18 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE
*/
- ACAMERA_CONTROL_AF_MODE = // byte (enum)
+ ACAMERA_CONTROL_AF_MODE = // byte (acamera_metadata_enum_android_control_af_mode_t)
ACAMERA_CONTROL_START + 7,
/**
* <p>List of metering areas to use for auto-focus.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[5*area_count]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Not available if android.control.maxRegionsAf is 0.
* Otherwise will always be present.</p>
@@ -638,11 +666,13 @@
/**
* <p>Whether the camera device will trigger autofocus for this request.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_af_trigger_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This entry is normally set to IDLE, or is not
* included at all in the request settings.</p>
@@ -665,17 +695,19 @@
* @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER
* @see ACAMERA_CONTROL_AF_STATE
*/
- ACAMERA_CONTROL_AF_TRIGGER = // byte (enum)
+ ACAMERA_CONTROL_AF_TRIGGER = // byte (acamera_metadata_enum_android_control_af_trigger_t)
ACAMERA_CONTROL_START + 9,
/**
* <p>Whether auto-white balance (AWB) is currently locked to its
* latest calculated values.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_awb_lock_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When set to <code>true</code> (ON), the AWB algorithm is locked to its latest parameters,
* and will not change color balance settings until the lock is set to <code>false</code> (OFF).</p>
@@ -699,18 +731,20 @@
*
* @see ACAMERA_CONTROL_AWB_MODE
*/
- ACAMERA_CONTROL_AWB_LOCK = // byte (enum)
+ ACAMERA_CONTROL_AWB_LOCK = // byte (acamera_metadata_enum_android_control_awb_lock_t)
ACAMERA_CONTROL_START + 10,
/**
* <p>Whether auto-white balance (AWB) is currently setting the color
* transform fields, and what its illumination target
* is.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_awb_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control is only effective if ACAMERA_CONTROL_MODE is AUTO.</p>
* <p>When set to the ON mode, the camera device's auto-white balance
@@ -739,17 +773,19 @@
* @see ACAMERA_CONTROL_AWB_LOCK
* @see ACAMERA_CONTROL_MODE
*/
- ACAMERA_CONTROL_AWB_MODE = // byte (enum)
+ ACAMERA_CONTROL_AWB_MODE = // byte (acamera_metadata_enum_android_control_awb_mode_t)
ACAMERA_CONTROL_START + 11,
/**
* <p>List of metering areas to use for auto-white-balance illuminant
* estimation.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[5*area_count]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Not available if android.control.maxRegionsAwb is 0.
* Otherwise will always be present.</p>
@@ -791,11 +827,13 @@
* of this capture, to help the camera device to decide optimal 3A
* strategy.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_capture_intent_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control (except for MANUAL) is only effective if
* <code>ACAMERA_CONTROL_MODE != OFF</code> and any 3A routine is active.</p>
@@ -807,16 +845,18 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
*/
- ACAMERA_CONTROL_CAPTURE_INTENT = // byte (enum)
+ ACAMERA_CONTROL_CAPTURE_INTENT = // byte (acamera_metadata_enum_android_control_capture_intent_t)
ACAMERA_CONTROL_START + 13,
/**
* <p>A special color effect to apply.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_effect_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When this mode is set, a color effect will be applied
* to images produced by the camera device. The interpretation
@@ -825,17 +865,19 @@
* depended on to be consistent (or present) across all
* devices.</p>
*/
- ACAMERA_CONTROL_EFFECT_MODE = // byte (enum)
+ ACAMERA_CONTROL_EFFECT_MODE = // byte (acamera_metadata_enum_android_control_effect_mode_t)
ACAMERA_CONTROL_START + 14,
/**
* <p>Overall mode of 3A (auto-exposure, auto-white-balance, auto-focus) control
* routines.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This is a top-level 3A control switch. When set to OFF, all 3A control
* by the camera device is disabled. The application must set the fields for
@@ -856,16 +898,18 @@
*
* @see ACAMERA_CONTROL_AF_MODE
*/
- ACAMERA_CONTROL_MODE = // byte (enum)
+ ACAMERA_CONTROL_MODE = // byte (acamera_metadata_enum_android_control_mode_t)
ACAMERA_CONTROL_START + 15,
/**
* <p>Control for which scene mode is currently active.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_scene_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Scene modes are custom camera modes optimized for a certain set of conditions and
* capture settings.</p>
@@ -883,17 +927,19 @@
* @see ACAMERA_CONTROL_AWB_MODE
* @see ACAMERA_CONTROL_MODE
*/
- ACAMERA_CONTROL_SCENE_MODE = // byte (enum)
+ ACAMERA_CONTROL_SCENE_MODE = // byte (acamera_metadata_enum_android_control_scene_mode_t)
ACAMERA_CONTROL_START + 16,
/**
* <p>Whether video stabilization is
* active.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_video_stabilization_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Video stabilization automatically warps images from
* the camera in order to stabilize motion between consecutive frames.</p>
@@ -923,7 +969,7 @@
* @see ACAMERA_LENS_OPTICAL_STABILIZATION_MODE
* @see ACAMERA_SCALER_CROP_REGION
*/
- ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE = // byte (enum)
+ ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE = // byte (acamera_metadata_enum_android_control_video_stabilization_mode_t)
ACAMERA_CONTROL_START + 17,
/**
* <p>List of auto-exposure antibanding modes for ACAMERA_CONTROL_AE_ANTIBANDING_MODE that are
@@ -931,10 +977,12 @@
*
* @see ACAMERA_CONTROL_AE_ANTIBANDING_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Not all of the auto-exposure anti-banding modes may be
* supported by a given camera device. This field lists the
@@ -952,10 +1000,12 @@
*
* @see ACAMERA_CONTROL_AE_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Not all the auto-exposure modes may be supported by a
* given camera device, especially if no flash unit is
@@ -980,10 +1030,12 @@
*
* @see ACAMERA_CONTROL_AE_TARGET_FPS_RANGE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>For devices at the LEGACY level or above:</p>
* <ul>
@@ -1025,12 +1077,13 @@
* @see ACAMERA_CONTROL_AE_COMPENSATION_STEP
* @see ACAMERA_CONTROL_AE_EXPOSURE_COMPENSATION
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_CONTROL_AE_COMPENSATION_RANGE = // int32[2]
ACAMERA_CONTROL_START + 21,
@@ -1038,10 +1091,12 @@
* <p>Smallest step by which the exposure compensation
* can be changed.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This is the unit for ACAMERA_CONTROL_AE_EXPOSURE_COMPENSATION. For example, if this key has
* a value of <code>1/2</code>, then a setting of <code>-2</code> for ACAMERA_CONTROL_AE_EXPOSURE_COMPENSATION means
@@ -1059,10 +1114,12 @@
*
* @see ACAMERA_CONTROL_AF_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Not all the auto-focus modes may be supported by a
* given camera device. This entry lists the valid modes for
@@ -1086,10 +1143,12 @@
*
* @see ACAMERA_CONTROL_EFFECT_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This list contains the color effect modes that can be applied to
* images produced by the camera device.
@@ -1111,10 +1170,12 @@
*
* @see ACAMERA_CONTROL_SCENE_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This list contains scene modes that can be set for the camera device.
* Only scene modes that have been fully implemented for the
@@ -1136,10 +1197,12 @@
*
* @see ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>OFF will always be listed.</p>
*/
@@ -1151,10 +1214,12 @@
*
* @see ACAMERA_CONTROL_AWB_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Not all the auto-white-balance modes may be supported by a
* given camera device. This entry lists the valid modes for
@@ -1183,22 +1248,25 @@
* @see ACAMERA_CONTROL_AF_REGIONS
* @see ACAMERA_CONTROL_AWB_REGIONS
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_CONTROL_MAX_REGIONS = // int32[3]
ACAMERA_CONTROL_START + 28,
/**
* <p>Current state of the auto-exposure (AE) algorithm.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_state_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Switching between or enabling AE modes (ACAMERA_CONTROL_AE_MODE) always
* resets the AE state to INACTIVE. Similarly, switching between ACAMERA_CONTROL_MODE,
@@ -1257,15 +1325,17 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_CONTROL_SCENE_MODE
*/
- ACAMERA_CONTROL_AE_STATE = // byte (enum)
+ ACAMERA_CONTROL_AE_STATE = // byte (acamera_metadata_enum_android_control_ae_state_t)
ACAMERA_CONTROL_START + 31,
/**
* <p>Current state of auto-focus (AF) algorithm.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_af_state_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Switching between or enabling AF modes (ACAMERA_CONTROL_AF_MODE) always
* resets the AF state to INACTIVE. Similarly, switching between ACAMERA_CONTROL_MODE,
@@ -1357,15 +1427,17 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_CONTROL_SCENE_MODE
*/
- ACAMERA_CONTROL_AF_STATE = // byte (enum)
+ ACAMERA_CONTROL_AF_STATE = // byte (acamera_metadata_enum_android_control_af_state_t)
ACAMERA_CONTROL_START + 32,
/**
* <p>Current state of auto-white balance (AWB) algorithm.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_awb_state_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Switching between or enabling AWB modes (ACAMERA_CONTROL_AWB_MODE) always
* resets the AWB state to INACTIVE. Similarly, switching between ACAMERA_CONTROL_MODE,
@@ -1408,37 +1480,41 @@
* @see ACAMERA_CONTROL_MODE
* @see ACAMERA_CONTROL_SCENE_MODE
*/
- ACAMERA_CONTROL_AWB_STATE = // byte (enum)
+ ACAMERA_CONTROL_AWB_STATE = // byte (acamera_metadata_enum_android_control_awb_state_t)
ACAMERA_CONTROL_START + 34,
/**
* <p>Whether the camera device supports ACAMERA_CONTROL_AE_LOCK</p>
*
* @see ACAMERA_CONTROL_AE_LOCK
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_ae_lock_available_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Devices with MANUAL_SENSOR capability or BURST_CAPTURE capability will always
* list <code>true</code>. This includes FULL devices.</p>
*/
- ACAMERA_CONTROL_AE_LOCK_AVAILABLE = // byte (enum)
+ ACAMERA_CONTROL_AE_LOCK_AVAILABLE = // byte (acamera_metadata_enum_android_control_ae_lock_available_t)
ACAMERA_CONTROL_START + 36,
/**
* <p>Whether the camera device supports ACAMERA_CONTROL_AWB_LOCK</p>
*
* @see ACAMERA_CONTROL_AWB_LOCK
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_awb_lock_available_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Devices with MANUAL_POST_PROCESSING capability or BURST_CAPTURE capability will
* always list <code>true</code>. This includes FULL devices.</p>
*/
- ACAMERA_CONTROL_AWB_LOCK_AVAILABLE = // byte (enum)
+ ACAMERA_CONTROL_AWB_LOCK_AVAILABLE = // byte (acamera_metadata_enum_android_control_awb_lock_available_t)
ACAMERA_CONTROL_START + 37,
/**
* <p>List of control modes for ACAMERA_CONTROL_MODE that are supported by this camera
@@ -1446,10 +1522,12 @@
*
* @see ACAMERA_CONTROL_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This list contains control modes that can be set for the camera device.
* LEGACY mode devices will always support AUTO mode. LIMITED and FULL
@@ -1463,10 +1541,12 @@
*
* @see ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Devices support post RAW sensitivity boost will advertise
* ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST key for controling
@@ -1484,11 +1564,13 @@
* <p>The amount of additional sensitivity boost applied to output images
* after RAW sensor data is captured.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Some camera devices support additional digital sensitivity boosting in the
* camera processing pipeline after sensor RAW image is captured.
@@ -1521,11 +1603,13 @@
*
* @see ACAMERA_CONTROL_CAPTURE_INTENT
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_control_enable_zsl_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>If enableZsl is <code>true</code>, the camera device may enable zero-shutter-lag mode for requests with
* STILL_CAPTURE capture intent. The camera device may use images captured in the past to
@@ -1552,7 +1636,7 @@
* @see ACAMERA_CONTROL_CAPTURE_INTENT
* @see ACAMERA_SENSOR_TIMESTAMP
*/
- ACAMERA_CONTROL_ENABLE_ZSL = // byte (enum)
+ ACAMERA_CONTROL_ENABLE_ZSL = // byte (acamera_metadata_enum_android_control_enable_zsl_t)
ACAMERA_CONTROL_START + 41,
ACAMERA_CONTROL_END,
@@ -1560,11 +1644,13 @@
* <p>Operation mode for edge
* enhancement.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_edge_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Edge enhancement improves sharpness and details in the captured image. OFF means
* no enhancement will be applied by the camera device.</p>
@@ -1586,7 +1672,7 @@
* The camera device may adjust its internal edge enhancement parameters for best
* image quality based on the android.reprocess.effectiveExposureFactor, if it is set.</p>
*/
- ACAMERA_EDGE_MODE = // byte (enum)
+ ACAMERA_EDGE_MODE = // byte (acamera_metadata_enum_android_edge_mode_t)
ACAMERA_EDGE_START,
/**
* <p>List of edge enhancement modes for ACAMERA_EDGE_MODE that are supported by this camera
@@ -1594,10 +1680,12 @@
*
* @see ACAMERA_EDGE_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Full-capability camera devices must always support OFF; camera devices that support
* YUV_REPROCESSING or PRIVATE_REPROCESSING will list ZERO_SHUTTER_LAG; all devices will
@@ -1610,11 +1698,13 @@
/**
* <p>The desired mode for for the camera device's flash control.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_flash_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control is only effective when flash unit is available
* (<code>ACAMERA_FLASH_INFO_AVAILABLE == true</code>).</p>
@@ -1635,16 +1725,18 @@
* @see ACAMERA_FLASH_INFO_AVAILABLE
* @see ACAMERA_FLASH_STATE
*/
- ACAMERA_FLASH_MODE = // byte (enum)
+ ACAMERA_FLASH_MODE = // byte (acamera_metadata_enum_android_flash_mode_t)
ACAMERA_FLASH_START + 2,
/**
* <p>Current state of the flash
* unit.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_flash_state_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>When the camera device doesn't have flash unit
* (i.e. <code>ACAMERA_FLASH_INFO_AVAILABLE == false</code>), this state will always be UNAVAILABLE.
@@ -1664,7 +1756,7 @@
* @see ACAMERA_FLASH_INFO_AVAILABLE
* @see ACAMERA_FLASH_MODE
*/
- ACAMERA_FLASH_STATE = // byte (enum)
+ ACAMERA_FLASH_STATE = // byte (acamera_metadata_enum_android_flash_state_t)
ACAMERA_FLASH_START + 5,
ACAMERA_FLASH_END,
@@ -1672,33 +1764,37 @@
* <p>Whether this camera device has a
* flash unit.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_flash_info_available_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Will be <code>false</code> if no flash is available.</p>
* <p>If there is no flash unit, none of the flash controls do
* anything.</p>
*/
- ACAMERA_FLASH_INFO_AVAILABLE = // byte (enum)
+ ACAMERA_FLASH_INFO_AVAILABLE = // byte (acamera_metadata_enum_android_flash_info_available_t)
ACAMERA_FLASH_INFO_START,
ACAMERA_FLASH_INFO_END,
/**
* <p>Operational mode for hot pixel correction.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_hot_pixel_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Hotpixel correction interpolates out, or otherwise removes, pixels
* that do not accurately measure the incoming light (i.e. pixels that
* are stuck at an arbitrary value or are oversensitive).</p>
*/
- ACAMERA_HOT_PIXEL_MODE = // byte (enum)
+ ACAMERA_HOT_PIXEL_MODE = // byte (acamera_metadata_enum_android_hot_pixel_mode_t)
ACAMERA_HOT_PIXEL_START,
/**
* <p>List of hot pixel correction modes for ACAMERA_HOT_PIXEL_MODE that are supported by this
@@ -1706,10 +1802,12 @@
*
* @see ACAMERA_HOT_PIXEL_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>FULL mode camera devices will always support FAST.</p>
*/
@@ -1721,13 +1819,14 @@
* <p>GPS coordinates to include in output JPEG
* EXIF.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: double[3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_JPEG_GPS_COORDINATES = // double[3]
ACAMERA_JPEG_START,
@@ -1735,13 +1834,14 @@
* <p>32 characters describing GPS algorithm to
* include in EXIF.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_JPEG_GPS_PROCESSING_METHOD = // byte
ACAMERA_JPEG_START + 1,
@@ -1749,24 +1849,27 @@
* <p>Time GPS fix was made to include in
* EXIF.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_JPEG_GPS_TIMESTAMP = // int64
ACAMERA_JPEG_START + 2,
/**
* <p>The orientation for a JPEG image.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The clockwise rotation angle in degrees, relative to the orientation
* to the camera, that the JPEG picture needs to be rotated by, to be viewed
@@ -1805,11 +1908,13 @@
* <p>Compression quality of the final JPEG
* image.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>85-95 is typical usage range.</p>
*/
@@ -1819,24 +1924,27 @@
* <p>Compression quality of JPEG
* thumbnail.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_JPEG_THUMBNAIL_QUALITY = // byte
ACAMERA_JPEG_START + 5,
/**
* <p>Resolution of embedded JPEG thumbnail.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When set to (0, 0) value, the JPEG EXIF will not contain thumbnail,
* but the captured JPEG will still be a valid image.</p>
@@ -1871,10 +1979,12 @@
*
* @see ACAMERA_JPEG_THUMBNAIL_SIZE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This list will include at least one non-zero resolution, plus <code>(0,0)</code> for indicating no
* thumbnail should be generated.</p>
@@ -1902,11 +2012,13 @@
* <p>The desired lens aperture size, as a ratio of lens focal length to the
* effective aperture diameter.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Setting this value is only supported on the camera devices that have a variable
* aperture lens.</p>
@@ -1934,11 +2046,13 @@
/**
* <p>The desired setting for the lens neutral density filter(s).</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control will not be supported on most camera devices.</p>
* <p>Lens filters are typically used to lower the amount of light the
@@ -1960,11 +2074,13 @@
/**
* <p>The desired lens focal length; used for optical zoom.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This setting controls the physical focal length of the camera
* device's lens. Changing the focal length changes the field of
@@ -1986,11 +2102,13 @@
* <p>Desired distance to plane of sharpest focus,
* measured from frontmost surface of the lens.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Should be zero for fixed-focus cameras</p>
*/
@@ -2000,11 +2118,13 @@
* <p>Sets whether the camera device uses optical image stabilization (OIS)
* when capturing images.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_lens_optical_stabilization_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>OIS is used to compensate for motion blur due to small
* movements of the camera during capture. Unlike digital image
@@ -2027,30 +2147,33 @@
* @see ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE
* @see ACAMERA_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
*/
- ACAMERA_LENS_OPTICAL_STABILIZATION_MODE = // byte (enum)
+ ACAMERA_LENS_OPTICAL_STABILIZATION_MODE = // byte (acamera_metadata_enum_android_lens_optical_stabilization_mode_t)
ACAMERA_LENS_START + 4,
/**
* <p>Direction the camera faces relative to
* device screen.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_lens_facing_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
- ACAMERA_LENS_FACING = // byte (enum)
+ ACAMERA_LENS_FACING = // byte (acamera_metadata_enum_android_lens_facing_t)
ACAMERA_LENS_START + 5,
/**
* <p>The orientation of the camera relative to the sensor
* coordinate system.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The four coefficients that describe the quaternion
* rotation from the Android sensor coordinate system to a
@@ -2084,11 +2207,13 @@
/**
* <p>Position of the camera optical center.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The position of the camera device's lens optical center,
* as a three-dimensional vector <code>(x,y,z)</code>, relative to the
@@ -2129,10 +2254,12 @@
* <p>The range of scene distances that are in
* sharp focus (depth of field).</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>If variable focus not supported, can still report
* fixed depth of field range</p>
@@ -2142,10 +2269,12 @@
/**
* <p>Current lens status.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_lens_state_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>For lens parameters ACAMERA_LENS_FOCAL_LENGTH, ACAMERA_LENS_FOCUS_DISTANCE,
* ACAMERA_LENS_FILTER_DENSITY and ACAMERA_LENS_APERTURE, when changes are requested,
@@ -2176,17 +2305,19 @@
* @see ACAMERA_LENS_INFO_AVAILABLE_FOCAL_LENGTHS
* @see ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE
*/
- ACAMERA_LENS_STATE = // byte (enum)
+ ACAMERA_LENS_STATE = // byte (acamera_metadata_enum_android_lens_state_t)
ACAMERA_LENS_START + 9,
/**
* <p>The parameters for this camera device's intrinsic
* calibration.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The five calibration parameters that describe the
* transform from camera-centric 3D coordinates to sensor
@@ -2245,11 +2376,13 @@
* <p>The correction coefficients to correct for this camera device's
* radial and tangential lens distortion.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[6]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Four radial distortion coefficients <code>[kappa_0, kappa_1, kappa_2,
* kappa_3]</code> and two tangential distortion coefficients
@@ -2290,10 +2423,12 @@
*
* @see ACAMERA_LENS_APERTURE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If the camera device doesn't support a variable lens aperture,
* this list will contain only one value, which is the fixed aperture size.</p>
@@ -2308,10 +2443,12 @@
*
* @see ACAMERA_LENS_FILTER_DENSITY
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If a neutral density filter is not supported by this camera device,
* this list will contain only 0. Otherwise, this list will include every
@@ -2325,10 +2462,12 @@
*
* @see ACAMERA_LENS_FOCAL_LENGTH
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If optical zoom is not supported, this list will only contain
* a single value corresponding to the fixed focal length of the
@@ -2343,10 +2482,12 @@
*
* @see ACAMERA_LENS_OPTICAL_STABILIZATION_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If OIS is not supported by a given camera device, this list will
* contain only OFF.</p>
@@ -2356,10 +2497,12 @@
/**
* <p>Hyperfocal distance for this lens.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If the lens is not fixed focus, the camera device will report this
* field when ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION is APPROXIMATE or CALIBRATED.</p>
@@ -2372,10 +2515,12 @@
* <p>Shortest distance from frontmost surface
* of the lens that can be brought into sharp focus.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If the lens is fixed-focus, this will be
* 0.</p>
@@ -2385,10 +2530,12 @@
/**
* <p>Dimensions of lens shading map.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The map should be on the order of 30-40 rows and columns, and
* must be smaller than 64x64.</p>
@@ -2398,10 +2545,12 @@
/**
* <p>The lens focus distance calibration quality.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_lens_info_focus_distance_calibration_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The lens focus distance calibration quality determines the reliability of
* focus related metadata entries, i.e. ACAMERA_LENS_FOCUS_DISTANCE,
@@ -2422,18 +2571,20 @@
* @see ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE
* @see ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE
*/
- ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION = // byte (enum)
+ ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION = // byte (acamera_metadata_enum_android_lens_info_focus_distance_calibration_t)
ACAMERA_LENS_INFO_START + 7,
ACAMERA_LENS_INFO_END,
/**
* <p>Mode of operation for the noise reduction algorithm.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_noise_reduction_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The noise reduction algorithm attempts to improve image quality by removing
* excessive noise added by the capture process, especially in dark conditions.</p>
@@ -2463,7 +2614,7 @@
*
* @see ACAMERA_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
*/
- ACAMERA_NOISE_REDUCTION_MODE = // byte (enum)
+ ACAMERA_NOISE_REDUCTION_MODE = // byte (acamera_metadata_enum_android_noise_reduction_mode_t)
ACAMERA_NOISE_REDUCTION_START,
/**
* <p>List of noise reduction modes for ACAMERA_NOISE_REDUCTION_MODE that are supported
@@ -2471,10 +2622,12 @@
*
* @see ACAMERA_NOISE_REDUCTION_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Full-capability camera devices will always support OFF and FAST.</p>
* <p>Camera devices that support YUV_REPROCESSING or PRIVATE_REPROCESSING will support
@@ -2489,10 +2642,12 @@
* <p>The maximum numbers of different types of output streams
* that can be configured and used simultaneously by a camera device.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This is a 3 element tuple that contains the max number of output simultaneous
* streams for raw sensor, processed (but not stalling), and processed (and stalling)
@@ -2523,10 +2678,12 @@
* through from when it was exposed to when the final completed result
* was available to the framework.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Depending on what settings are used in the request, and
* what streams are configured, the data may undergo less processing,
@@ -2542,10 +2699,12 @@
* has to go through from when it's exposed to when it's available
* to the framework.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>A typical minimum value for this is 2 (one stage to expose,
* one stage to readout) from the sensor. The ISP then usually adds
@@ -2568,10 +2727,12 @@
* <p>Defines how many sub-components
* a result will be composed of.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>In order to combat the pipeline latency, partial results
* may be delivered to the application layer from the camera device as
@@ -2592,10 +2753,12 @@
* <p>List of capabilities that this camera device
* advertises as fully supporting.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n] (acamera_metadata_enum_android_request_available_capabilities_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>A capability is a contract that the camera device makes in order
* to be able to satisfy one or more use cases.</p>
@@ -2620,16 +2783,18 @@
* @see ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS
* @see ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS
*/
- ACAMERA_REQUEST_AVAILABLE_CAPABILITIES = // byte[n] (enum)
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES = // byte[n] (acamera_metadata_enum_android_request_available_capabilities_t)
ACAMERA_REQUEST_START + 12,
/**
* <p>A list of all keys that the camera device has available
* to use with {@link ACaptureRequest}.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Attempting to set a key into a CaptureRequest that is not
* listed here will result in an invalid request and will be rejected
@@ -2648,10 +2813,12 @@
* to query with {@link ACameraMetadata} from
* {@link ACameraCaptureSession_captureCallback_result}.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Attempting to get a key from a CaptureResult that is not
* listed here will always return a <code>null</code> value. Getting a key from
@@ -2679,10 +2846,12 @@
* to query with {@link ACameraMetadata} from
* {@link ACameraManager_getCameraCharacteristics}.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This entry follows the same rules as
* ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS (except that it applies for
@@ -2698,11 +2867,13 @@
/**
* <p>The desired region of the sensor to read out for this capture.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>This control can be used to implement digital zoom.</p>
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
@@ -2748,10 +2919,12 @@
*
* @see ACAMERA_SCALER_CROP_REGION
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This represents the maximum amount of zooming possible by
* the camera device, or equivalently, the minimum cropping
@@ -2767,10 +2940,12 @@
* camera device supports
* (i.e. format, width, height, output/input stream).</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The configurations are listed as <code>(format, width, height, input?)</code>
* tuples.</p>
@@ -2805,16 +2980,18 @@
* @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
- ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS = // int32[n*4] (enum)
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS = // int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_t)
ACAMERA_SCALER_START + 10,
/**
* <p>This lists the minimum frame duration for each
* format/size combination.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This should correspond to the frame duration when only that
* stream is active, with all processing (typically in android.*.mode)
@@ -2836,10 +3013,12 @@
* <p>This lists the maximum stall duration for each
* output format/size combination.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>A stall duration is how much extra time would get added
* to the normal minimum frame duration for a repeating request
@@ -2904,10 +3083,12 @@
/**
* <p>The crop type that this camera device supports.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_scaler_cropping_type_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>When passing a non-centered crop region (ACAMERA_SCALER_CROP_REGION) to a camera
* device that only supports CENTER_ONLY cropping, the camera device will move the
@@ -2922,7 +3103,7 @@
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
- ACAMERA_SCALER_CROPPING_TYPE = // byte (enum)
+ ACAMERA_SCALER_CROPPING_TYPE = // byte (acamera_metadata_enum_android_scaler_cropping_type_t)
ACAMERA_SCALER_START + 13,
ACAMERA_SCALER_END,
@@ -2930,11 +3111,13 @@
* <p>Duration each pixel is exposed to
* light.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>If the sensor can't expose this exact duration, it will shorten the
* duration exposed to the nearest possible value (rather than expose longer).
@@ -2951,11 +3134,13 @@
* <p>Duration from start of frame exposure to
* start of next frame exposure.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
@@ -3037,11 +3222,13 @@
* <p>The amount of gain applied to sensor data
* before processing.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The sensitivity is the standard ISO sensitivity value,
* as defined in ISO 12232:2006.</p>
@@ -3072,10 +3259,12 @@
* @see ACAMERA_SENSOR_COLOR_TRANSFORM1
* @see ACAMERA_SENSOR_FORWARD_MATRIX1
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_reference_illuminant1_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The values in this key correspond to the values defined for the
* EXIF LightSource tag. These illuminants are standard light sources
@@ -3092,7 +3281,7 @@
* @see ACAMERA_SENSOR_FORWARD_MATRIX1
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
*/
- ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 = // byte (enum)
+ ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 = // byte (acamera_metadata_enum_android_sensor_reference_illuminant1_t)
ACAMERA_SENSOR_START + 3,
/**
* <p>The standard reference illuminant used as the scene light source when
@@ -3104,10 +3293,12 @@
* @see ACAMERA_SENSOR_COLOR_TRANSFORM2
* @see ACAMERA_SENSOR_FORWARD_MATRIX2
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>See ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 for more details.</p>
* <p>If this key is present, then ACAMERA_SENSOR_COLOR_TRANSFORM2,
@@ -3125,10 +3316,12 @@
* <p>A per-device calibration transform matrix that maps from the
* reference sensor colorspace to the actual device sensor colorspace.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to correct for per-device variations in the
* sensor colorspace, and is used for processing raw buffer data.</p>
@@ -3148,10 +3341,12 @@
* reference sensor colorspace to the actual device sensor colorspace
* (this is the colorspace of the raw buffer data).</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to correct for per-device variations in the
* sensor colorspace, and is used for processing raw buffer data.</p>
@@ -3172,10 +3367,12 @@
* <p>A matrix that transforms color values from CIE XYZ color space to
* reference sensor color space.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to convert from the standard CIE XYZ color
* space to the reference sensor colorspace, and is used when processing
@@ -3198,10 +3395,12 @@
* <p>A matrix that transforms color values from CIE XYZ color space to
* reference sensor color space.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to convert from the standard CIE XYZ color
* space to the reference sensor colorspace, and is used when processing
@@ -3226,10 +3425,12 @@
* <p>A matrix that transforms white balanced camera colors from the reference
* sensor colorspace to the CIE XYZ colorspace with a D50 whitepoint.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to convert to the standard CIE XYZ colorspace, and
* is used when processing raw buffer data.</p>
@@ -3250,10 +3451,12 @@
* <p>A matrix that transforms white balanced camera colors from the reference
* sensor colorspace to the CIE XYZ colorspace with a D50 whitepoint.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3*3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This matrix is used to convert to the standard CIE XYZ colorspace, and
* is used when processing raw buffer data.</p>
@@ -3276,10 +3479,12 @@
* <p>A fixed black level offset for each of the color filter arrangement
* (CFA) mosaic channels.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This key specifies the zero light value for each of the CFA mosaic
* channels in the camera sensor. The maximal value output by the
@@ -3310,10 +3515,12 @@
* <p>Maximum sensitivity that is implemented
* purely through analog gain.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>For ACAMERA_SENSOR_SENSITIVITY values less than or
* equal to this, all applied gain must be analog. For
@@ -3328,10 +3535,12 @@
* <p>Clockwise angle through which the output image needs to be rotated to be
* upright on the device screen in its native orientation.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Also defines the direction of rolling shutter readout, which is from top to bottom in
* the sensor's coordinate system.</p>
@@ -3342,10 +3551,12 @@
* <p>Time at start of exposure of first
* row of the image sensor active array, in nanoseconds.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The timestamps are also included in all image
* buffers produced for the same capture, and will be identical
@@ -3374,10 +3585,12 @@
* <p>The estimated camera neutral color in the native sensor colorspace at
* the time of capture.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: rational[3]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>This value gives the neutral color point encoded as an RGB value in the
* native sensor color space. The neutral color point indicates the
@@ -3391,10 +3604,12 @@
/**
* <p>Noise model coefficients for each CFA mosaic channel.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: double[2*CFA Channels]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>This key contains two noise model coefficients for each CFA channel
* corresponding to the sensor amplification (S) and sensor readout
@@ -3421,10 +3636,12 @@
/**
* <p>The worst-case divergence between Bayer green channels.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>This value is an estimate of the worst case split between the
* Bayer green channels in the red and blue rows in the sensor color
@@ -3465,11 +3682,13 @@
*
* @see ACAMERA_SENSOR_TEST_PATTERN_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Each color channel is treated as an unsigned 32-bit integer.
* The camera device then uses the most significant X bits
@@ -3484,11 +3703,13 @@
* <p>When enabled, the sensor sends a test pattern instead of
* doing a real exposure from the camera.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32 (acamera_metadata_enum_android_sensor_test_pattern_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When a test pattern is enabled, all manual sensor controls specified
* by ACAMERA_SENSOR_* will be ignored. All other controls should
@@ -3498,7 +3719,7 @@
* would not actually affect it).</p>
* <p>Defaults to OFF.</p>
*/
- ACAMERA_SENSOR_TEST_PATTERN_MODE = // int32 (enum)
+ ACAMERA_SENSOR_TEST_PATTERN_MODE = // int32 (acamera_metadata_enum_android_sensor_test_pattern_mode_t)
ACAMERA_SENSOR_START + 24,
/**
* <p>List of sensor test pattern modes for ACAMERA_SENSOR_TEST_PATTERN_MODE
@@ -3506,10 +3727,12 @@
*
* @see ACAMERA_SENSOR_TEST_PATTERN_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Defaults to OFF, and always includes OFF if defined.</p>
*/
@@ -3519,10 +3742,12 @@
* <p>Duration between the start of first row exposure
* and the start of last row exposure.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>This is the exposure time skew between the first and last
* row exposure start times. The first row and the last row are
@@ -3539,10 +3764,12 @@
* <p>List of disjoint rectangles indicating the sensor
* optically shielded black pixel regions.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4*num_regions]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>In most camera sensors, the active array is surrounded by some
* optically shielded pixel areas. By blocking light, these pixels
@@ -3569,10 +3796,12 @@
* <p>A per-frame dynamic black level offset for each of the color filter
* arrangement (CFA) mosaic channels.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Camera sensor black levels may vary dramatically for different
* capture settings (e.g. ACAMERA_SENSOR_SENSITIVITY). The fixed black
@@ -3610,10 +3839,12 @@
/**
* <p>Maximum raw value output by sensor for this frame.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Since the ACAMERA_SENSOR_BLACK_LEVEL_PATTERN may change for different
* capture settings (e.g., ACAMERA_SENSOR_SENSITIVITY), the white
@@ -3637,10 +3868,12 @@
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
* the region that actually receives light from the scene) after any geometric correction
@@ -3668,10 +3901,12 @@
*
* @see ACAMERA_SENSOR_SENSITIVITY
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The values are the standard ISO sensitivity values,
* as defined in ISO 12232:2006.</p>
@@ -3683,14 +3918,15 @@
* represents the colors in the top-left 2x2 section of
* the sensor, in reading order.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
- ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT = // byte (enum)
+ ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT = // byte (acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t)
ACAMERA_SENSOR_INFO_START + 2,
/**
* <p>The range of image exposure times for ACAMERA_SENSOR_EXPOSURE_TIME supported
@@ -3698,12 +3934,13 @@
*
* @see ACAMERA_SENSOR_EXPOSURE_TIME
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE = // int64[2]
ACAMERA_SENSOR_INFO_START + 3,
@@ -3713,10 +3950,12 @@
*
* @see ACAMERA_SENSOR_FRAME_DURATION
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Attempting to use frame durations beyond the maximum will result in the frame
* duration being clipped to the maximum. See that control for a full definition of frame
@@ -3731,10 +3970,12 @@
* <p>The physical dimensions of the full pixel
* array.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This is the physical size of the sensor pixel
* array defined by ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE.</p>
@@ -3747,10 +3988,12 @@
* <p>Dimensions of the full pixel array, possibly
* including black calibration pixels.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The pixel count of the full pixel array of the image sensor, which covers
* ACAMERA_SENSOR_INFO_PHYSICAL_SIZE area. This represents the full pixel dimensions of
@@ -3773,10 +4016,12 @@
/**
* <p>Maximum raw value output by sensor.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This specifies the fully-saturated encoding level for the raw
* sample values from the sensor. This is typically caused by the
@@ -3802,26 +4047,30 @@
/**
* <p>The time base source for sensor capture start timestamps.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_info_timestamp_source_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The timestamps provided for captures are always in nanoseconds and monotonic, but
* may not based on a time source that can be compared to other system time sources.</p>
* <p>This characteristic defines the source for the timestamps, and therefore whether they
* can be compared against other system time sources/timestamps.</p>
*/
- ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE = // byte (enum)
+ ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE = // byte (acamera_metadata_enum_android_sensor_info_timestamp_source_t)
ACAMERA_SENSOR_INFO_START + 8,
/**
* <p>Whether the RAW images output from this camera device are subject to
* lens shading correction.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_info_lens_shading_applied_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If TRUE, all images produced by the camera device in the RAW image formats will
* have lens shading correction already applied to it. If FALSE, the images will
@@ -3830,16 +4079,18 @@
* <p>This key will be <code>null</code> for all devices do not report this information.
* Devices with RAW capability will always report this information in this key.</p>
*/
- ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED = // byte (enum)
+ ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED = // byte (acamera_metadata_enum_android_sensor_info_lens_shading_applied_t)
ACAMERA_SENSOR_INFO_START + 9,
/**
* <p>The area of the image sensor which corresponds to active pixels prior to the
* application of any geometric distortion correction.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
@@ -3906,11 +4157,13 @@
* <p>Quality of lens shading correction applied
* to the image data.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_shading_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When set to OFF mode, no lens shading correction will be applied by the
* camera device, and an identity lens shading map data will be provided
@@ -3940,17 +4193,19 @@
* @see ACAMERA_CONTROL_AWB_MODE
* @see ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE
*/
- ACAMERA_SHADING_MODE = // byte (enum)
+ ACAMERA_SHADING_MODE = // byte (acamera_metadata_enum_android_shading_mode_t)
ACAMERA_SHADING_START,
/**
* <p>List of lens shading modes for ACAMERA_SHADING_MODE that are supported by this camera device.</p>
*
* @see ACAMERA_SHADING_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This list contains lens shading modes that can be set for the camera device.
* Camera devices that support the MANUAL_POST_PROCESSING capability will always
@@ -3965,41 +4220,47 @@
* <p>Operating mode for the face detector
* unit.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_statistics_face_detect_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Whether face detection is enabled, and whether it
* should output just the basic fields or the full set of
* fields.</p>
*/
- ACAMERA_STATISTICS_FACE_DETECT_MODE = // byte (enum)
+ ACAMERA_STATISTICS_FACE_DETECT_MODE = // byte (acamera_metadata_enum_android_statistics_face_detect_mode_t)
ACAMERA_STATISTICS_START,
/**
* <p>Operating mode for hot pixel map generation.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_statistics_hot_pixel_map_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>If set to <code>true</code>, a hot pixel map is returned in ACAMERA_STATISTICS_HOT_PIXEL_MAP.
* If set to <code>false</code>, no hot pixel map will be returned.</p>
*
* @see ACAMERA_STATISTICS_HOT_PIXEL_MAP
*/
- ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE = // byte (enum)
+ ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE = // byte (acamera_metadata_enum_android_statistics_hot_pixel_map_mode_t)
ACAMERA_STATISTICS_START + 3,
/**
* <p>List of unique IDs for detected faces.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Each detected face is given a unique ID that is valid for as long as the face is visible
* to the camera device. A face that leaves the field of view and later returns may be
@@ -4014,10 +4275,12 @@
* <p>List of landmarks for detected
* faces.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n*6]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The coordinate system is that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
@@ -4032,10 +4295,12 @@
* <p>List of the bounding rectangles for detected
* faces.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n*4]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
* <p>The coordinate system is that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
@@ -4051,10 +4316,12 @@
* <p>List of the face confidence scores for
* detected faces</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE != OFF.</p>
*
@@ -4067,10 +4334,12 @@
* that lists the coefficients used to correct for vignetting and color shading,
* for each Bayer color channel of RAW image data.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[4*n*m]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>The map provided here is the same map that is used by the camera device to
* correct both color shading and vignetting for output non-RAW images.</p>
@@ -4144,10 +4413,12 @@
* <p>The camera device estimated scene illumination lighting
* frequency.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_statistics_scene_flicker_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>Many light sources, such as most fluorescent lights, flicker at a rate
* that depends on the local utility power standards. This flicker must be
@@ -4167,15 +4438,17 @@
* @see ACAMERA_CONTROL_AE_MODE
* @see ACAMERA_CONTROL_MODE
*/
- ACAMERA_STATISTICS_SCENE_FLICKER = // byte (enum)
+ ACAMERA_STATISTICS_SCENE_FLICKER = // byte (acamera_metadata_enum_android_statistics_scene_flicker_t)
ACAMERA_STATISTICS_START + 14,
/**
* <p>List of <code>(x, y)</code> coordinates of hot/defective pixels on the sensor.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[2*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>A coordinate <code>(x, y)</code> must lie between <code>(0, 0)</code>, and
* <code>(width - 1, height - 1)</code> (inclusive), which are the top-left and
@@ -4193,11 +4466,13 @@
* <p>Whether the camera device will output the lens
* shading map in output result metadata.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When set to ON,
* ACAMERA_STATISTICS_LENS_SHADING_MAP will be provided in
@@ -4206,7 +4481,7 @@
*
* @see ACAMERA_STATISTICS_LENS_SHADING_MAP
*/
- ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE = // byte (enum)
+ ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE = // byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)
ACAMERA_STATISTICS_START + 16,
ACAMERA_STATISTICS_END,
@@ -4216,10 +4491,12 @@
*
* @see ACAMERA_STATISTICS_FACE_DETECT_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>OFF is always supported.</p>
*/
@@ -4229,12 +4506,13 @@
* <p>The maximum number of simultaneously detectable
* faces.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
- * <p>None</p>
*/
ACAMERA_STATISTICS_INFO_MAX_FACE_COUNT = // int32
ACAMERA_STATISTICS_INFO_START + 2,
@@ -4244,10 +4522,12 @@
*
* @see ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If no hotpixel map output is available for this camera device, this will contain only
* <code>false</code>.</p>
@@ -4261,10 +4541,12 @@
*
* @see ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If no lens shading map output is available for this camera device, this key will
* contain only OFF.</p>
@@ -4282,11 +4564,13 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n*2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>See ACAMERA_TONEMAP_CURVE_RED for more details.</p>
*
@@ -4301,11 +4585,13 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n*2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>See ACAMERA_TONEMAP_CURVE_RED for more details.</p>
*
@@ -4320,11 +4606,13 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float[n*2]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Each channel's curve is defined by an array of control points:</p>
* <pre><code>ACAMERA_TONEMAP_CURVE_RED =
@@ -4375,11 +4663,13 @@
/**
* <p>High-level global contrast/gamma/tonemapping control.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_tonemap_mode_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>When switching to an application-defined contrast curve by setting
* ACAMERA_TONEMAP_MODE to CONTRAST_CURVE, the curve is defined
@@ -4402,16 +4692,18 @@
*
* @see ACAMERA_TONEMAP_MODE
*/
- ACAMERA_TONEMAP_MODE = // byte (enum)
+ ACAMERA_TONEMAP_MODE = // byte (acamera_metadata_enum_android_tonemap_mode_t)
ACAMERA_TONEMAP_START + 3,
/**
* <p>Maximum number of supported points in the
* tonemap curve that can be used for android.tonemap.curve.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If the actual number of points provided by the application (in ACAMERA_TONEMAPCURVE_*) is
* less than this maximum, the camera device will resample the curve to its internal
@@ -4428,10 +4720,12 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always contain
* at least one of below mode combinations:</p>
@@ -4449,11 +4743,13 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: float</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The tonemap curve will be defined the following formula:
* * OUT = pow(IN, 1.0 / gamma)
@@ -4474,11 +4770,13 @@
*
* @see ACAMERA_TONEMAP_MODE
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_tonemap_preset_curve_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>The tonemap curve will be defined by specified standard.</p>
* <p>sRGB (approximated by 16 control points):</p>
@@ -4488,17 +4786,19 @@
* <p>Note that above figures show a 16 control points approximation of preset
* curves. Camera devices may apply a different approximation to the curve.</p>
*/
- ACAMERA_TONEMAP_PRESET_CURVE = // byte (enum)
+ ACAMERA_TONEMAP_PRESET_CURVE = // byte (acamera_metadata_enum_android_tonemap_preset_curve_t)
ACAMERA_TONEMAP_START + 7,
ACAMERA_TONEMAP_END,
/**
* <p>Generally classifies the overall set of the camera device functionality.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_info_supported_hardware_level_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>The supported hardware level is a high-level description of the camera device's
* capabilities, summarizing several capabilities into one field. Each level adds additional
@@ -4551,7 +4851,7 @@
* @see ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
* @see ACAMERA_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES
*/
- ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL = // byte (enum)
+ ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL = // byte (acamera_metadata_enum_android_info_supported_hardware_level_t)
ACAMERA_INFO_START,
ACAMERA_INFO_END,
@@ -4559,11 +4859,13 @@
* <p>Whether black-level compensation is locked
* to its current values, or is free to vary.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_black_level_lock_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* <li>ACaptureRequest</li>
- * </ul>
+ * </ul></p>
*
* <p>Whether the black level offset was locked for this frame. Should be
* ON if ACAMERA_BLACK_LEVEL_LOCK was ON in the capture request, unless
@@ -4572,7 +4874,7 @@
*
* @see ACAMERA_BLACK_LEVEL_LOCK
*/
- ACAMERA_BLACK_LEVEL_LOCK = // byte (enum)
+ ACAMERA_BLACK_LEVEL_LOCK = // byte (acamera_metadata_enum_android_black_level_lock_t)
ACAMERA_BLACK_LEVEL_START,
ACAMERA_BLACK_LEVEL_END,
@@ -4581,10 +4883,12 @@
* with which the output result (metadata + buffers) has been fully
* synchronized.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64 (acamera_metadata_enum_android_sync_frame_number_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul>
+ * </ul></p>
*
* <p>When a request is submitted to the camera device, there is usually a
* delay of several frames before the controls get applied. A camera
@@ -4638,17 +4942,19 @@
* @see ACAMERA_REQUEST_PIPELINE_MAX_DEPTH
* @see ACAMERA_SYNC_FRAME_NUMBER
*/
- ACAMERA_SYNC_FRAME_NUMBER = // int64 (enum)
+ ACAMERA_SYNC_FRAME_NUMBER = // int64 (acamera_metadata_enum_android_sync_frame_number_t)
ACAMERA_SYNC_START,
/**
* <p>The maximum number of frames that can occur after a request
* (different than the previous) has been submitted, and before the
* result's state becomes synchronized.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32 (acamera_metadata_enum_android_sync_max_latency_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This defines the maximum distance (in number of metadata results),
* between the frame number of the request that has new controls to apply
@@ -4657,7 +4963,7 @@
* must occur before the camera device knows for a fact that the new
* submitted camera settings have been applied in outgoing frames.</p>
*/
- ACAMERA_SYNC_MAX_LATENCY = // int32 (enum)
+ ACAMERA_SYNC_MAX_LATENCY = // int32 (acamera_metadata_enum_android_sync_max_latency_t)
ACAMERA_SYNC_START + 1,
ACAMERA_SYNC_END,
@@ -4666,10 +4972,12 @@
* configurations that this camera device supports
* (i.e. format, width, height, output/input stream).</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>These are output stream configurations for use with
* dataSpace HAL_DATASPACE_DEPTH. The configurations are
@@ -4683,16 +4991,18 @@
* android.depth.maxDepthSamples, 1, OUTPUT)</code> in addition to
* the entries for HAL_PIXEL_FORMAT_Y16.</p>
*/
- ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS = // int32[n*4] (enum)
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS = // int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_t)
ACAMERA_DEPTH_START + 1,
/**
* <p>This lists the minimum frame duration for each
* format/size combination for depth output formats.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>This should correspond to the frame duration when only that
* stream is active, with all processing (typically in android.*.mode)
@@ -4714,10 +5024,12 @@
* <p>This lists the maximum stall duration for each
* output format/size combination for depth streams.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>A stall duration is how much extra time would get added
* to the normal minimum frame duration for a repeating request
@@ -4737,10 +5049,12 @@
* DEPTH16 / DEPTH_POINT_CLOUD output, and normal color outputs (such as
* YUV_420_888, JPEG, or RAW) simultaneously.</p>
*
- * <p>This tag may appear in:</p>
+ * <p>Type: byte (acamera_metadata_enum_android_depth_depth_is_exclusive_t)</p>
+ *
+ * <p>This tag may appear in:
* <ul>
* <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * </ul>
+ * </ul></p>
*
* <p>If TRUE, including both depth and color outputs in a single
* capture request is not supported. An application must interleave color
@@ -4751,7 +5065,7 @@
* measure depth values, which causes the color image to be
* corrupted during depth measurement.</p>
*/
- ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE = // byte (enum)
+ ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE = // byte (acamera_metadata_enum_android_depth_depth_is_exclusive_t)
ACAMERA_DEPTH_START + 4,
ACAMERA_DEPTH_END,
@@ -6966,6 +7280,7 @@
} acamera_metadata_enum_android_depth_depth_is_exclusive_t;
+
#endif /* __ANDROID_API__ >= 24 */
__END_DECLS
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 386546f..074489a 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -245,6 +245,11 @@
plugin = hPlugin;
}
);
+
+ if (!hResult.isOk()) {
+ ALOGE("createPlugin remote call failed");
+ }
+
return plugin;
}
@@ -396,8 +401,11 @@
if (mPlugin == NULL) {
mInitCheck = ERROR_UNSUPPORTED;
} else {
- mInitCheck = OK;
- mPlugin->setListener(this);
+ if (!mPlugin->setListener(this).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ } else {
+ mInitCheck = OK;
+ }
}
return mInitCheck;
@@ -411,12 +419,14 @@
}
setListener(NULL);
- if (mPlugin != NULL) {
- mPlugin->setListener(NULL);
- }
- mPlugin.clear();
mInitCheck = NO_INIT;
+ if (mPlugin != NULL) {
+ if (!mPlugin->setListener(NULL).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
+ }
+ mPlugin.clear();
return OK;
}
@@ -472,11 +482,14 @@
return mInitCheck;
}
- Status status = mPlugin->closeSession(toHidlVec(sessionId));
- if (status == Status::OK) {
- DrmSessionManager::Instance()->removeSession(sessionId);
+ Return<Status> status = mPlugin->closeSession(toHidlVec(sessionId));
+ if (status.isOk()) {
+ if (status == Status::OK) {
+ DrmSessionManager::Instance()->removeSession(sessionId);
+ }
+ return toStatusT(status);
}
- return toStatusT(status);
+ return DEAD_OBJECT;
}
status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
@@ -962,12 +975,16 @@
void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
{
Mutex::Autolock autoLock(mLock);
+
setListener(NULL);
+ mInitCheck = NO_INIT;
+
if (mPlugin != NULL) {
- mPlugin->setListener(NULL);
+ if (!mPlugin->setListener(NULL).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
}
mPlugin.clear();
- mInitCheck = NO_INIT;
}
void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
diff --git a/include/media/AudioClient.h b/include/media/AudioClient.h
new file mode 120000
index 0000000..a0530e4
--- /dev/null
+++ b/include/media/AudioClient.h
@@ -0,0 +1 @@
+../../media/libaudioclient/include/media/AudioClient.h
\ No newline at end of file
diff --git a/include/media/MmapStreamInterface.h b/include/media/MmapStreamInterface.h
index 7dbc19e..d689e25 100644
--- a/include/media/MmapStreamInterface.h
+++ b/include/media/MmapStreamInterface.h
@@ -18,6 +18,7 @@
#define ANDROID_AUDIO_MMAP_STREAM_INTERFACE_H
#include <system/audio.h>
+#include <media/AudioClient.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -37,12 +38,6 @@
DIRECTION_INPUT, /**< open a capture mmap stream */
} stream_direction_t;
- class Client {
- public:
- uid_t clientUid;
- pid_t clientPid;
- String16 packageName;
- };
/**
* Open a playback or capture stream in MMAP mode at the audio HAL.
*
@@ -53,13 +48,14 @@
* \param[in,out] config audio parameters (sampling rate, format ...) for the stream.
* Requested parameters as input,
* Actual parameters as output
- * \param[in] client a Client struct describing the first client using this stream.
+ * \param[in] client a AudioClient struct describing the first client using this stream.
* \param[in,out] deviceId audio device the stream should preferably be routed to/from
* Requested as input,
* Actual as output
* \param[in] callback the MmapStreamCallback interface used by AudioFlinger to notify
* condition changes affecting the stream operation
* \param[out] interface the MmapStreamInterface interface controlling the created stream
+ * \param[out] same unique handle as the one used for the first client stream started.
* \return OK if the stream was successfully created.
* NO_INIT if AudioFlinger is not properly initialized
* BAD_VALUE if the stream cannot be opened because of invalid arguments
@@ -68,10 +64,11 @@
static status_t openMmapStream(stream_direction_t direction,
const audio_attributes_t *attr,
audio_config_base_t *config,
- const Client& client,
+ const AudioClient& client,
audio_port_handle_t *deviceId,
const sp<MmapStreamCallback>& callback,
- sp<MmapStreamInterface>& interface);
+ sp<MmapStreamInterface>& interface,
+ audio_port_handle_t *handle);
/**
* Retrieve information on the mmap buffer used for audio samples transfer.
@@ -105,13 +102,13 @@
* Start a stream operating in mmap mode.
* createMmapBuffer() must be called before calling start()
*
- * \param[in] client a Client struct describing the client starting on this stream.
+ * \param[in] client a AudioClient struct describing the client starting on this stream.
* \param[out] handle unique handle for this instance. Used with stop().
* \return OK in case of success.
* NO_INIT in case of initialization error
* INVALID_OPERATION if called out of sequence
*/
- virtual status_t start(const Client& client, audio_port_handle_t *handle) = 0;
+ virtual status_t start(const AudioClient& client, audio_port_handle_t *handle) = 0;
/**
* Stop a stream operating in mmap mode.
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
index 397e576..bf3be9a 100644
--- a/include/media/omx/1.0/WGraphicBufferSource.h
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -74,6 +74,7 @@
BnStatus setTimeLapseConfig(double fps, double captureFps) override;
BnStatus setStartTimeUs(int64_t startTimeUs) override;
BnStatus setStopTimeUs(int64_t stopTimeUs) override;
+ BnStatus getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) override;
BnStatus setColorAspects(int32_t aspects) override;
BnStatus setTimeOffsetUs(int64_t timeOffsetsUs) override;
BnStatus signalEndOfInputStream() override;
diff --git a/media/libaaudio/examples/input_monitor/Android.mk b/media/libaaudio/examples/input_monitor/Android.mk
index b56328b..5053e7d 100644
--- a/media/libaaudio/examples/input_monitor/Android.mk
+++ b/media/libaaudio/examples/input_monitor/Android.mk
@@ -1,6 +1 @@
-# include $(call all-subdir-makefiles)
-
-# Just include static/ for now.
-LOCAL_PATH := $(call my-dir)
-#include $(LOCAL_PATH)/jni/Android.mk
-include $(LOCAL_PATH)/static/Android.mk
+include $(call all-subdir-makefiles)
diff --git a/media/libaaudio/examples/input_monitor/jni/Android.mk b/media/libaaudio/examples/input_monitor/jni/Android.mk
index 3e24f9f..9b1ce2c 100644
--- a/media/libaaudio/examples/input_monitor/jni/Android.mk
+++ b/media/libaaudio/examples/input_monitor/jni/Android.mk
@@ -11,7 +11,7 @@
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/input_monitor.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := input_monitor_ndk
+LOCAL_MODULE := input_monitor
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
@@ -23,11 +23,11 @@
LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := input_monitor_callback_ndk
+LOCAL_MODULE := input_monitor_callback
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := libaaudio_prebuilt
LOCAL_SRC_FILES := libaaudio.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
-include $(PREBUILT_SHARED_LIBRARY)
\ No newline at end of file
+include $(PREBUILT_SHARED_LIBRARY)
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index 715c5f8..910b10c 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -22,33 +22,22 @@
#include <stdlib.h>
#include <math.h>
#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimpleRecorder.h"
-#define SAMPLE_RATE 48000
-
-#define NUM_SECONDS 10
-
+// TODO support FLOAT
+#define REQUIRED_FORMAT AAUDIO_FORMAT_PCM_I16
#define MIN_FRAMES_TO_READ 48 /* arbitrary, 1 msec at 48000 Hz */
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
{
- (void)argc; // unused
-
+ AAudioArgsParser argParser;
aaudio_result_t result;
AAudioSimpleRecorder recorder;
int actualSamplesPerFrame;
int actualSampleRate;
- const aaudio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
- aaudio_format_t actualDataFormat;
-
- const int requestedInputChannelCount = 1; // Can affect whether we get a FAST path.
-
- //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
- const aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
- //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
- const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
- //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
+ aaudio_format_t actualDataFormat;
aaudio_sharing_mode_t actualSharingMode;
AAudioStream *aaudioStream = nullptr;
@@ -61,6 +50,7 @@
int16_t *data = nullptr;
float peakLevel = 0.0;
int loopCounter = 0;
+ int32_t deviceId;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -68,27 +58,27 @@
printf("%s - Monitor input level using AAudio\n", argv[0]);
- recorder.setPerformanceMode(requestedPerformanceMode);
- recorder.setSharingMode(requestedSharingMode);
+ argParser.setFormat(REQUIRED_FORMAT);
+ if (argParser.parseArgs(argc, argv)) {
+ return EXIT_FAILURE;
+ }
- result = recorder.open(requestedInputChannelCount, 48000, requestedDataFormat,
- nullptr, nullptr, nullptr);
+ result = recorder.open(argParser);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - recorder.open() returned %d\n", result);
goto finish;
}
aaudioStream = recorder.getStream();
+ argParser.compareWithStream(aaudioStream);
+
+ deviceId = AAudioStream_getDeviceId(aaudioStream);
+ printf("deviceId = %d\n", deviceId);
actualSamplesPerFrame = AAudioStream_getSamplesPerFrame(aaudioStream);
printf("SamplesPerFrame = %d\n", actualSamplesPerFrame);
actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
printf("SamplesPerFrame = %d\n", actualSampleRate);
- actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
- printf("SharingMode: requested = %s, actual = %s\n",
- getSharingModeText(requestedSharingMode),
- getSharingModeText(actualSharingMode));
-
// This is the number of frames that are written in one chunk by a DMA controller
// or a DSP.
framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
@@ -103,14 +93,12 @@
printf("DataFormat: framesPerRead = %d\n",framesPerRead);
actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("DataFormat: requested = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+ printf("DataFormat: requested = %d, actual = %d\n",
+ REQUIRED_FORMAT, actualDataFormat);
// TODO handle other data formats
- assert(actualDataFormat == AAUDIO_FORMAT_PCM_I16);
+ assert(actualDataFormat == REQUIRED_FORMAT);
- printf("PerformanceMode: requested = %d, actual = %d\n", requestedPerformanceMode,
- AAudioStream_getPerformanceMode(aaudioStream));
-
- // Allocate a buffer for the audio data.
+ // Allocate a buffer for the PCM_16 audio data.
data = new(std::nothrow) int16_t[framesPerRead * actualSamplesPerFrame];
if (data == nullptr) {
fprintf(stderr, "ERROR - could not allocate data buffer\n");
@@ -129,11 +117,11 @@
printf("after start, state = %s\n", AAudio_convertStreamStateToText(state));
// Record for a while.
- framesToRecord = actualSampleRate * NUM_SECONDS;
+ framesToRecord = actualSampleRate * argParser.getDurationSeconds();
framesLeft = framesToRecord;
while (framesLeft > 0) {
// Read audio data from the stream.
- const int64_t timeoutNanos = 100 * NANOS_PER_MILLISECOND;
+ const int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
int minFrames = (framesToRecord < framesPerRead) ? framesToRecord : framesPerRead;
int actual = AAudioStream_read(aaudioStream, data, minFrames, timeoutNanos);
if (actual < 0) {
@@ -169,6 +157,8 @@
goto finish;
}
+ argParser.compareWithStream(aaudioStream);
+
finish:
recorder.close();
delete[] data;
diff --git a/media/libaaudio/examples/input_monitor/static/Android.mk b/media/libaaudio/examples/input_monitor/static/Android.mk
deleted file mode 100644
index 80a3906..0000000
--- a/media/libaaudio/examples/input_monitor/static/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := samples
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-# TODO reorganize folders to avoid using ../
-LOCAL_SRC_FILES:= ../src/input_monitor.cpp
-
-LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
- libbinder libcutils libutils \
- libaudioclient liblog libtinyalsa libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
-
-LOCAL_MODULE := input_monitor
-include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp
-
-LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
- libbinder libcutils libutils \
- libaudioclient liblog libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
-
-LOCAL_MODULE := input_monitor_callback
-include $(BUILD_EXECUTABLE)
diff --git a/media/libaaudio/examples/input_monitor/static/README.md b/media/libaaudio/examples/input_monitor/static/README.md
deleted file mode 100644
index 6e26d7b..0000000
--- a/media/libaaudio/examples/input_monitor/static/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Makefile for building simple command line examples.
-They link with AAudio as a static library.
diff --git a/media/libaaudio/examples/loopback/jni/Android.mk b/media/libaaudio/examples/loopback/jni/Android.mk
index dc933e3..d78f286 100644
--- a/media/libaaudio/examples/loopback/jni/Android.mk
+++ b/media/libaaudio/examples/loopback/jni/Android.mk
@@ -4,7 +4,8 @@
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/examples/utils
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/loopback.cpp
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
new file mode 100644
index 0000000..21cf341
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tools for measuring latency and for detecting glitches.
+ * These classes are pure math and can be used with any audio system.
+ */
+
+#ifndef AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
+#define AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
+
+#include <algorithm>
+#include <assert.h>
+#include <cctype>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// Tag for machine readable results as property = value pairs
+#define LOOPBACK_RESULT_TAG "RESULT: "
+#define LOOPBACK_SAMPLE_RATE 48000
+
+#define MILLIS_PER_SECOND 1000
+
+#define MAX_ZEROTH_PARTIAL_BINS 40
+
+static const float s_Impulse[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, // silence on each side of the impulse
+ 0.5f, 0.9999f, 0.0f, -0.9999, -0.5f, // bipolar
+ -0.2f, 0.0f, 0.0f, 0.0f, 0.0f
+};
+
+class PseudoRandom {
+public:
+ PseudoRandom() {}
+ PseudoRandom(int64_t seed)
+ : mSeed(seed)
+ {}
+
+ /**
+ * Returns the next random double from -1.0 to 1.0
+ *
+ * @return value from -1.0 to 1.0
+ */
+ double nextRandomDouble() {
+ return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
+ }
+
+ /** Calculate random 32 bit number using linear-congruential method. */
+ int32_t nextRandomInteger() {
+ // Use values for 64-bit sequence from MMIX by Donald Knuth.
+ mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
+ return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
+ }
+
+private:
+ int64_t mSeed = 99887766;
+};
+
+static double calculateCorrelation(const float *a,
+ const float *b,
+ int windowSize)
+{
+ double correlation = 0.0;
+ double sumProducts = 0.0;
+ double sumSquares = 0.0;
+
+ // Correlate a against b.
+ for (int i = 0; i < windowSize; i++) {
+ float s1 = a[i];
+ float s2 = b[i];
+ // Use a normalized cross-correlation.
+ sumProducts += s1 * s2;
+ sumSquares += ((s1 * s1) + (s2 * s2));
+ }
+
+ if (sumSquares >= 0.00000001) {
+ correlation = (float) (2.0 * sumProducts / sumSquares);
+ }
+ 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;
+
+ // 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);
+
+ printf("first = %d, again at %d\n", first, again);
+ first = again;
+
+ // 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
+
+ // Generate correlation for every position.
+ numCorrelations = calculateCorrelations(&haystack[first], remaining,
+ needle, needleSize,
+ correlations, numCorrelations);
+
+ // Add higher harmonics mapped onto lower harmonics.
+ // This reinforces the "fundamental" echo.
+ const int numEchoes = 10;
+ for (int partial = 1; partial < numEchoes; partial++) {
+ for (int i = 0; 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++) {
+ 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) {
+ report->confidence = 0.0;
+ } else {
+ report->confidence = maxCorrelation / sumOfPeaks;
+ }
+
+ delete[] correlations;
+ delete[] harmonicSums;
+ return 0;
+}
+
+class AudioRecording
+{
+public:
+ AudioRecording() {
+ }
+ ~AudioRecording() {
+ delete[] mData;
+ }
+
+ void allocate(int maxFrames) {
+ delete[] mData;
+ mData = new float[maxFrames];
+ mMaxFrames = maxFrames;
+ }
+
+ // Write SHORT data from the first channel.
+ int write(int16_t *inputData, int inputChannelCount, int numFrames) {
+ // stop at end of buffer
+ if ((mFrameCounter + numFrames) > mMaxFrames) {
+ numFrames = mMaxFrames - mFrameCounter;
+ }
+ for (int i = 0; i < numFrames; i++) {
+ mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
+ }
+ return numFrames;
+ }
+
+ // Write FLOAT data from the first channel.
+ int write(float *inputData, int inputChannelCount, int numFrames) {
+ // stop at end of buffer
+ if ((mFrameCounter + numFrames) > mMaxFrames) {
+ numFrames = mMaxFrames - mFrameCounter;
+ }
+ for (int i = 0; i < numFrames; i++) {
+ mData[mFrameCounter++] = inputData[i * inputChannelCount];
+ }
+ return numFrames;
+ }
+
+ int size() {
+ return mFrameCounter;
+ }
+
+ float *getData() {
+ return mData;
+ }
+
+ int save(const char *fileName, bool writeShorts = true) {
+ int written = 0;
+ const int chunkSize = 64;
+ FILE *fid = fopen(fileName, "wb");
+ if (fid == NULL) {
+ return -errno;
+ }
+
+ if (writeShorts) {
+ int16_t buffer[chunkSize];
+ int32_t framesLeft = mFrameCounter;
+ int32_t cursor = 0;
+ while (framesLeft) {
+ int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize;
+ for (int i = 0; i < framesToWrite; i++) {
+ buffer[i] = (int16_t) (mData[cursor++] * 32767);
+ }
+ written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid);
+ framesLeft -= framesToWrite;
+ }
+ } else {
+ written = (int) fwrite(mData, sizeof(float), mFrameCounter, fid);
+ }
+ fclose(fid);
+ return written;
+ }
+
+private:
+ float *mData = nullptr;
+ int32_t mFrameCounter = 0;
+ int32_t mMaxFrames = 0;
+};
+
+// ====================================================================================
+class LoopbackProcessor {
+public:
+ virtual ~LoopbackProcessor() = default;
+
+
+ virtual void reset() {}
+
+ virtual void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) = 0;
+
+
+ virtual void report() = 0;
+
+ virtual void printStatus() {};
+
+ virtual bool isDone() {
+ return false;
+ }
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ int32_t getSampleRate() {
+ return mSampleRate;
+ }
+
+ // Measure peak amplitude of buffer.
+ static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
+ float peak = 0.0f;
+ for (int i = 0; i < numFrames; i++) {
+ float pos = fabs(*inputData);
+ if (pos > peak) {
+ peak = pos;
+ }
+ inputData += inputChannelCount;
+ }
+ return peak;
+ }
+
+
+private:
+ int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
+};
+
+class PeakDetector {
+public:
+ float process(float input) {
+ float output = mPrevious * mDecay;
+ if (input > output) {
+ output = input;
+ }
+ mPrevious = output;
+ return output;
+ }
+
+private:
+ float mDecay = 0.99f;
+ 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.
+ * Uses a state machine to cycle through various stages including:
+ *
+ */
+class EchoAnalyzer : public LoopbackProcessor {
+public:
+
+ EchoAnalyzer() : LoopbackProcessor() {
+ audioRecorder.allocate(2 * LOOPBACK_SAMPLE_RATE);
+ }
+
+ void reset() override {
+ mDownCounter = 200;
+ mLoopCounter = 0;
+ mMeasuredLoopGain = 0.0f;
+ mEchoGain = 1.0f;
+ mState = STATE_INITIAL_SILENCE;
+ }
+
+ virtual bool isDone() {
+ return mState == STATE_DONE;
+ }
+
+ void setGain(float gain) {
+ mEchoGain = gain;
+ }
+
+ float getGain() {
+ return mEchoGain;
+ }
+
+ void report() override {
+
+ printf("EchoAnalyzer ---------------\n");
+ printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain);
+ printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain);
+ printf(LOOPBACK_RESULT_TAG "frame.count = %d\n", mFrameCounter);
+ printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState);
+ if (mMeasuredLoopGain >= 0.9999) {
+ printf(" ERROR - clipping, turn down volume slightly\n");
+ } else {
+ const float *needle = s_Impulse;
+ int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
+ float *haystack = audioRecorder.getData();
+ int haystackSize = audioRecorder.size();
+ int result = measureLatencyFromEchos(haystack, haystackSize,
+ needle, needleSize,
+ &latencyReport);
+ if (latencyReport.confidence < 0.01) {
+ printf(" ERROR - confidence too low = %f\n", latencyReport.confidence);
+ } else {
+ double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate();
+ printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames);
+ printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis);
+ printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence);
+ }
+ }
+
+ {
+#define ECHO_FILENAME "/data/oboe_echo.raw"
+ int written = audioRecorder.save(ECHO_FILENAME);
+ printf("Echo wrote %d mono samples to %s on Android device\n", written, ECHO_FILENAME);
+ }
+ }
+
+ void printStatus() override {
+ printf("state = %d, echo gain = %f ", mState, mEchoGain);
+ }
+
+ static void sendImpulse(float *outputData, int outputChannelCount) {
+ for (float sample : s_Impulse) {
+ *outputData = sample;
+ outputData += outputChannelCount;
+ }
+ }
+
+ void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) override {
+ int channelsValid = std::min(inputChannelCount, outputChannelCount);
+ float peak = 0.0f;
+ int numWritten;
+ int numSamples;
+
+ echo_state_t nextState = mState;
+
+ switch (mState) {
+ case STATE_INITIAL_SILENCE:
+ // Output silence at the beginning.
+ numSamples = numFrames * outputChannelCount;
+ for (int i = 0; i < numSamples; i++) {
+ outputData[i] = 0;
+ }
+ if (mDownCounter-- <= 0) {
+ nextState = STATE_MEASURING_GAIN;
+ //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
+ mDownCounter = 8;
+ }
+ break;
+
+ case STATE_MEASURING_GAIN:
+ sendImpulse(outputData, outputChannelCount);
+ peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
+ // If we get several in a row then go to next state.
+ if (peak > mPulseThreshold) {
+ if (mDownCounter-- <= 0) {
+ nextState = STATE_WAITING_FOR_SILENCE;
+ //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
+ // mLoopCounter, peak);
+ mDownCounter = 8;
+ mMeasuredLoopGain = peak; // assumes original pulse amplitude is one
+ // Calculate gain that will give us a nice decaying echo.
+ mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
+ }
+ } else {
+ mDownCounter = 8;
+ }
+ break;
+
+ case STATE_WAITING_FOR_SILENCE:
+ // Output silence.
+ numSamples = numFrames * outputChannelCount;
+ for (int i = 0; i < numSamples; i++) {
+ outputData[i] = 0;
+ }
+ peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
+ // If we get several in a row then go to next state.
+ if (peak < mSilenceThreshold) {
+ if (mDownCounter-- <= 0) {
+ nextState = STATE_SENDING_PULSE;
+ //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
+ mDownCounter = 8;
+ }
+ } else {
+ mDownCounter = 8;
+ }
+ break;
+
+ case STATE_SENDING_PULSE:
+ audioRecorder.write(inputData, inputChannelCount, numFrames);
+ sendImpulse(outputData, outputChannelCount);
+ nextState = STATE_GATHERING_ECHOS;
+ //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter);
+ break;
+
+ case STATE_GATHERING_ECHOS:
+ numWritten = audioRecorder.write(inputData, inputChannelCount, numFrames);
+ peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
+ if (peak > mMeasuredLoopGain) {
+ mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly.
+ // Recalculate gain that will give us a nice decaying echo.
+ mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
+ }
+ // Echo input to output.
+ for (int i = 0; i < numFrames; i++) {
+ int ic;
+ for (ic = 0; ic < channelsValid; ic++) {
+ outputData[ic] = inputData[ic] * mEchoGain;
+ }
+ for (; ic < outputChannelCount; ic++) {
+ outputData[ic] = 0;
+ }
+ inputData += inputChannelCount;
+ outputData += outputChannelCount;
+ }
+ if (numWritten < numFrames) {
+ nextState = STATE_DONE;
+ //printf("%5d: switch to STATE_DONE\n", mLoopCounter);
+ }
+ break;
+
+ case STATE_DONE:
+ default:
+ break;
+ }
+
+ mState = nextState;
+ mLoopCounter++;
+ }
+
+private:
+
+ enum echo_state_t {
+ STATE_INITIAL_SILENCE,
+ STATE_MEASURING_GAIN,
+ STATE_WAITING_FOR_SILENCE,
+ STATE_SENDING_PULSE,
+ STATE_GATHERING_ECHOS,
+ STATE_DONE
+ };
+
+ int mDownCounter = 500;
+ int mLoopCounter = 0;
+ int mLoopStart = 1000;
+ float mPulseThreshold = 0.02f;
+ float mSilenceThreshold = 0.002f;
+ float mMeasuredLoopGain = 0.0f;
+ float mDesiredEchoGain = 0.95f;
+ float mEchoGain = 1.0f;
+ echo_state_t mState = STATE_INITIAL_SILENCE;
+ int32_t mFrameCounter = 0;
+
+ AudioRecording audioRecorder;
+ LatencyReport latencyReport;
+ PeakDetector mPeakDetector;
+};
+
+
+// ====================================================================================
+/**
+ * Output a steady sinewave and analyze the return signal.
+ *
+ * Use a cosine transform to measure the predicted magnitude and relative phase of the
+ * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
+ */
+class SineAnalyzer : public LoopbackProcessor {
+public:
+
+ void report() override {
+ printf("SineAnalyzer ------------------\n");
+ printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude);
+ printf(LOOPBACK_RESULT_TAG "sine.magnitude = %7.5f\n", mMagnitude);
+ printf(LOOPBACK_RESULT_TAG "phase.offset = %7.5f\n", mPhaseOffset);
+ printf(LOOPBACK_RESULT_TAG "ref.phase = %7.5f\n", mPhase);
+ printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
+ printf(LOOPBACK_RESULT_TAG "sine.period = %6d\n", mPeriod);
+ printf(LOOPBACK_RESULT_TAG "test.state = %6d\n", mState);
+ printf(LOOPBACK_RESULT_TAG "frame.count = %6d\n", mFrameCounter);
+ // Did we ever get a lock?
+ bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
+ if (!gotLock) {
+ printf("ERROR - failed to lock on reference sine tone\n");
+ } else {
+ // Only print if meaningful.
+ printf(LOOPBACK_RESULT_TAG "glitch.count = %6d\n", mGlitchCount);
+ }
+ }
+
+ void printStatus() override {
+ printf(" state = %d, glitches = %d,", mState, mGlitchCount);
+ }
+
+ double calculateMagnitude(double *phasePtr = NULL) {
+ if (mFramesAccumulated == 0) {
+ return 0.0;
+ }
+ double sinMean = mSinAccumulator / mFramesAccumulated;
+ double cosMean = mCosAccumulator / mFramesAccumulated;
+ double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
+ if( phasePtr != NULL )
+ {
+ double phase = M_PI_2 - atan2( sinMean, cosMean );
+ *phasePtr = phase;
+ }
+ return magnitude;
+ }
+
+ /**
+ * @param inputData contains microphone data with sine signal feedback
+ * @param outputData contains the reference sine wave
+ */
+ void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) override {
+ float sample;
+ float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
+ if (peak > mPeakAmplitude) {
+ mPeakAmplitude = peak;
+ }
+
+ for (int i = 0; i < numFrames; i++) {
+ float sample = inputData[i * inputChannelCount];
+
+ float sinOut = sinf(mPhase);
+
+ switch (mState) {
+ case STATE_IMMUNE:
+ case STATE_WAITING_FOR_SIGNAL:
+ break;
+ case STATE_WAITING_FOR_LOCK:
+ mSinAccumulator += sample * sinOut;
+ mCosAccumulator += sample * cosf(mPhase);
+ mFramesAccumulated++;
+ // Must be a multiple of the period or the calculation will not be accurate.
+ if (mFramesAccumulated == mPeriod * 4) {
+ mPhaseOffset = 0.0;
+ mMagnitude = calculateMagnitude(&mPhaseOffset);
+ if (mMagnitude > mThreshold) {
+ if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) {
+ mState = STATE_LOCKED;
+ //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter);
+ }
+ mPreviousPhaseOffset = mPhaseOffset;
+ }
+ resetAccumulator();
+ }
+ break;
+
+ case STATE_LOCKED: {
+ // Predict next sine value
+ float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude;
+ // printf(" predicted = %f, actual = %f\n", predicted, sample);
+
+ float diff = predicted - sample;
+ if (fabs(diff) > mTolerance) {
+ mGlitchCount++;
+ //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
+ // mFrameCounter, mGlitchCount, predicted, sample);
+ mState = STATE_IMMUNE;
+ //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
+ mDownCounter = mPeriod; // Set duration of IMMUNE state.
+ }
+ } break;
+ }
+
+ // Output sine wave so we can measure it.
+ outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
+ + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
+ // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
+
+ // advance and wrap phase
+ mPhase += mPhaseIncrement;
+ if (mPhase > M_PI) {
+ mPhase -= (2.0 * M_PI);
+ }
+
+ mFrameCounter++;
+ }
+
+ // Do these once per buffer.
+ switch (mState) {
+ case STATE_IMMUNE:
+ mDownCounter -= numFrames;
+ if (mDownCounter <= 0) {
+ mState = STATE_WAITING_FOR_SIGNAL;
+ //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
+ }
+ break;
+ case STATE_WAITING_FOR_SIGNAL:
+ if (peak > mThreshold) {
+ mState = STATE_WAITING_FOR_LOCK;
+ //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
+ resetAccumulator();
+ }
+ break;
+ case STATE_WAITING_FOR_LOCK:
+ case STATE_LOCKED:
+ break;
+ }
+
+ }
+
+ void resetAccumulator() {
+ mFramesAccumulated = 0;
+ mSinAccumulator = 0.0;
+ mCosAccumulator = 0.0;
+ }
+
+ void reset() override {
+ mGlitchCount = 0;
+ mState = STATE_IMMUNE;
+ mPhaseIncrement = 2.0 * M_PI / mPeriod;
+ printf("phaseInc = %f for period %d\n", mPhaseIncrement, mPeriod);
+ resetAccumulator();
+ }
+
+private:
+
+ enum sine_state_t {
+ STATE_IMMUNE,
+ STATE_WAITING_FOR_SIGNAL,
+ STATE_WAITING_FOR_LOCK,
+ STATE_LOCKED
+ };
+
+ int mPeriod = 79;
+ double mPhaseIncrement = 0.0;
+ double mPhase = 0.0;
+ double mPhaseOffset = 0.0;
+ double mPreviousPhaseOffset = 0.0;
+ double mMagnitude = 0.0;
+ double mThreshold = 0.005;
+ double mTolerance = 0.01;
+ int32_t mFramesAccumulated = 0;
+ double mSinAccumulator = 0.0;
+ double mCosAccumulator = 0.0;
+ int32_t mGlitchCount = 0;
+ double mPeakAmplitude = 0.0;
+ int mDownCounter = 4000;
+ int32_t mFrameCounter = 0;
+ float mOutputAmplitude = 0.75;
+
+ int32_t mZeroCrossings = 0;
+
+ PseudoRandom mWhiteNoise;
+ float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
+
+ sine_state_t mState = STATE_IMMUNE;
+};
+
+
+#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 43eb61c..67dafa9 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-// Play an impulse and then record it.
-// Measure the round trip latency.
+// Audio loopback tests to measure the round trip latency and glitches.
+#include <algorithm>
#include <assert.h>
#include <cctype>
#include <errno.h>
@@ -28,207 +28,41 @@
#include <unistd.h>
#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
-#define INPUT_PEAK_THRESHOLD 0.1f
-#define SILENCE_FRAMES 10000
+#include "AAudioSimplePlayer.h"
+#include "AAudioSimpleRecorder.h"
+#include "AAudioExampleUtils.h"
+#include "LoopbackAnalyzer.h"
+
+// Tag for machine readable results as property = value pairs
+#define RESULT_TAG "RESULT: "
#define SAMPLE_RATE 48000
-#define NUM_SECONDS 7
+#define NUM_SECONDS 5
+#define NUM_INPUT_CHANNELS 1
#define FILENAME "/data/oboe_input.raw"
+#define APP_VERSION "0.1.22"
-#define NANOS_PER_MICROSECOND ((int64_t)1000)
-#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
-#define MILLIS_PER_SECOND 1000
-#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
-
-class AudioRecorder
-{
-public:
- AudioRecorder() {
- }
- ~AudioRecorder() {
- delete[] mData;
- }
-
- void allocate(int maxFrames) {
- delete[] mData;
- mData = new float[maxFrames];
- mMaxFrames = maxFrames;
- }
-
- void record(int16_t *inputData, int inputChannelCount, int numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
- }
- }
-
- void record(float *inputData, int inputChannelCount, int numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount];
- }
- }
-
- int save(const char *fileName) {
- FILE *fid = fopen(fileName, "wb");
- if (fid == NULL) {
- return errno;
- }
- int written = fwrite(mData, sizeof(float), mFrameCounter, fid);
- fclose(fid);
- return written;
- }
-
-private:
- float *mData = NULL;
- int32_t mFrameCounter = 0;
- int32_t mMaxFrames = 0;
-};
-
-// ====================================================================================
-// ========================= Loopback Processor =======================================
-// ====================================================================================
-class LoopbackProcessor {
-public:
-
- // Calculate mean and standard deviation.
- double calculateAverageLatency(double *deviation) {
- if (mLatencyCount <= 0) {
- return -1.0;
- }
- double sum = 0.0;
- for (int i = 0; i < mLatencyCount; i++) {
- sum += mLatencyArray[i];
- }
- double average = sum / mLatencyCount;
- sum = 0.0;
- for (int i = 0; i < mLatencyCount; i++) {
- double error = average - mLatencyArray[i];
- sum += error * error; // squared
- }
- *deviation = sqrt(sum / mLatencyCount);
- return average;
- }
-
- float getMaxAmplitude() const { return mMaxAmplitude; }
- int getMeasurementCount() const { return mLatencyCount; }
- float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
-
- // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
- void process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) {
- (void) outputChannelCount;
-
- // Measure peak and average amplitude.
- for (int i = 0; i < numFrames; i++) {
- float sample = inputData[i * inputChannelCount];
- if (sample > mMaxAmplitude) {
- mMaxAmplitude = sample;
- }
- if (sample < 0) {
- sample = 0 - sample;
- }
- mAmplitudeTotal += sample;
- mAmplitudeCount++;
- }
-
- // Clear output.
- memset(outputData, 0, numFrames * outputChannelCount * sizeof(float));
-
- // Wait a while between hearing the pulse and starting a new one.
- if (mState == STATE_SILENT) {
- mCounter += numFrames;
- if (mCounter > SILENCE_FRAMES) {
- //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter);
- // copy impulse
- for (float sample : mImpulse) {
- *outputData = sample;
- outputData += outputChannelCount;
- }
- mState = STATE_LISTENING;
- mCounter = 0;
- }
- }
- // Start listening as soon as we send the impulse.
- if (mState == STATE_LISTENING) {
- for (int i = 0; i < numFrames; i++) {
- float sample = inputData[i * inputChannelCount];
- if (sample >= INPUT_PEAK_THRESHOLD) {
- mLatencyArray[mLatencyCount++] = mCounter;
- if (mLatencyCount >= MAX_LATENCY_VALUES) {
- mState = STATE_DONE;
- } else {
- mState = STATE_SILENT;
- }
- mCounter = 0;
- break;
- } else {
- mCounter++;
- }
- }
- }
- }
-
- void echo(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) {
- int channelsValid = (inputChannelCount < outputChannelCount)
- ? inputChannelCount : outputChannelCount;
- for (int i = 0; i < numFrames; i++) {
- int ic;
- for (ic = 0; ic < channelsValid; ic++) {
- outputData[ic] = inputData[ic];
- }
- for (ic = 0; ic < outputChannelCount; ic++) {
- outputData[ic] = 0;
- }
- inputData += inputChannelCount;
- outputData += outputChannelCount;
- }
- }
-private:
- enum {
- STATE_SILENT,
- STATE_LISTENING,
- STATE_DONE
- };
-
- enum {
- MAX_LATENCY_VALUES = 64
- };
-
- int mState = STATE_SILENT;
- int32_t mCounter = 0;
- int32_t mLatencyArray[MAX_LATENCY_VALUES];
- int32_t mLatencyCount = 0;
- float mMaxAmplitude = 0;
- float mAmplitudeTotal = 0;
- int32_t mAmplitudeCount = 0;
- static const float mImpulse[5];
-};
-
-const float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f};
-
-// TODO make this a class that manages its own buffer allocation
struct LoopbackData {
- AAudioStream *inputStream = nullptr;
- int32_t inputFramesMaximum = 0;
- int16_t *inputData = nullptr;
- float *conversionBuffer = nullptr;
- int32_t actualInputChannelCount = 0;
- int32_t actualOutputChannelCount = 0;
- int32_t inputBuffersToDiscard = 10;
+ AAudioStream *inputStream = nullptr;
+ int32_t inputFramesMaximum = 0;
+ int16_t *inputData = nullptr;
+ int16_t peakShort = 0;
+ float *conversionBuffer = nullptr;
+ int32_t actualInputChannelCount = 0;
+ int32_t actualOutputChannelCount = 0;
+ int32_t inputBuffersToDiscard = 10;
+ int32_t minNumFrames = INT32_MAX;
+ int32_t maxNumFrames = 0;
+ bool isDone = false;
- aaudio_result_t inputError;
- LoopbackProcessor loopbackProcessor;
- AudioRecorder audioRecorder;
+ aaudio_result_t inputError = AAUDIO_OK;
+ aaudio_result_t outputError = AAUDIO_OK;
+
+ SineAnalyzer sineAnalyzer;
+ EchoAnalyzer echoAnalyzer;
+ AudioRecording audioRecorder;
+ LoopbackProcessor *loopbackProcessor;
};
static void convertPcm16ToFloat(const int16_t *source,
@@ -251,6 +85,7 @@
int32_t numFrames
) {
(void) outputStream;
+ aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_CONTINUE;
LoopbackData *myData = (LoopbackData *) userData;
float *outputData = (float *) audioData;
@@ -262,6 +97,13 @@
return AAUDIO_CALLBACK_RESULT_STOP;
}
+ if (numFrames > myData->maxNumFrames) {
+ myData->maxNumFrames = numFrames;
+ }
+ if (numFrames < myData->minNumFrames) {
+ myData->minNumFrames = numFrames;
+ }
+
if (myData->inputBuffersToDiscard > 0) {
// Drain the input.
do {
@@ -269,6 +111,8 @@
numFrames, 0);
if (framesRead < 0) {
myData->inputError = framesRead;
+ printf("ERROR in read = %d", framesRead);
+ result = AAUDIO_CALLBACK_RESULT_STOP;
} else if (framesRead > 0) {
myData->inputBuffersToDiscard--;
}
@@ -278,39 +122,62 @@
numFrames, 0);
if (framesRead < 0) {
myData->inputError = framesRead;
+ printf("ERROR in read = %d", framesRead);
+ result = AAUDIO_CALLBACK_RESULT_STOP;
} else if (framesRead > 0) {
- // Process valid input data.
- myData->audioRecorder.record(myData->inputData,
- myData->actualInputChannelCount,
- framesRead);
+
+ myData->audioRecorder.write(myData->inputData,
+ myData->actualInputChannelCount,
+ numFrames);
int32_t numSamples = framesRead * myData->actualInputChannelCount;
convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
- myData->loopbackProcessor.process(myData->conversionBuffer,
+ myData->loopbackProcessor->process(myData->conversionBuffer,
myData->actualInputChannelCount,
outputData,
myData->actualOutputChannelCount,
framesRead);
+ myData->isDone = myData->loopbackProcessor->isDone();
+ if (myData->isDone) {
+ result = AAUDIO_CALLBACK_RESULT_STOP;
+ }
}
}
- return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ return result;
+}
+
+static void MyErrorCallbackProc(
+ AAudioStream *stream __unused,
+ void *userData __unused,
+ aaudio_result_t error)
+{
+ printf("Error Callback, error: %d\n",(int)error);
+ LoopbackData *myData = (LoopbackData *) userData;
+ myData->outputError = error;
}
static void usage() {
- printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n");
- printf(" -b{burstsPerBuffer} for example 2 for double buffered\n");
- printf(" -p{outputPerfMode} set output AAUDIO_PERFORMANCE_MODE*\n");
- printf(" -P{inputPerfMode} set input AAUDIO_PERFORMANCE_MODE*\n");
+ printf("loopback: -n{numBursts} -p{outPerf} -P{inPerf} -t{test} -g{gain} -f{freq}\n");
+ printf(" -c{inputChannels}\n");
+ printf(" -f{freq} sine frequency\n");
+ printf(" -g{gain} recirculating loopback gain\n");
+ printf(" -m enable MMAP mode\n");
+ printf(" -n{numBursts} buffer size, for example 2 for double buffered\n");
+ printf(" -p{outPerf} set output AAUDIO_PERFORMANCE_MODE*\n");
+ printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n");
printf(" n for _NONE\n");
printf(" l for _LATENCY\n");
printf(" p for _POWER_SAVING;\n");
+ printf(" -t{test} select test mode\n");
+ printf(" m for sine magnitude\n");
+ printf(" e for echo latency (default)\n");
printf("For example: loopback -b2 -pl -Pn\n");
}
static aaudio_performance_mode_t parsePerformanceMode(char c) {
- aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE;
+ aaudio_performance_mode_t mode = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
c = tolower(c);
switch (c) {
case 'n':
@@ -323,208 +190,281 @@
mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
break;
default:
- printf("ERROR invalue performance mode %c\n", c);
+ printf("ERROR in value performance mode %c\n", c);
break;
}
return mode;
}
+enum {
+ TEST_SINE_MAGNITUDE = 0,
+ TEST_ECHO_LATENCY,
+};
+
+static int parseTestMode(char c) {
+ int testMode = TEST_ECHO_LATENCY;
+ c = tolower(c);
+ switch (c) {
+ case 'm':
+ testMode = TEST_SINE_MAGNITUDE;
+ break;
+ case 'e':
+ testMode = TEST_ECHO_LATENCY;
+ break;
+ default:
+ printf("ERROR in value test mode %c\n", c);
+ break;
+ }
+ return testMode;
+}
+
+void printAudioGraph(AudioRecording &recording, int numSamples) {
+ int32_t start = recording.size() / 2;
+ int32_t end = start + numSamples;
+ if (end >= recording.size()) {
+ end = recording.size() - 1;
+ }
+ float *data = recording.getData();
+ // Normalize data so we can see it better.
+ float maxSample = 0.01;
+ for (int32_t i = start; i < end; i++) {
+ float samplePos = fabs(data[i]);
+ if (samplePos > maxSample) {
+ maxSample = samplePos;
+ }
+ }
+ float gain = 0.98f / maxSample;
+ for (int32_t i = start; i < end; i++) {
+ float sample = data[i];
+ printf("%5.3f ", sample); // actual value
+ sample *= gain;
+ printAudioScope(sample);
+ }
+}
+
+
// ====================================================================================
// TODO break up this large main() function into smaller functions
int main(int argc, const char **argv)
{
- aaudio_result_t result = AAUDIO_OK;
- LoopbackData loopbackData;
- AAudioStream *outputStream = nullptr;
- const int requestedInputChannelCount = 1;
- const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
- const int requestedSampleRate = SAMPLE_RATE;
- int actualSampleRate = 0;
+ AAudioArgsParser argParser;
+ AAudioSimplePlayer player;
+ AAudioSimpleRecorder recorder;
+ LoopbackData loopbackData;
+ AAudioStream *outputStream = nullptr;
+
+ aaudio_result_t result = AAUDIO_OK;
+ aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ int requestedInputChannelCount = NUM_INPUT_CHANNELS;
+ const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
+ int actualSampleRate = 0;
const aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16;
const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
- aaudio_format_t actualInputFormat;
- aaudio_format_t actualOutputFormat;
+ aaudio_format_t actualInputFormat;
+ aaudio_format_t actualOutputFormat;
+ aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+ aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
- const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
- //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
- aaudio_sharing_mode_t actualSharingMode;
+ int testMode = TEST_ECHO_LATENCY;
+ double gain = 1.0;
- AAudioStreamBuilder *builder = nullptr;
aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
int32_t framesPerBurst = 0;
float *outputData = NULL;
double deviation;
double latency;
- aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
- aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-
int32_t burstsPerBuffer = 1; // single buffered
- for (int i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (arg[0] == '-') {
- char option = arg[1];
- switch (option) {
- case 'b':
- burstsPerBuffer = atoi(&arg[2]);
- break;
- case 'p':
- outputPerformanceLevel = parsePerformanceMode(arg[2]);
- break;
- case 'P':
- inputPerformanceLevel = parsePerformanceMode(arg[2]);
- break;
- default:
- usage();
- break;
- }
- } else {
- break;
- }
- }
-
- loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
-
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, NULL, _IONBF, (size_t) 0);
- printf("%s - Audio loopback using AAudio\n", argv[0]);
+ printf("%s - Audio loopback using AAudio V" APP_VERSION "\n", argv[0]);
- // Use an AAudioStreamBuilder to contain requested parameters.
- result = AAudio_createStreamBuilder(&builder);
- if (result < 0) {
- goto finish;
+ for (int i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (argParser.parseArg(arg)) {
+ // Handle options that are not handled by the ArgParser
+ if (arg[0] == '-') {
+ char option = arg[1];
+ switch (option) {
+ case 'C':
+ requestedInputChannelCount = atoi(&arg[2]);
+ break;
+ case 'g':
+ gain = atof(&arg[2]);
+ break;
+ case 'P':
+ inputPerformanceLevel = parsePerformanceMode(arg[2]);
+ break;
+ case 'X':
+ requestedInputSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
+ break;
+ case 't':
+ testMode = parseTestMode(arg[2]);
+ break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ } else {
+ usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
}
- // Request common stream properties.
- AAudioStreamBuilder_setSampleRate(builder, requestedSampleRate);
- AAudioStreamBuilder_setFormat(builder, requestedInputFormat);
- AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
-
- // Open the input stream.
- AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
- AAudioStreamBuilder_setPerformanceMode(builder, inputPerformanceLevel);
- AAudioStreamBuilder_setChannelCount(builder, requestedInputChannelCount);
-
- result = AAudioStreamBuilder_openStream(builder, &loopbackData.inputStream);
- printf("AAudioStreamBuilder_openStream(input) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
- if (result < 0) {
- goto finish;
+ if (inputPerformanceLevel < 0) {
+ printf("illegal inputPerformanceLevel = %d\n", inputPerformanceLevel);
+ exit(EXIT_FAILURE);
}
- // Create an output stream using the Builder.
- AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
- AAudioStreamBuilder_setFormat(builder, requestedOutputFormat);
- AAudioStreamBuilder_setPerformanceMode(builder, outputPerformanceLevel);
- AAudioStreamBuilder_setChannelCount(builder, requestedOutputChannelCount);
- AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &loopbackData);
+ int32_t requestedDuration = argParser.getDurationSeconds();
+ int32_t recordingDuration = std::min(60, requestedDuration);
+ loopbackData.audioRecorder.allocate(recordingDuration * SAMPLE_RATE);
- result = AAudioStreamBuilder_openStream(builder, &outputStream);
- printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
+ switch(testMode) {
+ case TEST_SINE_MAGNITUDE:
+ loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
+ break;
+ case TEST_ECHO_LATENCY:
+ loopbackData.echoAnalyzer.setGain(gain);
+ loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
+ break;
+ default:
+ exit(1);
+ break;
+ }
+
+ printf("OUTPUT stream ----------------------------------------\n");
+ argParser.setFormat(requestedOutputFormat);
+ result = player.open(argParser, MyDataCallbackProc, MyErrorCallbackProc, &loopbackData);
if (result != AAUDIO_OK) {
+ fprintf(stderr, "ERROR - player.open() returned %d\n", result);
goto finish;
}
+ outputStream = player.getStream();
+ argParser.compareWithStream(outputStream);
- printf("Stream INPUT ---------------------\n");
- loopbackData.actualInputChannelCount = AAudioStream_getChannelCount(loopbackData.inputStream);
- printf(" channelCount: requested = %d, actual = %d\n", requestedInputChannelCount,
- loopbackData.actualInputChannelCount);
- printf(" framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream));
+ actualOutputFormat = AAudioStream_getFormat(outputStream);
+ assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
- actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
- printf(" dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat);
- assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
-
- printf("Stream OUTPUT ---------------------\n");
- // Check to see what kind of stream we actually got.
- actualSampleRate = AAudioStream_getSampleRate(outputStream);
- printf(" sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
-
- loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream);
- printf(" channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount,
- loopbackData.actualOutputChannelCount);
-
- actualSharingMode = AAudioStream_getSharingMode(outputStream);
- printf(" sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode);
+ printf("INPUT stream ----------------------------------------\n");
+ // Use different parameters for the input.
+ argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED);
+ argParser.setFormat(requestedInputFormat);
+ argParser.setPerformanceMode(inputPerformanceLevel);
+ argParser.setChannelCount(requestedInputChannelCount);
+ argParser.setSharingMode(requestedInputSharingMode);
+ result = recorder.open(argParser);
+ if (result != AAUDIO_OK) {
+ fprintf(stderr, "ERROR - recorder.open() returned %d\n", result);
+ goto finish;
+ }
+ loopbackData.inputStream = recorder.getStream();
+ argParser.compareWithStream(loopbackData.inputStream);
// This is the number of frames that are read in one chunk by a DMA controller
// or a DSP or a mixer.
framesPerBurst = AAudioStream_getFramesPerBurst(outputStream);
- printf(" framesPerBurst = %d\n", framesPerBurst);
- printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
+ actualInputFormat = AAudioStream_getFormat(outputStream);
+ assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
- actualOutputFormat = AAudioStream_getFormat(outputStream);
- printf(" dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat);
- assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
+
+ loopbackData.actualInputChannelCount = recorder.getChannelCount();
+ loopbackData.actualOutputChannelCount = player.getChannelCount();
// Allocate a buffer for the audio data.
loopbackData.inputFramesMaximum = 32 * framesPerBurst;
+ loopbackData.inputBuffersToDiscard = 100;
- loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount];
+ loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum
+ * loopbackData.actualInputChannelCount];
loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
loopbackData.actualInputChannelCount];
- result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst);
- if (result < 0) { // may be positive buffer size
- fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result);
- goto finish;
- }
- printf("AAudioStream_setBufferSize() actual = %d\n",result);
+ loopbackData.loopbackProcessor->reset();
- // Start output first so input stream runs low.
- result = AAudioStream_requestStart(outputStream);
+ result = recorder.start();
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
+ printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
+ result, AAudio_convertResultToText(result));
goto finish;
}
- result = AAudioStream_requestStart(loopbackData.inputStream);
+ result = player.start();
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
+ printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
+ result, AAudio_convertResultToText(result));
goto finish;
}
printf("------- sleep while the callback runs --------------\n");
fflush(stdout);
- sleep(NUM_SECONDS);
-
+ for (int i = requestedDuration; i > 0 ; i--) {
+ if (loopbackData.inputError != AAUDIO_OK) {
+ printf(" ERROR on input stream\n");
+ break;
+ } else if (loopbackData.outputError != AAUDIO_OK) {
+ printf(" ERROR on output stream\n");
+ break;
+ } else if (loopbackData.isDone) {
+ printf(" test says it is done!\n");
+ break;
+ } else {
+ sleep(1);
+ printf("%4d: ", i);
+ loopbackData.loopbackProcessor->printStatus();
+ int64_t framesWritten = AAudioStream_getFramesWritten(loopbackData.inputStream);
+ int64_t framesRead = AAudioStream_getFramesRead(loopbackData.inputStream);
+ printf(" input written = %lld, read %lld, xruns = %d\n",
+ (long long) framesWritten,
+ (long long) framesRead,
+ AAudioStream_getXRunCount(outputStream)
+ );
+ }
+ }
printf("input error = %d = %s\n",
loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
printf("AAudioStream_getXRunCount %d\n", AAudioStream_getXRunCount(outputStream));
- printf("framesRead = %d\n", (int) AAudioStream_getFramesRead(outputStream));
- printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream));
+ printf("framesRead = %8d\n", (int) AAudioStream_getFramesRead(outputStream));
+ printf("framesWritten = %8d\n", (int) AAudioStream_getFramesWritten(outputStream));
+ printf("min numFrames = %8d\n", (int) loopbackData.minNumFrames);
+ printf("max numFrames = %8d\n", (int) loopbackData.maxNumFrames);
- latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation);
- printf("measured peak = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude());
- printf("threshold = %8.5f\n", INPUT_PEAK_THRESHOLD);
- printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude());
- printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount());
- printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation);
- printf("measured latency = %8.2f msec <===== !!\n", (1000.0 * latency / actualSampleRate));
+ if (loopbackData.inputError == AAUDIO_OK) {
+ if (testMode == TEST_SINE_MAGNITUDE) {
+ printAudioGraph(loopbackData.audioRecorder, 200);
+ }
+ loopbackData.loopbackProcessor->report();
+ }
{
int written = loopbackData.audioRecorder.save(FILENAME);
- printf("wrote %d samples to %s\n", written, FILENAME);
+ printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME);
}
finish:
- AAudioStream_close(outputStream);
- AAudioStream_close(loopbackData.inputStream);
+ player.close();
+ recorder.close();
delete[] loopbackData.conversionBuffer;
delete[] loopbackData.inputData;
delete[] outputData;
- AAudioStreamBuilder_delete(builder);
- printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+ printf(RESULT_TAG "error = %d = %s\n", result, AAudio_convertResultToText(result));
+ if ((result != AAUDIO_OK)) {
+ printf("error %d = %s\n", result, AAudio_convertResultToText(result));
+ return EXIT_FAILURE;
+ } else {
+ printf("SUCCESS\n");
+ return EXIT_SUCCESS;
+ }
}
diff --git a/media/libaaudio/examples/loopback/src/loopback.sh b/media/libaaudio/examples/loopback/src/loopback.sh
new file mode 100644
index 0000000..bc63125
--- /dev/null
+++ b/media/libaaudio/examples/loopback/src/loopback.sh
@@ -0,0 +1,14 @@
+#!/system/bin/sh
+# Run a loopback test in the background after a delay.
+# To run the script enter:
+# adb shell "nohup sh /data/loopback.sh &"
+
+SLEEP_TIME=10
+TEST_COMMAND="aaudio_loopback -pl -Pl -C1 -n2 -m2 -tm -d5"
+
+echo "Plug in USB Mir and Fun Plug."
+echo "Test will start in ${SLEEP_TIME} seconds: ${TEST_COMMAND}"
+sleep ${SLEEP_TIME}
+date > /data/loopreport.txt
+${TEST_COMMAND} >> /data/loopreport.txt
+date >> /data/loopreport.txt
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
new file mode 100644
index 0000000..46bc99e
--- /dev/null
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2017 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 AAUDIO_EXAMPLE_ARGS_PARSER_H
+#define AAUDIO_EXAMPLE_ARGS_PARSER_H
+
+#include <cctype>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+#include <AAudioExampleUtils.h>
+
+// TODO use this as a base class within AAudio
+class AAudioParameters {
+public:
+
+ /**
+ * This is also known as samplesPerFrame.
+ */
+ int32_t getChannelCount() const {
+ return mChannelCount;
+ }
+
+ void setChannelCount(int32_t channelCount) {
+ mChannelCount = channelCount;
+ }
+
+ int32_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ aaudio_format_t getFormat() const {
+ return mFormat;
+ }
+
+ void setFormat(aaudio_format_t format) {
+ mFormat = format;
+ }
+
+ aaudio_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ void setSharingMode(aaudio_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ }
+
+ int32_t getBufferCapacity() const {
+ return mBufferCapacity;
+ }
+
+ void setBufferCapacity(int32_t frames) {
+ mBufferCapacity = frames;
+ }
+
+ int32_t getPerformanceMode() const {
+ return mPerformanceMode;
+ }
+
+ void setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+ mPerformanceMode = performanceMode;
+ }
+
+ int32_t getDeviceId() const {
+ return mDeviceId;
+ }
+
+ void setDeviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ }
+
+ int32_t getNumberOfBursts() const {
+ return mNumberOfBursts;
+ }
+
+ void setNumberOfBursts(int32_t numBursts) {
+ mNumberOfBursts = numBursts;
+ }
+
+ /**
+ * Apply these parameters to a stream builder.
+ * @param builder
+ */
+ void applyParameters(AAudioStreamBuilder *builder) const {
+ AAudioStreamBuilder_setChannelCount(builder, mChannelCount);
+ AAudioStreamBuilder_setFormat(builder, mFormat);
+ AAudioStreamBuilder_setSampleRate(builder, mSampleRate);
+ AAudioStreamBuilder_setBufferCapacityInFrames(builder, mBufferCapacity);
+ AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
+ AAudioStreamBuilder_setSharingMode(builder, mSharingMode);
+ AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
+ }
+
+private:
+ int32_t mChannelCount = AAUDIO_UNSPECIFIED;
+ aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+
+ int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceId = AAUDIO_UNSPECIFIED;
+ aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+
+ int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED;
+};
+
+class AAudioArgsParser : public AAudioParameters {
+public:
+ AAudioArgsParser() = default;
+ ~AAudioArgsParser() = default;
+
+ enum {
+ DEFAULT_DURATION_SECONDS = 5
+ };
+
+ /**
+ * @param arg
+ * @return true if the argument was not handled
+ */
+ bool parseArg(const char *arg) {
+ bool unrecognized = false;
+ if (arg[0] == '-') {
+ char option = arg[1];
+ switch (option) {
+ case 'b':
+ setBufferCapacity(atoi(&arg[2]));
+ break;
+ case 'c':
+ setChannelCount(atoi(&arg[2]));
+ break;
+ case 'd':
+ mDurationSeconds = atoi(&arg[2]);
+ break;
+ case 'm': {
+ aaudio_policy_t policy = AAUDIO_POLICY_AUTO;
+ if (strlen(arg) > 2) {
+ policy = atoi(&arg[2]);
+ }
+ AAudio_setMMapPolicy(policy);
+ } break;
+ case 'n':
+ setNumberOfBursts(atoi(&arg[2]));
+ break;
+ case 'p':
+ setPerformanceMode(parsePerformanceMode(arg[2]));
+ break;
+ case 'r':
+ setSampleRate(atoi(&arg[2]));
+ break;
+ case 'x':
+ setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+ break;
+ default:
+ unrecognized = true;
+ break;
+ }
+ }
+ return unrecognized;
+ }
+
+ /**
+ *
+ * @param argc
+ * @param argv
+ * @return true if an unrecognized argument was passed
+ */
+ bool parseArgs(int argc, const char **argv) {
+ for (int i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (parseArg(arg)) {
+ usage();
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+ static void usage() {
+ printf("-c{channels} -d{duration} -m -n{burstsPerBuffer} -p{perfMode} -r{rate} -x\n");
+ printf(" Default values are UNSPECIFIED unless otherwise stated.\n");
+ printf(" -b{bufferCapacity} frames\n");
+ printf(" -c{channels} for example 2 for stereo\n");
+ printf(" -d{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+ printf(" -m{0|1|2|3} set MMAP policy\n");
+ printf(" 0 = _UNSPECIFIED, default\n");
+ printf(" 1 = _NEVER\n");
+ printf(" 2 = _AUTO, also if -m is used with no number\n");
+ printf(" 3 = _ALWAYS\n");
+ printf(" -n{numberOfBursts} for setBufferSize\n");
+ printf(" -p{performanceMode} set output AAUDIO_PERFORMANCE_MODE*, default NONE\n");
+ printf(" n for _NONE\n");
+ printf(" l for _LATENCY\n");
+ printf(" p for _POWER_SAVING;\n");
+ printf(" -r{sampleRate} for example 44100\n");
+ printf(" -x to use EXCLUSIVE mode\n");
+ }
+
+ static aaudio_performance_mode_t parsePerformanceMode(char c) {
+ aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE;
+ switch (c) {
+ case 'n':
+ mode = AAUDIO_PERFORMANCE_MODE_NONE;
+ break;
+ case 'l':
+ mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+ break;
+ case 'p':
+ mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
+ break;
+ default:
+ printf("ERROR invalid performance mode %c\n", c);
+ break;
+ }
+ return mode;
+ }
+
+ /**
+ * Print stream parameters in comparison with requested values.
+ * @param stream
+ */
+ void compareWithStream(AAudioStream *stream) {
+
+ printf(" DeviceId: requested = %d, actual = %d\n",
+ getDeviceId(), AAudioStream_getDeviceId(stream));
+
+ aaudio_stream_state_t state = AAudioStream_getState(stream);
+ printf(" State: %s\n", AAudio_convertStreamStateToText(state));
+
+ // Check to see what kind of stream we actually got.
+ printf(" SampleRate: requested = %d, actual = %d\n",
+ getSampleRate(), AAudioStream_getSampleRate(stream));
+
+ printf(" ChannelCount: requested = %d, actual = %d\n",
+ getChannelCount(), AAudioStream_getChannelCount(stream));
+
+ printf(" DataFormat: requested = %d, actual = %d\n",
+ getFormat(), AAudioStream_getFormat(stream));
+
+ int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
+ int32_t sizeFrames = AAudioStream_getBufferSizeInFrames(stream);
+ printf(" Buffer: burst = %d\n", framesPerBurst);
+ if (framesPerBurst > 0) {
+ printf(" Buffer: size = %d = (%d * %d) + %d\n",
+ sizeFrames,
+ (sizeFrames / framesPerBurst),
+ framesPerBurst,
+ (sizeFrames % framesPerBurst));
+ }
+ printf(" Capacity: requested = %d, actual = %d\n", getBufferCapacity(),
+ AAudioStream_getBufferCapacityInFrames(stream));
+
+ printf(" SharingMode: requested = %s, actual = %s\n",
+ getSharingModeText(getSharingMode()),
+ getSharingModeText(AAudioStream_getSharingMode(stream)));
+
+ printf(" PerformanceMode: requested = %d, actual = %d\n",
+ getPerformanceMode(), AAudioStream_getPerformanceMode(stream));
+ printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
+ ? "yes" : "no");
+
+ }
+
+ int32_t getDurationSeconds() const {
+ return mDurationSeconds;
+ }
+
+ void setDurationSeconds(int32_t seconds) {
+ mDurationSeconds = seconds;
+ }
+
+private:
+ int32_t mDurationSeconds = DEFAULT_DURATION_SECONDS;
+};
+
+#endif // AAUDIO_EXAMPLE_ARGS_PARSER_H
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index aaeb25f..cc0cb34 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -23,12 +23,18 @@
#include <sched.h>
#include <aaudio/AAudio.h>
+#include "AAudioArgsParser.h"
#include "SineGenerator.h"
//#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
+// Arbitrary period for glitches, once per second at 48000 Hz.
+#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
+// How long to sleep in a callback to cause an intentional glitch. For testing.
+#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
+
/**
* Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
*/
@@ -55,15 +61,23 @@
mRequestedPerformanceMode = requestedPerformanceMode;
}
+ // TODO Extract a common base class for record and playback.
/**
* Also known as "sample rate"
* Only call this after open() has been called.
*/
- int32_t getFramesPerSecond() {
+ int32_t getFramesPerSecond() const {
+ return getSampleRate(); // alias
+ }
+
+ /**
+ * Only call this after open() has been called.
+ */
+ int32_t getSampleRate() const {
if (mStream == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return AAudioStream_getSampleRate(mStream);;
+ return AAudioStream_getSampleRate(mStream);
}
/**
@@ -73,57 +87,83 @@
if (mStream == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return AAudioStream_getChannelCount(mStream);;
+ return AAudioStream_getChannelCount(mStream);
}
/**
* Open a stream
*/
+ aaudio_result_t open(const AAudioParameters ¶meters,
+ AAudioStream_dataCallback dataCallback = nullptr,
+ AAudioStream_errorCallback errorCallback = nullptr,
+ void *userContext = nullptr) {
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ AAudioStreamBuilder *builder = nullptr;
+ result = AAudio_createStreamBuilder(&builder);
+ if (result != AAUDIO_OK) return result;
+
+ parameters.applyParameters(builder); // apply args
+
+ AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+
+ if (dataCallback != nullptr) {
+ AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
+ }
+ if (errorCallback != nullptr) {
+ AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
+ }
+ //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
+ //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
+
+ // Open an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+
+ if (result == AAUDIO_OK) {
+ int32_t sizeInBursts = parameters.getNumberOfBursts();
+ if (sizeInBursts > 0) {
+ int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
+ AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
+ }
+ }
+
+ AAudioStreamBuilder_delete(builder);
+ return result;
+ }
+
aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
- AAudioStream_dataCallback dataProc, AAudioStream_errorCallback errorProc,
+ AAudioStream_dataCallback dataProc,
+ AAudioStream_errorCallback errorProc,
void *userContext) {
aaudio_result_t result = AAUDIO_OK;
// Use an AAudioStreamBuilder to contain requested parameters.
- result = AAudio_createStreamBuilder(&mBuilder);
+ AAudioStreamBuilder *builder = nullptr;
+ result = AAudio_createStreamBuilder(&builder);
if (result != AAUDIO_OK) return result;
- //AAudioStreamBuilder_setSampleRate(mBuilder, 44100);
- AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequestedPerformanceMode);
- AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
+ AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+ AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
+ AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
+
+ AAudioStreamBuilder_setChannelCount(builder, channelCount);
+ AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
+ AAudioStreamBuilder_setFormat(builder, format);
+
if (dataProc != nullptr) {
- AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
+ AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
}
if (errorProc != nullptr) {
- AAudioStreamBuilder_setErrorCallback(mBuilder, errorProc, userContext);
+ AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
}
- AAudioStreamBuilder_setChannelCount(mBuilder, channelCount);
- AAudioStreamBuilder_setSampleRate(mBuilder, sampSampleRate);
- AAudioStreamBuilder_setFormat(mBuilder, format);
- //AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
- AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
-
- //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_NONE;
- aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
- //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
- AAudioStreamBuilder_setPerformanceMode(mBuilder, perfMode);
+ //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
+ //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
// Open an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
- if (result != AAUDIO_OK) goto finish1;
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
- printf("AAudioStream_getFramesPerBurst() = %d\n",
- AAudioStream_getFramesPerBurst(mStream));
- printf("AAudioStream_getBufferSizeInFrames() = %d\n",
- AAudioStream_getBufferSizeInFrames(mStream));
- printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
- AAudioStream_getBufferCapacityInFrames(mStream));
- printf("AAudioStream_getPerformanceMode() = %d, requested %d\n",
- AAudioStream_getPerformanceMode(mStream), perfMode);
-
- finish1:
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
+ AAudioStreamBuilder_delete(builder);
return result;
}
@@ -132,8 +172,6 @@
printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
AAudioStream_close(mStream);
mStream = nullptr;
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
}
return AAUDIO_OK;
}
@@ -178,7 +216,6 @@
}
private:
- AAudioStreamBuilder *mBuilder = nullptr;
AAudioStream *mStream = nullptr;
aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
@@ -187,8 +224,13 @@
typedef struct SineThreadedData_s {
SineGenerator sineOsc1;
SineGenerator sineOsc2;
+ int64_t framesTotal = 0;
+ int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
+ int32_t minNumFrames = INT32_MAX;
+ int32_t maxNumFrames = 0;
int scheduler;
- bool schedulerChecked;
+ bool schedulerChecked = false;
+ bool forceUnderruns = false;
} SineThreadedData_t;
// Callback function that fills the audio output buffer.
@@ -201,16 +243,33 @@
// should not happen but just in case...
if (userData == nullptr) {
- fprintf(stderr, "ERROR - SimplePlayerDataCallbackProc needs userData\n");
+ printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
return AAUDIO_CALLBACK_RESULT_STOP;
}
SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+ sineData->framesTotal += numFrames;
+
+ if (sineData->forceUnderruns) {
+ if (sineData->framesTotal > sineData->nextFrameToGlitch) {
+ usleep(FORCED_UNDERRUN_SLEEP_MICROS);
+ printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
+ sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
+ }
+ }
+
if (!sineData->schedulerChecked) {
sineData->scheduler = sched_getscheduler(gettid());
sineData->schedulerChecked = true;
}
+ if (numFrames > sineData->maxNumFrames) {
+ sineData->maxNumFrames = numFrames;
+ }
+ if (numFrames < sineData->minNumFrames) {
+ sineData->minNumFrames = numFrames;
+ }
+
int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
// This code only plays on the first one or two channels.
// TODO Support arbitrary number of channels.
diff --git a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
index 9e7c463..5ecac04 100644
--- a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
+++ b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
@@ -20,10 +20,12 @@
#define AAUDIO_SIMPLE_RECORDER_H
#include <aaudio/AAudio.h>
+#include "AAudioArgsParser.h"
//#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
+
/**
* Simple wrapper for AAudio that opens an input stream either in callback or blocking read mode.
*/
@@ -54,22 +56,37 @@
* Also known as "sample rate"
* Only call this after open() has been called.
*/
- int32_t getFramesPerSecond() {
- if (mStream == nullptr) {
- return AAUDIO_ERROR_INVALID_STATE;
- }
- return AAudioStream_getSampleRate(mStream);;
+ int32_t getFramesPerSecond() const {
+ return getSampleRate(); // alias
}
/**
* Only call this after open() has been called.
*/
- int32_t getSamplesPerFrame() {
+ int32_t getSampleRate() const {
if (mStream == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return AAudioStream_getSamplesPerFrame(mStream);;
+ return AAudioStream_getSampleRate(mStream);
}
+
+ /**
+ * Only call this after open() has been called.
+ */
+ int32_t getChannelCount() {
+ if (mStream == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return AAudioStream_getChannelCount(mStream);
+ }
+
+ /**
+ * @deprecated use getChannelCount()
+ */
+ int32_t getSamplesPerFrame() {
+ return getChannelCount();
+ }
+
/**
* Only call this after open() has been called.
*/
@@ -77,53 +94,85 @@
if (mStream == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return AAudioStream_getFramesRead(mStream);;
+ return AAudioStream_getFramesRead(mStream);
+ }
+
+ aaudio_result_t open(const AAudioParameters ¶meters,
+ AAudioStream_dataCallback dataCallback = nullptr,
+ AAudioStream_errorCallback errorCallback = nullptr,
+ void *userContext = nullptr) {
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ AAudioStreamBuilder *builder = nullptr;
+ result = AAudio_createStreamBuilder(&builder);
+ if (result != AAUDIO_OK) return result;
+
+ parameters.applyParameters(builder); // apply args
+
+ AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
+
+ if (dataCallback != nullptr) {
+ AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
+ }
+ if (errorCallback != nullptr) {
+ AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
+ }
+
+ // Open an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ if (result != AAUDIO_OK) {
+ fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
+ result, AAudio_convertResultToText(result));
+ }
+
+ if (result == AAUDIO_OK) {
+ int32_t sizeInBursts = parameters.getNumberOfBursts();
+ if (sizeInBursts > 0) {
+ int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
+ AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
+ }
+ }
+
+ AAudioStreamBuilder_delete(builder);
+ return result;
}
/**
* Open a stream
*/
aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
- AAudioStream_dataCallback dataProc, AAudioStream_errorCallback errorProc,
+ AAudioStream_dataCallback dataProc,
+ AAudioStream_errorCallback errorProc,
void *userContext) {
aaudio_result_t result = AAUDIO_OK;
// Use an AAudioStreamBuilder to contain requested parameters.
- result = AAudio_createStreamBuilder(&mBuilder);
+ AAudioStreamBuilder *builder = nullptr;
+ result = AAudio_createStreamBuilder(&builder);
if (result != AAUDIO_OK) return result;
- AAudioStreamBuilder_setDirection(mBuilder, AAUDIO_DIRECTION_INPUT);
- AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequestedPerformanceMode);
- AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
+ AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
+ AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
+ AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
if (dataProc != nullptr) {
- AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
+ AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
}
if (errorProc != nullptr) {
- AAudioStreamBuilder_setErrorCallback(mBuilder, errorProc, userContext);
+ AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
}
- AAudioStreamBuilder_setChannelCount(mBuilder, channelCount);
- AAudioStreamBuilder_setSampleRate(mBuilder, sampSampleRate);
- AAudioStreamBuilder_setFormat(mBuilder, format);
+ AAudioStreamBuilder_setChannelCount(builder, channelCount);
+ AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
+ AAudioStreamBuilder_setFormat(builder, format);
// Open an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
result, AAudio_convertResultToText(result));
- goto finish1;
}
- printf("AAudioStream_getFramesPerBurst() = %d\n",
- AAudioStream_getFramesPerBurst(mStream));
- printf("AAudioStream_getBufferSizeInFrames() = %d\n",
- AAudioStream_getBufferSizeInFrames(mStream));
- printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
- AAudioStream_getBufferCapacityInFrames(mStream));
- return result;
-
- finish1:
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
+ AAudioStreamBuilder_delete(builder);
return result;
}
@@ -132,8 +181,6 @@
printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
AAudioStream_close(mStream);
mStream = nullptr;
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
}
return AAUDIO_OK;
}
@@ -186,7 +233,6 @@
}
private:
- AAudioStreamBuilder *mBuilder = nullptr;
AAudioStream *mStream = nullptr;
aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
diff --git a/media/libaaudio/examples/utils/dummy.cpp b/media/libaaudio/examples/utils/dummy.cpp
new file mode 100644
index 0000000..8ef7e36
--- /dev/null
+++ b/media/libaaudio/examples/utils/dummy.cpp
@@ -0,0 +1,5 @@
+/**
+ * Dummy file needed to get Android Studio to scan this folder.
+ */
+
+int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
index c306ed3..d630e76 100644
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ b/media/libaaudio/examples/write_sine/jni/Android.mk
@@ -11,7 +11,7 @@
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/write_sine.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := write_sine_ndk
+LOCAL_MODULE := write_sine
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
@@ -23,7 +23,7 @@
LOCAL_SRC_FILES:= ../src/write_sine_callback.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := write_sine_callback_ndk
+LOCAL_MODULE := write_sine_callback
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 6522ba4..87fb40b 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -16,46 +16,32 @@
// Play sine waves using AAudio.
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
+#include <asm/fcntl.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
+#include "AAudioArgsParser.h"
-#define SAMPLE_RATE 48000
-#define NUM_SECONDS 20
+#define NUM_SECONDS 4
-#define MMAP_POLICY AAUDIO_UNSPECIFIED
-//#define MMAP_POLICY AAUDIO_POLICY_NEVER
-//#define MMAP_POLICY AAUDIO_POLICY_AUTO
-//#define MMAP_POLICY AAUDIO_POLICY_ALWAYS
-
-#define REQUESTED_FORMAT AAUDIO_FORMAT_PCM_I16
-
-#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_SHARED
-//#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
-
-
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
{
- (void)argc; // unused
-
+ AAudioArgsParser argParser;
AAudioSimplePlayer player;
SineThreadedData_t myData;
- aaudio_result_t result = AAUDIO_OK;
+ aaudio_result_t result = AAUDIO_OK;
- const int requestedChannelCount = 2;
- int actualChannelCount = 0;
- const int requestedSampleRate = SAMPLE_RATE;
- int actualSampleRate = 0;
- aaudio_format_t requestedDataFormat = REQUESTED_FORMAT;
+ int32_t actualChannelCount = 0;
+ int32_t actualSampleRate = 0;
aaudio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
- aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
AAudioStream *aaudioStream = nullptr;
- aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
int32_t framesPerBurst = 0;
int32_t framesPerWrite = 0;
int32_t bufferCapacity = 0;
@@ -65,56 +51,38 @@
float *floatData = nullptr;
int16_t *shortData = nullptr;
+ int testFd = -1;
+
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine wave using AAudio\n", argv[0]);
+ printf("%s - Play a sine wave using AAudio V0.1.1\n", argv[0]);
- AAudio_setMMapPolicy(MMAP_POLICY);
- printf("requested MMapPolicy = %d\n", AAudio_getMMapPolicy());
+ if (argParser.parseArgs(argc, argv)) {
+ return EXIT_FAILURE;
+ }
- player.setSharingMode(REQUESTED_SHARING_MODE);
-
- result = player.open(requestedChannelCount, requestedSampleRate, requestedDataFormat,
- nullptr, nullptr, &myData);
+ result = player.open(argParser);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - player.open() returned %d\n", result);
goto finish;
}
aaudioStream = player.getStream();
- // Request stream properties.
- state = AAudioStream_getState(aaudioStream);
- printf("after open, state = %s\n", AAudio_convertStreamStateToText(state));
+ argParser.compareWithStream(aaudioStream);
- // Check to see what kind of stream we actually got.
+ actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
- printf("SampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
+ actualDataFormat = AAudioStream_getFormat(aaudioStream);
myData.sineOsc1.setup(440.0, actualSampleRate);
myData.sineOsc2.setup(660.0, actualSampleRate);
- actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
- printf("ChannelCount: requested = %d, actual = %d\n",
- requestedChannelCount, actualChannelCount);
-
- actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
- printf("SharingMode: requested = %s, actual = %s\n",
- getSharingModeText(REQUESTED_SHARING_MODE),
- getSharingModeText(actualSharingMode));
-
- // This is the number of frames that are read in one chunk by a DMA controller
- // or a DSP or a mixer.
- framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
- printf("Buffer: bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(aaudioStream));
- bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
- printf("Buffer: bufferCapacity = %d, remainder = %d\n",
- bufferCapacity, bufferCapacity % framesPerBurst);
-
// Some DMA might use very short bursts of 16 frames. We don't need to write such small
// buffers. But it helps to use a multiple of the burst size for predictable scheduling.
+ framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
framesPerWrite = framesPerBurst;
while (framesPerWrite < 48) {
framesPerWrite *= 2;
@@ -122,13 +90,6 @@
printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
- printf("PerformanceMode = %d\n", AAudioStream_getPerformanceMode(aaudioStream));
- printf("is MMAP used? = %s\n", AAudioStream_isMMapUsed(aaudioStream) ? "yes" : "no");
-
- actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("DataFormat: requested = %d, actual = %d\n", REQUESTED_FORMAT, actualDataFormat);
- // TODO handle other data formats
-
// Allocate a buffer for the audio data.
if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
floatData = new float[framesPerWrite * actualChannelCount];
@@ -139,6 +100,9 @@
goto finish;
}
+ testFd = open("/data/aaudio_temp.raw", O_CREAT | O_RDWR, S_IRWXU);
+ printf("testFd = %d, pid = %d\n", testFd, getpid());
+
// Start the stream.
printf("call player.start()\n");
result = player.start();
@@ -147,11 +111,11 @@
goto finish;
}
- state = AAudioStream_getState(aaudioStream);
- printf("after start, state = %s\n", AAudio_convertStreamStateToText(state));
+ printf("after start, state = %s\n",
+ AAudio_convertStreamStateToText(AAudioStream_getState(aaudioStream)));
// Play for a while.
- framesToPlay = actualSampleRate * NUM_SECONDS;
+ framesToPlay = actualSampleRate * argParser.getDurationSeconds();
framesLeft = framesToPlay;
while (framesLeft > 0) {
@@ -220,7 +184,17 @@
}
finish:
+ printf("testFd = %d, fcntl before aaudio close returns 0x%08X\n",
+ testFd, fcntl(testFd, F_GETFD));
player.close();
+ printf("testFd = %d, fcntl after aaudio close returns 0x%08X\n",
+ testFd, fcntl(testFd, F_GETFD));
+ if (::close(testFd) != 0) {
+ printf("ERROR SharedMemoryParcelable::close() of testFd = %d, errno = %s\n",
+ testFd, strerror(errno));
+ }
+ printf("testFd = %d, fcntl after close() returns 0x%08X\n", testFd, fcntl(testFd, F_GETFD));
+
delete[] floatData;
delete[] shortData;
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 69145aa..b5602e9 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -26,37 +26,42 @@
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
+#include "../../utils/AAudioSimplePlayer.h"
-#define NUM_SECONDS 5
-
-// Application data that gets passed to the callback.
-#define MAX_FRAME_COUNT_RECORDS 256
-
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
{
- (void)argc; // unused
+ AAudioArgsParser argParser;
AAudioSimplePlayer player;
SineThreadedData_t myData;
aaudio_result_t result;
+ int32_t actualSampleRate;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine sweep using an AAudio callback\n", argv[0]);
+
+ printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
myData.schedulerChecked = false;
+ myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
- result = player.open(2, 44100, AAUDIO_FORMAT_PCM_FLOAT,
+ if (argParser.parseArgs(argc, argv)) {
+ return EXIT_FAILURE;
+ }
+
+ result = player.open(argParser,
SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - player.open() returned %d\n", result);
goto error;
}
- printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
- printf("player.getChannelCount() = %d\n", player.getChannelCount());
- myData.sineOsc1.setup(440.0, 48000);
+
+ argParser.compareWithStream(player.getStream());
+
+ actualSampleRate = player.getSampleRate();
+ myData.sineOsc1.setup(440.0, actualSampleRate);
myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
- myData.sineOsc2.setup(660.0, 48000);
+ myData.sineOsc2.setup(660.0, actualSampleRate);
myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
#if 0
@@ -73,8 +78,9 @@
goto error;
}
- printf("Sleep for %d seconds while audio plays in a callback thread.\n", NUM_SECONDS);
- for (int second = 0; second < NUM_SECONDS; second++)
+ printf("Sleep for %d seconds while audio plays in a callback thread.\n",
+ argParser.getDurationSeconds());
+ for (int second = 0; second < argParser.getDurationSeconds(); second++)
{
const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
(void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
@@ -92,7 +98,10 @@
printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
break;
}
- printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(player.getStream()));
+ printf("framesWritten = %d, underruns = %d\n",
+ (int) AAudioStream_getFramesWritten(player.getStream()),
+ (int) AAudioStream_getXRunCount(player.getStream())
+ );
}
printf("Woke up now.\n");
@@ -113,6 +122,9 @@
SCHED_FIFO);
}
+ printf("min numFrames = %8d\n", (int) myData.minNumFrames);
+ printf("max numFrames = %8d\n", (int) myData.maxNumFrames);
+
printf("SUCCESS\n");
return EXIT_SUCCESS;
error:
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
deleted file mode 100644
index 1f8dcd9..0000000
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := samples
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/src \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-# NDK recommends using this kind of relative path instead of an absolute path.
-LOCAL_SRC_FILES:= ../src/write_sine.cpp
-
-LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
- libbinder libcutils libutils \
- libaudioclient liblog libtinyalsa libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
-
-LOCAL_MODULE := write_sine
-include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-LOCAL_SRC_FILES:= ../src/write_sine_callback.cpp
-
-LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
- libbinder libcutils libutils \
- libaudioclient liblog libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
-
-LOCAL_MODULE := write_sine_callback
-include $(BUILD_EXECUTABLE)
diff --git a/media/libaaudio/examples/write_sine/static/README.md b/media/libaaudio/examples/write_sine/static/README.md
deleted file mode 100644
index 6e26d7b..0000000
--- a/media/libaaudio/examples/write_sine/static/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Makefile for building simple command line examples.
-They link with AAudio as a static library.
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index 28c4d7f..cfcf27a 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -32,6 +32,7 @@
core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ core/AAudioStreamParameters.cpp \
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
@@ -52,6 +53,7 @@
binding/AAudioBinderClient.cpp \
binding/AAudioStreamRequest.cpp \
binding/AAudioStreamConfiguration.cpp \
+ binding/IAAudioClient.cpp \
binding/IAAudioService.cpp \
binding/RingBufferParcelable.cpp \
binding/SharedMemoryParcelable.cpp \
@@ -89,6 +91,7 @@
LOCAL_SRC_FILES = core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ core/AAudioStreamParameters.cpp \
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
@@ -109,6 +112,7 @@
binding/AAudioBinderClient.cpp \
binding/AAudioStreamRequest.cpp \
binding/AAudioStreamConfiguration.cpp \
+ binding/IAAudioClient.cpp \
binding/IAAudioService.cpp \
binding/RingBufferParcelable.cpp \
binding/SharedMemoryParcelable.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 435b30f..a268e49 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -19,75 +19,104 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <binder/IInterface.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
+#include <media/AudioSystem.h>
#include <aaudio/AAudio.h>
#include "AudioEndpointParcelable.h"
-#include "binding/AAudioStreamRequest.h"
-#include "binding/AAudioStreamConfiguration.h"
-#include "binding/IAAudioService.h"
-#include "binding/AAudioServiceMessage.h"
+#include "binding/AAudioBinderClient.h"
+//#include "binding/AAudioStreamRequest.h"
+//#include "binding/AAudioStreamConfiguration.h"
+//#include "binding/IAAudioService.h"
+//#include "binding/AAudioServiceMessage.h"
-#include "AAudioBinderClient.h"
-#include "AAudioServiceInterface.h"
+//#include "AAudioServiceInterface.h"
using android::String16;
using android::IServiceManager;
using android::defaultServiceManager;
using android::interface_cast;
+using android::IInterface;
using android::IAAudioService;
using android::Mutex;
using android::sp;
+using android::wp;
using namespace aaudio;
-static android::Mutex gServiceLock;
-static sp<IAAudioService> gAAudioService;
-
ANDROID_SINGLETON_STATIC_INSTANCE(AAudioBinderClient);
+AAudioBinderClient::AAudioBinderClient()
+ : AAudioServiceInterface()
+ , Singleton<AAudioBinderClient>() {
+
+ mAAudioClient = new AAudioClient(this);
+ ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+}
+
+AAudioBinderClient::~AAudioBinderClient() {
+ Mutex::Autolock _l(mServiceLock);
+ if (mAAudioService != 0) {
+ IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
+ }
+}
+
// TODO Share code with other service clients.
// Helper function to get access to the "AAudioService" service.
// This code was modeled after frameworks/av/media/libaudioclient/AudioSystem.cpp
-static const sp<IAAudioService> getAAudioService() {
- sp<IBinder> binder;
- Mutex::Autolock _l(gServiceLock);
- if (gAAudioService == 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- // Try several times to get the service.
- int retries = 4;
- do {
- binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
+const sp<IAAudioService> AAudioBinderClient::getAAudioService() {
+ sp<IAAudioService> aaudioService;
+ bool needToRegister = false;
+ {
+ Mutex::Autolock _l(mServiceLock);
+ if (mAAudioService == 0) {
+ sp<IBinder> binder;
+ sp<IServiceManager> sm = defaultServiceManager();
+ // Try several times to get the service.
+ int retries = 4;
+ do {
+ binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
+ if (binder != 0) {
+ break;
+ }
+ } while (retries-- > 0);
+
if (binder != 0) {
- break;
+ // Ask for notification if the service dies.
+ status_t status = binder->linkToDeath(mAAudioClient);
+ // TODO review what we should do if this fails
+ if (status != NO_ERROR) {
+ ALOGE("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
+ mAAudioClient.get(), status);
+ }
+ mAAudioService = interface_cast<IAAudioService>(binder);
+ needToRegister = true;
+ // Make sure callbacks can be received by mAAudioClient
+ android::ProcessState::self()->startThreadPool();
+ } else {
+ ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
}
- } while (retries-- > 0);
-
- if (binder != 0) {
- // TODO Add linkToDeath() like in frameworks/av/media/libaudioclient/AudioSystem.cpp
- // TODO Create a DeathRecipient that disconnects all active streams.
- gAAudioService = interface_cast<IAAudioService>(binder);
- } else {
- ALOGE("AudioStreamInternal could not get %s", AAUDIO_SERVICE_NAME);
}
+ aaudioService = mAAudioService;
}
- return gAAudioService;
+ // Do this outside the mutex lock.
+ if (needToRegister && aaudioService != 0) { // new client?
+ aaudioService->registerClient(mAAudioClient);
+ }
+ return aaudioService;
}
-static void dropAAudioService() {
- Mutex::Autolock _l(gServiceLock);
- gAAudioService.clear(); // force a reconnect
+void AAudioBinderClient::dropAAudioService() {
+ Mutex::Autolock _l(mServiceLock);
+ mAAudioService.clear(); // force a reconnect
}
-AAudioBinderClient::AAudioBinderClient()
- : AAudioServiceInterface()
- , Singleton<AAudioBinderClient>() {}
-
-AAudioBinderClient::~AAudioBinderClient() {}
/**
* @param request info needed to create the stream
@@ -159,23 +188,19 @@
* Manage the specified thread as a low latency audio thread.
*/
aaudio_result_t AAudioBinderClient::registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId,
int64_t periodNanoseconds) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->registerAudioThread(streamHandle,
- clientProcessId,
clientThreadId,
periodNanoseconds);
}
aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->unregisterAudioThread(streamHandle,
- clientProcessId,
clientThreadId);
}
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index e223376..89ae85c 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
#define ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
+#include <utils/RefBase.h>
#include <utils/Singleton.h>
#include <aaudio/AAudio.h>
@@ -25,14 +26,16 @@
#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
#include "binding/AudioEndpointParcelable.h"
+#include "binding/IAAudioService.h"
/**
- * Implements the AAudioServiceInterface by talking to the actual service through Binder.
+ * Implements the AAudioServiceInterface by talking to the service through Binder.
*/
namespace aaudio {
-class AAudioBinderClient : public AAudioServiceInterface
+class AAudioBinderClient : public virtual android::RefBase
+ , public AAudioServiceInterface
, public android::Singleton<AAudioBinderClient> {
public:
@@ -41,6 +44,12 @@
virtual ~AAudioBinderClient();
+ const android::sp<android::IAAudioService> getAAudioService();
+
+ void dropAAudioService();
+
+ void registerClient(const android::sp<android::IAAudioClient>& client __unused) override {}
+
/**
* @param request info needed to create the stream
* @param configuration contains resulting information about the created stream
@@ -82,13 +91,61 @@
* TODO Consider passing this information as part of the startStream() call.
*/
aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId,
int64_t periodNanoseconds) override;
aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId) override;
+
+ aaudio_result_t startClient(aaudio_handle_t streamHandle __unused,
+ const android::AudioClient& client __unused,
+ audio_port_handle_t *clientHandle) override {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ aaudio_result_t stopClient(aaudio_handle_t streamHandle __unused,
+ audio_port_handle_t clientHandle __unused) override {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
+ // TODO This is just a stub so we can have a client Binder to pass to the service.
+ // TODO Implemented in a later CL.
+ ALOGW("onStreamChange called!");
+ }
+
+ class AAudioClient : public android::IBinder::DeathRecipient , public android::BnAAudioClient
+ {
+ public:
+ AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
+ : mBinderClient(aaudioBinderClient) {
+ }
+
+ // implement DeathRecipient
+ virtual void binderDied(const android::wp<android::IBinder>& who __unused) {
+ android::sp<AAudioBinderClient> client = mBinderClient.promote();
+ if (client != 0) {
+ client->dropAAudioService();
+ }
+ ALOGW("AAudio service binderDied()!");
+ }
+
+ // implement BnAAudioClient
+ void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
+ android::sp<AAudioBinderClient> client = mBinderClient.promote();
+ if (client != 0) {
+ client->onStreamChange(handle, opcode, value);
+ }
+ }
+ private:
+ android::wp<AAudioBinderClient> mBinderClient;
+ };
+
+
+ android::Mutex mServiceLock;
+ android::sp<android::IAAudioService> mAAudioService;
+ android::sp<AAudioClient> mAAudioClient;
+
};
diff --git a/media/libaaudio/src/binding/AAudioServiceDefinitions.h b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
index 638544e..8a2303c 100644
--- a/media/libaaudio/src/binding/AAudioServiceDefinitions.h
+++ b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
@@ -29,8 +29,9 @@
namespace android {
-enum aaudio_commands_t {
- OPEN_STREAM = IBinder::FIRST_CALL_TRANSACTION,
+enum aaudio_service_commands_t {
+ REGISTER_CLIENT = IBinder::FIRST_CALL_TRANSACTION,
+ OPEN_STREAM,
CLOSE_STREAM,
GET_STREAM_DESCRIPTION,
START_STREAM,
@@ -41,6 +42,10 @@
UNREGISTER_AUDIO_THREAD
};
+enum aaudio_client_commands_t {
+ ON_STREAM_CHANGE = IBinder::FIRST_CALL_TRANSACTION
+};
+
} // namespace android
namespace aaudio {
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 824e5bc..a64405b 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -17,10 +17,14 @@
#ifndef ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
#define ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#include <utils/StrongPointer.h>
+#include <media/AudioClient.h>
+
#include "binding/AAudioServiceDefinitions.h"
#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
#include "binding/AudioEndpointParcelable.h"
+#include "binding/IAAudioClient.h"
/**
* This has the same methods as IAAudioService but without the Binder features.
@@ -36,6 +40,8 @@
AAudioServiceInterface() {};
virtual ~AAudioServiceInterface() = default;
+ virtual void registerClient(const android::sp<android::IAAudioClient>& client) = 0;
+
/**
* @param request info needed to create the stream
* @param configuration contains information about the created stream
@@ -76,13 +82,18 @@
* Manage the specified thread as a low latency audio thread.
*/
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId,
int64_t periodNanoseconds) = 0;
virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId) = 0;
+
+ virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
+ const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) = 0;
+
+ virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
+ audio_port_handle_t clientHandle) = 0;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 44edb1d..e763934 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -36,17 +36,17 @@
status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
status_t status;
- status = parcel->writeInt32(mDeviceId);
+ status = parcel->writeInt32(getDeviceId());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mSampleRate);
+ status = parcel->writeInt32(getSampleRate());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mSamplesPerFrame);
+ status = parcel->writeInt32(getSamplesPerFrame());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) mSharingMode);
+ status = parcel->writeInt32((int32_t) getSharingMode());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) mAudioFormat);
+ status = parcel->writeInt32((int32_t) getFormat());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mBufferCapacity);
+ status = parcel->writeInt32(getBufferCapacity());
if (status != NO_ERROR) goto error;
return NO_ERROR;
error:
@@ -55,57 +55,27 @@
}
status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mDeviceId);
+ int32_t value;
+ status_t status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSampleRate);
+ setDeviceId(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSamplesPerFrame);
+ setSampleRate(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSharingMode);
+ setSamplesPerFrame(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mAudioFormat);
+ setSharingMode(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mBufferCapacity);
+ setFormat(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
+ setBufferCapacity(value);
return NO_ERROR;
error:
ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
return status;
-}
-
-aaudio_result_t AAudioStreamConfiguration::validate() const {
- // Validate results of the open.
- if (mSampleRate < 0 || mSampleRate >= 8 * 48000) { // TODO review limits
- ALOGE("AAudioStreamConfiguration.validate(): invalid sampleRate = %d", mSampleRate);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- if (mSamplesPerFrame < 1 || mSamplesPerFrame >= 32) { // TODO review limits
- ALOGE("AAudioStreamConfiguration.validate() invalid samplesPerFrame = %d", mSamplesPerFrame);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- switch (mAudioFormat) {
- case AAUDIO_FORMAT_PCM_I16:
- case AAUDIO_FORMAT_PCM_FLOAT:
- break;
- default:
- ALOGE("AAudioStreamConfiguration.validate() invalid audioFormat = %d", mAudioFormat);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- if (mBufferCapacity < 0) {
- ALOGE("AAudioStreamConfiguration.validate() invalid mBufferCapacity = %d", mBufferCapacity);
- return AAUDIO_ERROR_INTERNAL;
- }
- return AAUDIO_OK;
-}
-
-void AAudioStreamConfiguration::dump() const {
- ALOGD("AAudioStreamConfiguration mDeviceId = %d", mDeviceId);
- ALOGD("AAudioStreamConfiguration mSampleRate = %d", mSampleRate);
- ALOGD("AAudioStreamConfiguration mSamplesPerFrame = %d", mSamplesPerFrame);
- ALOGD("AAudioStreamConfiguration mSharingMode = %d", (int)mSharingMode);
- ALOGD("AAudioStreamConfiguration mAudioFormat = %d", (int)mAudioFormat);
- ALOGD("AAudioStreamConfiguration mBufferCapacity = %d", mBufferCapacity);
-}
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index 144595a..b324896 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -22,6 +22,7 @@
#include <aaudio/AAudio.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include "core/AAudioStreamParameters.h"
using android::status_t;
using android::Parcel;
@@ -29,74 +30,14 @@
namespace aaudio {
-class AAudioStreamConfiguration : public Parcelable {
+class AAudioStreamConfiguration : public AAudioStreamParameters, public Parcelable {
public:
AAudioStreamConfiguration();
virtual ~AAudioStreamConfiguration();
- int32_t getDeviceId() const {
- return mDeviceId;
- }
-
- void setDeviceId(int32_t deviceId) {
- mDeviceId = deviceId;
- }
-
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSamplesPerFrame() const {
- return mSamplesPerFrame;
- }
-
- void setSamplesPerFrame(int32_t samplesPerFrame) {
- mSamplesPerFrame = samplesPerFrame;
- }
-
- aaudio_format_t getAudioFormat() const {
- return mAudioFormat;
- }
-
- void setAudioFormat(aaudio_format_t audioFormat) {
- mAudioFormat = audioFormat;
- }
-
- aaudio_sharing_mode_t getSharingMode() const {
- return mSharingMode;
- }
-
- void setSharingMode(aaudio_sharing_mode_t sharingMode) {
- mSharingMode = sharingMode;
- }
-
- int32_t getBufferCapacity() const {
- return mBufferCapacity;
- }
-
- void setBufferCapacity(int32_t frames) {
- mBufferCapacity = frames;
- }
-
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
-
- aaudio_result_t validate() const;
-
- void dump() const;
-
-private:
- int32_t mDeviceId = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
- aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
- int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index a5c27b9..abdcf5b 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -45,16 +45,19 @@
status_t AAudioStreamRequest::writeToParcel(Parcel* parcel) const {
status_t status = parcel->writeInt32((int32_t) mUserId);
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) mProcessId);
- if (status != NO_ERROR) goto error;
+
status = parcel->writeInt32((int32_t) mDirection);
if (status != NO_ERROR) goto error;
status = parcel->writeBool(mSharingModeMatchRequired);
if (status != NO_ERROR) goto error;
+ status = parcel->writeBool(mInService);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.writeToParcel(parcel);
if (status != NO_ERROR) goto error;
+
return NO_ERROR;
error:
@@ -70,17 +73,17 @@
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
- mProcessId = (pid_t) temp;
-
- status = parcel->readInt32(&temp);
- if (status != NO_ERROR) goto error;
mDirection = (aaudio_direction_t) temp;
status = parcel->readBool(&mSharingModeMatchRequired);
if (status != NO_ERROR) goto error;
+ status = parcel->readBool(&mInService);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.readFromParcel(parcel);
if (status != NO_ERROR) goto error;
+
return NO_ERROR;
error:
@@ -96,5 +99,7 @@
ALOGD("AAudioStreamRequest mUserId = %d", mUserId);
ALOGD("AAudioStreamRequest mProcessId = %d", mProcessId);
ALOGD("AAudioStreamRequest mDirection = %d", mDirection);
+ ALOGD("AAudioStreamRequest mSharingModeMatchRequired = %d", mSharingModeMatchRequired);
+ ALOGD("AAudioStreamRequest mInService = %d", mInService);
mConfiguration.dump();
}
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 77138da..b0fa96a 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -68,7 +68,6 @@
mSharingModeMatchRequired = required;
}
-
const AAudioStreamConfiguration &getConstantConfiguration() const {
return mConfiguration;
}
@@ -77,6 +76,14 @@
return mConfiguration;
}
+ bool isInService() const {
+ return mInService;
+ }
+
+ void setInService(bool inService) {
+ mInService = inService;
+ }
+
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
@@ -91,6 +98,7 @@
pid_t mProcessId;
aaudio_direction_t mDirection;
bool mSharingModeMatchRequired = false;
+ bool mInService = false; // Stream opened by AAudioservice
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
index d05abb0..1a97555 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
@@ -28,6 +28,7 @@
#include "binding/RingBufferParcelable.h"
#include "binding/AudioEndpointParcelable.h"
+using android::base::unique_fd;
using android::NO_ERROR;
using android::status_t;
using android::Parcel;
@@ -49,7 +50,8 @@
* Add the file descriptor to the table.
* @return index in table or negative error
*/
-int32_t AudioEndpointParcelable::addFileDescriptor(int fd, int32_t sizeInBytes) {
+int32_t AudioEndpointParcelable::addFileDescriptor(const unique_fd& fd,
+ int32_t sizeInBytes) {
if (mNumSharedMemories >= MAX_SHARED_MEMORIES) {
return AAUDIO_ERROR_OUT_OF_RANGE;
}
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 993075c..aa8573f 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -20,6 +20,7 @@
#include <stdint.h>
//#include <sys/mman.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -47,7 +48,7 @@
* Add the file descriptor to the table.
* @return index in table or negative error
*/
- int32_t addFileDescriptor(int fd, int32_t sizeInBytes);
+ int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);
virtual status_t writeToParcel(Parcel* parcel) const override;
diff --git a/media/libaaudio/src/binding/IAAudioClient.cpp b/media/libaaudio/src/binding/IAAudioClient.cpp
new file mode 100644
index 0000000..c69c4e8
--- /dev/null
+++ b/media/libaaudio/src/binding/IAAudioClient.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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 "AAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <aaudio/AAudio.h>
+
+#include "binding/AAudioBinderClient.h"
+#include "binding/AAudioServiceDefinitions.h"
+#include "binding/IAAudioClient.h"
+#include "utility/AAudioUtilities.h"
+
+namespace android {
+
+using aaudio::aaudio_handle_t;
+
+/**
+ * This is used by the AAudio Service to talk to an AAudio Client.
+ *
+ * The order of parameters in the Parcels must match with code in AAudioClient.cpp.
+ */
+class BpAAudioClient : public BpInterface<IAAudioClient>
+{
+public:
+ explicit BpAAudioClient(const sp<IBinder>& impl)
+ : BpInterface<IAAudioClient>(impl)
+ {
+ }
+
+ void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAAudioClient::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeInt32(opcode);
+ data.writeInt32(value);
+ remote()->transact(ON_STREAM_CHANGE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+};
+
+// Implement an interface to the service.
+IMPLEMENT_META_INTERFACE(AAudioClient, "IAAudioClient");
+
+// The order of parameters in the Parcels must match with code in BpAAudioClient
+
+status_t BnAAudioClient::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ aaudio_handle_t streamHandle;
+ int32_t opcode = 0;
+ int32_t value = 0;
+ ALOGV("BnAAudioClient::onTransact(%u) %u", code, flags);
+
+ switch(code) {
+ case ON_STREAM_CHANGE: {
+ CHECK_INTERFACE(IAAudioClient, data, reply);
+ data.readInt32(&streamHandle);
+ data.readInt32(&opcode);
+ data.readInt32(&value);
+ onStreamChange(streamHandle, opcode, value);
+ ALOGD("BnAAudioClient onStreamChange(%x, %d, %d)", streamHandle, opcode, value);
+ return NO_ERROR;
+ } break;
+
+ default:
+ // ALOGW("BnAAudioClient::onTransact not handled %u", code);
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} /* namespace android */
diff --git a/media/libaaudio/src/binding/IAAudioClient.h b/media/libaaudio/src/binding/IAAudioClient.h
new file mode 100644
index 0000000..21cc33b
--- /dev/null
+++ b/media/libaaudio/src/binding/IAAudioClient.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_AAUDIO_IAAUDIO_CLIENT_H
+#define ANDROID_AAUDIO_IAAUDIO_CLIENT_H
+
+#include <stdint.h>
+#include <binder/IInterface.h>
+
+#include <aaudio/AAudio.h>
+
+#include "utility/HandleTracker.h"
+
+namespace android {
+
+
+// Interface (our AIDL) - client methods called by service
+class IAAudioClient : public IInterface {
+public:
+
+ DECLARE_META_INTERFACE(AAudioClient);
+
+ virtual void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
+
+};
+
+class BnAAudioClient : public BnInterface<IAAudioClient> {
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
+} /* namespace android */
+
+#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index b8ef611..b3c4934 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -14,7 +14,12 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <aaudio/AAudio.h>
+#include <binder/IPCThreadState.h>
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioStreamRequest.h"
@@ -40,16 +45,22 @@
{
}
- virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) override {
+ void registerClient(const sp<IAAudioClient>& client) override
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(client));
+ remote()->transact(REGISTER_CLIENT, data, &reply);
+ }
+
+ aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
+ aaudio::AAudioStreamConfiguration &configurationOutput) override {
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- ALOGV("BpAAudioService::client openStream --------------------");
// request.dump();
request.writeToParcel(&data);
status_t err = remote()->transact(OPEN_STREAM, data, &reply);
- ALOGV("BpAAudioService::client openStream returned %d", err);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client openStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
@@ -186,15 +197,13 @@
}
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
- pid_t clientThreadId,
- int64_t periodNanoseconds)
+ pid_t clientThreadId,
+ int64_t periodNanoseconds)
override {
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
data.writeInt32(streamHandle);
- data.writeInt32((int32_t) clientProcessId);
data.writeInt32((int32_t) clientThreadId);
data.writeInt64(periodNanoseconds);
status_t err = remote()->transact(REGISTER_AUDIO_THREAD, data, &reply);
@@ -208,14 +217,12 @@
}
virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId)
override {
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
data.writeInt32(streamHandle);
- data.writeInt32((int32_t) clientProcessId);
data.writeInt32((int32_t) clientThreadId);
status_t err = remote()->transact(UNREGISTER_AUDIO_THREAD, data, &reply);
if (err != NO_ERROR) {
@@ -237,43 +244,59 @@
status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
- aaudio_handle_t stream;
+ aaudio_handle_t streamHandle;
aaudio::AAudioStreamRequest request;
aaudio::AAudioStreamConfiguration configuration;
- pid_t pid;
pid_t tid;
int64_t nanoseconds;
aaudio_result_t result;
ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
- data.checkInterface(this);
switch(code) {
+ case REGISTER_CLIENT: {
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ sp<IAAudioClient> client = interface_cast<IAAudioClient>(
+ data.readStrongBinder());
+ registerClient(client);
+ return NO_ERROR;
+ } break;
+
case OPEN_STREAM: {
+ CHECK_INTERFACE(IAAudioService, data, reply);
request.readFromParcel(&data);
-
- //ALOGD("BnAAudioService::client openStream request dump --------------------");
- //request.dump();
-
- stream = openStream(request, configuration);
- //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
- reply->writeInt32(stream);
+ result = request.validate();
+ if (result != AAUDIO_OK) {
+ streamHandle = result;
+ } else {
+ //ALOGD("BnAAudioService::client openStream request dump --------------------");
+ //request.dump();
+ // Override the uid and pid from the client in case they are incorrect.
+ request.setUserId(IPCThreadState::self()->getCallingUid());
+ request.setProcessId(IPCThreadState::self()->getCallingPid());
+ streamHandle = openStream(request, configuration);
+ //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
+ // streamHandle);
+ }
+ reply->writeInt32(streamHandle);
configuration.writeToParcel(reply);
return NO_ERROR;
} break;
case CLOSE_STREAM: {
- data.readInt32(&stream);
- result = closeStream(stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
+ result = closeStream(streamHandle);
//ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
- // stream, result);
+ // streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case GET_STREAM_DESCRIPTION: {
- data.readInt32(&stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
aaudio::AudioEndpointParcelable parcelable;
- result = getStreamDescription(stream, parcelable);
+ result = getStreamDescription(streamHandle, parcelable);
if (result != AAUDIO_OK) {
return AAudioConvert_aaudioToAndroidStatus(result);
}
@@ -289,60 +312,64 @@
} break;
case START_STREAM: {
- data.readInt32(&stream);
- result = startStream(stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
+ result = startStream(streamHandle);
ALOGV("BnAAudioService::onTransact START_STREAM 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case PAUSE_STREAM: {
- data.readInt32(&stream);
- result = pauseStream(stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
+ result = pauseStream(streamHandle);
ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case STOP_STREAM: {
- data.readInt32(&stream);
- result = stopStream(stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
+ result = stopStream(streamHandle);
ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case FLUSH_STREAM: {
- data.readInt32(&stream);
- result = flushStream(stream);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
+ result = flushStream(streamHandle);
ALOGV("BnAAudioService::onTransact FLUSH_STREAM 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case REGISTER_AUDIO_THREAD: {
- data.readInt32(&stream);
- data.readInt32(&pid);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
data.readInt32(&tid);
data.readInt64(&nanoseconds);
- result = registerAudioThread(stream, pid, tid, nanoseconds);
+ result = registerAudioThread(streamHandle, tid, nanoseconds);
ALOGV("BnAAudioService::onTransact REGISTER_AUDIO_THREAD 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case UNREGISTER_AUDIO_THREAD: {
- data.readInt32(&stream);
- data.readInt32(&pid);
+ CHECK_INTERFACE(IAAudioService, data, reply);
+ data.readInt32(&streamHandle);
data.readInt32(&tid);
- result = unregisterAudioThread(stream, pid, tid);
+ result = unregisterAudioThread(streamHandle, tid);
ALOGV("BnAAudioService::onTransact UNREGISTER_AUDIO_THREAD 0x%08X, result = %d",
- stream, result);
+ streamHandle, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index 44a5e12..30b3ead 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -28,18 +28,24 @@
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
+#include "binding/IAAudioClient.h"
#include "utility/HandleTracker.h"
namespace android {
#define AAUDIO_SERVICE_NAME "media.aaudio"
-// Interface (our AIDL) - Shared by server and client
+// Interface (our AIDL) - service methods called by client
class IAAudioService : public IInterface {
public:
DECLARE_META_INTERFACE(AAudioService);
+ // Register an object to receive audio input/output change and track notifications.
+ // For a given calling pid, AAudio service disregards any registrations after the first.
+ // Thus the IAAudioClient must be a singleton per process.
+ virtual void registerClient(const sp<IAAudioClient>& client) = 0;
+
/**
* @param request info needed to create the stream
* @param configuration contains information about the created stream
@@ -82,15 +88,12 @@
/**
* Manage the specified thread as a low latency audio thread.
- * TODO Consider passing this information as part of the startStream() call.
*/
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId,
int64_t periodNanoseconds) = 0;
virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId) = 0;
};
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 899eb04..90217ab 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -24,11 +24,13 @@
#include <sys/mman.h>
#include <aaudio/AAudio.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcelable.h>
#include <utility/AAudioUtilities.h>
#include "binding/SharedMemoryParcelable.h"
+using android::base::unique_fd;
using android::NO_ERROR;
using android::status_t;
using android::Parcel;
@@ -39,17 +41,19 @@
SharedMemoryParcelable::SharedMemoryParcelable() {}
SharedMemoryParcelable::~SharedMemoryParcelable() {};
-void SharedMemoryParcelable::setup(int fd, int32_t sizeInBytes) {
- mFd = fd;
+void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) {
+ mFd.reset(dup(fd.get())); // store a duplicate fd
+ ALOGV("SharedMemoryParcelable::setup(%d -> %d, %d) this = %p\n",
+ fd.get(), mFd.get(), sizeInBytes, this);
mSizeInBytes = sizeInBytes;
-
}
status_t SharedMemoryParcelable::writeToParcel(Parcel* parcel) const {
status_t status = parcel->writeInt32(mSizeInBytes);
if (status != NO_ERROR) return status;
if (mSizeInBytes > 0) {
- status = parcel->writeDupFileDescriptor(mFd);
+ ALOGV("SharedMemoryParcelable::writeToParcel() mFd = %d, this = %p\n", mFd.get(), this);
+ status = parcel->writeUniqueFileDescriptor(mFd);
ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d",
status);
}
@@ -62,15 +66,16 @@
return status;
}
if (mSizeInBytes > 0) {
- // Keep the original FD until you are done with the mFd.
- // If you close it in here then it will prevent mFd from working.
- mOriginalFd = parcel->readFileDescriptor();
- ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mOriginalFd = %d\n", mOriginalFd);
- mFd = fcntl(mOriginalFd, F_DUPFD_CLOEXEC, 0);
- ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mFd = %d\n", mFd);
- if (mFd == -1) {
- status = -errno;
- ALOGE("SharedMemoryParcelable readFromParcel fcntl() failed : %d", status);
+ // The Parcel owns the file descriptor and will close it later.
+ unique_fd mmapFd;
+ status = parcel->readUniqueFileDescriptor(&mmapFd);
+ if (status != NO_ERROR) {
+ ALOGE("SharedMemoryParcelable::readFromParcel() readUniqueFileDescriptor() failed : %d",
+ status);
+ } else {
+ // Resolve the memory now while we still have the FD from the Parcel.
+ // Closing the FD will not affect the shared memory once mmap() has been called.
+ status = AAudioConvert_androidToAAudioResult(resolveSharedMemory(mmapFd));
}
}
return status;
@@ -85,45 +90,50 @@
}
mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
}
- if (mFd != -1) {
- ALOGV("SharedMemoryParcelable::close() LEAK? mFd = %d\n", mFd);
- ::close(mFd);
- mFd = -1;
- }
- if (mOriginalFd != -1) {
- ALOGV("SharedMemoryParcelable::close() LEAK? mOriginalFd = %d\n", mOriginalFd);
- ::close(mOriginalFd);
- mOriginalFd = -1;
+ return AAUDIO_OK;
+}
+
+aaudio_result_t SharedMemoryParcelable::resolveSharedMemory(const unique_fd& fd) {
+ mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd.get(), 0);
+ if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
+ ALOGE("SharedMemoryParcelable mmap() failed for fd = %d, errno = %s",
+ fd.get(), strerror(errno));
+ return AAUDIO_ERROR_INTERNAL;
}
return AAUDIO_OK;
}
aaudio_result_t SharedMemoryParcelable::resolve(int32_t offsetInBytes, int32_t sizeInBytes,
void **regionAddressPtr) {
-
if (offsetInBytes < 0) {
ALOGE("SharedMemoryParcelable illegal offsetInBytes = %d", offsetInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
} else if ((offsetInBytes + sizeInBytes) > mSizeInBytes) {
ALOGE("SharedMemoryParcelable out of range, offsetInBytes = %d, "
- "sizeInBytes = %d, mSizeInBytes = %d",
+ "sizeInBytes = %d, mSizeInBytes = %d",
offsetInBytes, sizeInBytes, mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+
+ aaudio_result_t result = AAUDIO_OK;
+
if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
- MAP_SHARED, mFd, 0);
- if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- ALOGE("SharedMemoryParcelable mmap failed for fd = %d, errno = %s",
- mFd, strerror(errno));
- return AAUDIO_ERROR_INTERNAL;
+ if (mFd.get() != -1) {
+ result = resolveSharedMemory(mFd);
+ } else {
+ ALOGE("SharedMemoryParcelable has no file descriptor for shared memory.");
+ result = AAUDIO_ERROR_INTERNAL;
}
}
- *regionAddressPtr = mResolvedAddress + offsetInBytes;
- ALOGV("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
- ALOGV("SharedMemoryParcelable offset by %d, *regionAddressPtr = %p",
- offsetInBytes, *regionAddressPtr);
- return AAUDIO_OK;
+
+ if (result == AAUDIO_OK && mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
+ *regionAddressPtr = mResolvedAddress + offsetInBytes;
+ ALOGV("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
+ ALOGV("SharedMemoryParcelable offset by %d, *regionAddressPtr = %p",
+ offsetInBytes, *regionAddressPtr);
+ }
+ return result;
}
int32_t SharedMemoryParcelable::getSizeInBytes() {
@@ -135,17 +145,11 @@
ALOGE("SharedMemoryParcelable invalid mSizeInBytes = %d", mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
- if (mSizeInBytes > 0) {
- if (mFd == -1) {
- ALOGE("SharedMemoryParcelable uninitialized mFd = %d", mFd);
- return AAUDIO_ERROR_INTERNAL;
- }
- }
return AAUDIO_OK;
}
void SharedMemoryParcelable::dump() {
- ALOGD("SharedMemoryParcelable mFd = %d", mFd);
+ ALOGD("SharedMemoryParcelable mFd = %d", mFd.get());
ALOGD("SharedMemoryParcelable mSizeInBytes = %d", mSizeInBytes);
ALOGD("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 4b94b46..2a634e0 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -18,15 +18,12 @@
#define ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
#include <stdint.h>
-
#include <sys/mman.h>
+
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
-
namespace aaudio {
// Arbitrary limits for sanity checks. TODO remove after debugging.
@@ -37,17 +34,24 @@
/**
* This is a parcelable description of a shared memory referenced by a file descriptor.
* It may be divided into several regions.
+ * The memory can be shared using Binder or simply shared between threads.
*/
-class SharedMemoryParcelable : public Parcelable {
+class SharedMemoryParcelable : public android::Parcelable {
public:
SharedMemoryParcelable();
virtual ~SharedMemoryParcelable();
- void setup(int fd, int32_t sizeInBytes);
+ /**
+ * Make a dup() of the fd and store it for later use.
+ *
+ * @param fd
+ * @param sizeInBytes
+ */
+ void setup(const android::base::unique_fd& fd, int32_t sizeInBytes);
- virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual android::status_t writeToParcel(android::Parcel* parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ virtual android::status_t readFromParcel(const android::Parcel* parcel) override;
// mmap() shared memory
aaudio_result_t resolve(int32_t offsetInBytes, int32_t sizeInBytes, void **regionAddressPtr);
@@ -55,8 +59,6 @@
// munmap() any mapped memory
aaudio_result_t close();
- bool isFileDescriptorSafe();
-
int32_t getSizeInBytes();
aaudio_result_t validate();
@@ -67,10 +69,11 @@
#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
- int mFd = -1;
- int mOriginalFd = -1;
- int32_t mSizeInBytes = 0;
- uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+ aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
+
+ android::base::unique_fd mFd;
+ int32_t mSizeInBytes = 0;
+ uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 5cb642b..6ec285f 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -113,7 +113,8 @@
return result;
}
-aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor)
+aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor,
+ aaudio_direction_t direction)
{
aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
if (result != AAUDIO_OK) {
@@ -143,12 +144,20 @@
descriptor->dataAddress
);
- // ============================ down data queue =============================
+ // ============================ data queue =============================
descriptor = &pEndpointDescriptor->dataQueueDescriptor;
ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
- ALOGV("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
- mFreeRunning = descriptor->readCounterAddress == nullptr;
+ ALOGV("AudioEndpoint::configure() data readCounterAddress = %p",
+ descriptor->readCounterAddress);
+
+ // An example of free running is when the other side is read or written by hardware DMA
+ // or a DSP. It does not update its counter so we have to update it.
+ int64_t *remoteCounter = (direction == AAUDIO_DIRECTION_OUTPUT)
+ ? descriptor->readCounterAddress // read by other side
+ : descriptor->writeCounterAddress; // written by other side
+ mFreeRunning = (remoteCounter == nullptr);
ALOGV("AudioEndpoint::configure() mFreeRunning = %d", mFreeRunning ? 1 : 0);
+
int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
? &mDataReadCounter
: descriptor->readCounterAddress;
@@ -173,13 +182,8 @@
return mUpCommandQueue->read(commandPtr, 1);
}
-aaudio_result_t AudioEndpoint::writeDataNow(const void *buffer, int32_t numFrames)
-{
- return mDataQueue->write(buffer, numFrames);
-}
-
-void AudioEndpoint::getEmptyFramesAvailable(WrappingBuffer *wrappingBuffer) {
- mDataQueue->getEmptyRoomAvailable(wrappingBuffer);
+int32_t AudioEndpoint::getEmptyFramesAvailable(WrappingBuffer *wrappingBuffer) {
+ return mDataQueue->getEmptyRoomAvailable(wrappingBuffer);
}
int32_t AudioEndpoint::getEmptyFramesAvailable()
@@ -187,7 +191,7 @@
return mDataQueue->getFifoControllerBase()->getEmptyFramesAvailable();
}
-void AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer)
+int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer)
{
return mDataQueue->getFullDataAvailable(wrappingBuffer);
}
@@ -246,3 +250,7 @@
return (int32_t)mDataQueue->getBufferCapacityInFrames();
}
+void AudioEndpoint::dump() const {
+ ALOGD("AudioEndpoint: data readCounter = %lld", (long long) mDataQueue->getReadCounter());
+ ALOGD("AudioEndpoint: data writeCounter = %lld", (long long) mDataQueue->getWriteCounter());
+}
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 53ba033..81a4f7b 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -40,7 +40,8 @@
/**
* Configure based on the EndPointDescriptor_t.
*/
- aaudio_result_t configure(const EndpointDescriptor *pEndpointDescriptor);
+ aaudio_result_t configure(const EndpointDescriptor *pEndpointDescriptor,
+ aaudio_direction_t direction);
/**
* Read from a command passed up from the Server.
@@ -48,17 +49,11 @@
*/
aaudio_result_t readUpCommand(AAudioServiceMessage *commandPtr);
- /**
- * Non-blocking write.
- * @return framesWritten or a negative error code.
- */
- aaudio_result_t writeDataNow(const void *buffer, int32_t numFrames);
-
- void getEmptyFramesAvailable(android::WrappingBuffer *wrappingBuffer);
+ int32_t getEmptyFramesAvailable(android::WrappingBuffer *wrappingBuffer);
int32_t getEmptyFramesAvailable();
- void getFullFramesAvailable(android::WrappingBuffer *wrappingBuffer);
+ int32_t getFullFramesAvailable(android::WrappingBuffer *wrappingBuffer);
int32_t getFullFramesAvailable();
@@ -91,6 +86,8 @@
int32_t getBufferCapacityInFrames() const;
+ void dump() const;
+
private:
android::FifoBuffer *mUpCommandQueue;
android::FifoBuffer *mDataQueue;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 3a827f0..4c7d0f7 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -16,7 +16,7 @@
// This file is used in both client and server processes.
// This is needed to make sense of the logs more easily.
-#define LOG_TAG (mInService ? "AAudioService" : "AAudio")
+#define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -28,10 +28,10 @@
#include <binder/IServiceManager.h>
#include <aaudio/AAudio.h>
+#include <cutils/properties.h>
#include <utils/String16.h>
#include <utils/Trace.h>
-#include "AudioClock.h"
#include "AudioEndpointParcelable.h"
#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
@@ -39,6 +39,7 @@
#include "binding/AAudioServiceMessage.h"
#include "core/AudioStreamBuilder.h"
#include "fifo/FifoBuffer.h"
+#include "utility/AudioClock.h"
#include "utility/LinearRamp.h"
#include "AudioStreamInternal.h"
@@ -62,8 +63,14 @@
, mAudioEndpoint()
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mFramesPerBurst(16)
+ , mStreamVolume(1.0f)
+ , mInService(inService)
, mServiceInterface(serviceInterface)
- , mInService(inService) {
+ , mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
+ , mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
+ {
+ ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
+ mWakeupDelayNanos, mMinimumSleepNanos);
}
AudioStreamInternal::~AudioStreamInternal() {
@@ -85,13 +92,14 @@
setFormat(AAUDIO_FORMAT_PCM_FLOAT);
}
// Request FLOAT for the shared mixer.
- request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
request.setProcessId(getpid());
request.setDirection(getDirection());
request.setSharingModeMatchRequired(isSharingModeMatchRequired());
+ request.setInService(mInService);
request.getConfiguration().setDeviceId(getDeviceId());
request.getConfiguration().setSampleRate(getSampleRate());
@@ -114,9 +122,10 @@
setSampleRate(configuration.getSampleRate());
setSamplesPerFrame(configuration.getSamplesPerFrame());
setDeviceId(configuration.getDeviceId());
+ setSharingMode(configuration.getSharingMode());
// Save device format so we can do format conversion and volume scaling together.
- mDeviceFormat = configuration.getAudioFormat();
+ mDeviceFormat = configuration.getFormat();
result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
if (result != AAUDIO_OK) {
@@ -132,7 +141,7 @@
}
// Configure endpoint based on descriptor.
- mAudioEndpoint.configure(&mEndpointDescriptor);
+ mAudioEndpoint.configure(&mEndpointDescriptor, getDirection());
mFramesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
int32_t capacity = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
@@ -153,13 +162,13 @@
if (getDataCallbackProc()) {
mCallbackFrames = builder.getFramesPerDataCallback();
if (mCallbackFrames > getBufferCapacity() / 2) {
- ALOGE("AudioStreamInternal.open(): framesPerCallback too large = %d, capacity = %d",
+ ALOGE("AudioStreamInternal::open(): framesPerCallback too big = %d, capacity = %d",
mCallbackFrames, getBufferCapacity());
mServiceInterface.closeStream(mServiceStreamHandle);
return AAUDIO_ERROR_OUT_OF_RANGE;
} else if (mCallbackFrames < 0) {
- ALOGE("AudioStreamInternal.open(): framesPerCallback negative");
+ ALOGE("AudioStreamInternal::open(): framesPerCallback negative");
mServiceInterface.closeStream(mServiceStreamHandle);
return AAUDIO_ERROR_OUT_OF_RANGE;
@@ -175,12 +184,16 @@
}
setState(AAUDIO_STREAM_STATE_OPEN);
+ // only connect to AudioManager if this is a playback stream running in client process
+ if (!mInService && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ }
}
return result;
}
aaudio_result_t AudioStreamInternal::close() {
- ALOGD("AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X",
+ ALOGD("AudioStreamInternal::close(): mServiceStreamHandle = 0x%08X",
mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
// Don't close a stream while it is running.
@@ -196,12 +209,14 @@
result, AAudio_convertResultToText(result));
}
}
+ setState(AAUDIO_STREAM_STATE_CLOSING);
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
mServiceInterface.closeStream(serviceStreamHandle);
delete[] mCallbackBuffer;
mCallbackBuffer = nullptr;
+ setState(AAUDIO_STREAM_STATE_CLOSED);
return mEndPointParcelable.close();
} else {
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -223,15 +238,20 @@
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
- ALOGD("AudioStreamInternal(): start()");
+ ALOGD("AudioStreamInternal()::requestStart()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
+ if (isActive()) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ aaudio_stream_state_t originalState = getState();
+
+ setState(AAUDIO_STREAM_STATE_STARTING);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(startWithStatus());
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
- setState(AAUDIO_STREAM_STATE_STARTING);
- aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);;
if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
// Launch the callback loop thread.
@@ -241,6 +261,9 @@
mCallbackEnabled.store(true);
result = createThread(periodNanos, aaudio_callback_thread_proc, this);
}
+ if (result != AAUDIO_OK) {
+ setState(originalState);
+ }
return result;
}
@@ -271,66 +294,17 @@
}
}
-aaudio_result_t AudioStreamInternal::requestPauseInternal()
-{
- if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal(): requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
- mServiceStreamHandle);
- return AAUDIO_ERROR_INVALID_STATE;
- }
-
- mClockModel.stop(AudioClock::getNanoseconds());
- setState(AAUDIO_STREAM_STATE_PAUSING);
- return mServiceInterface.pauseStream(mServiceStreamHandle);
-}
-
-aaudio_result_t AudioStreamInternal::requestPause()
-{
- aaudio_result_t result = stopCallback();
- if (result != AAUDIO_OK) {
- return result;
- }
- result = requestPauseInternal();
- return result;
-}
-
-aaudio_result_t AudioStreamInternal::requestFlush() {
- if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal(): requestFlush() mServiceStreamHandle invalid = 0x%08X",
- mServiceStreamHandle);
- return AAUDIO_ERROR_INVALID_STATE;
- }
-
- setState(AAUDIO_STREAM_STATE_FLUSHING);
- return mServiceInterface.flushStream(mServiceStreamHandle);
-}
-
-// TODO for Play only
-void AudioStreamInternal::onFlushFromServer() {
- ALOGD("AudioStreamInternal(): onFlushFromServer()");
- int64_t readCounter = mAudioEndpoint.getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
-
- // Bump offset so caller does not see the retrograde motion in getFramesRead().
- int64_t framesFlushed = writeCounter - readCounter;
- mFramesOffsetFromService += framesFlushed;
-
- // Flush written frames by forcing writeCounter to readCounter.
- // This is because we cannot move the read counter in the hardware.
- mAudioEndpoint.setDataWriteCounter(readCounter);
-}
-
aaudio_result_t AudioStreamInternal::requestStopInternal()
{
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal(): requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+ ALOGE("AudioStreamInternal::requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_STOPPING);
- return mServiceInterface.stopStream(mServiceStreamHandle);
+ return AAudioConvert_androidToAAudioResult(stopWithStatus());
}
aaudio_result_t AudioStreamInternal::requestStop()
@@ -348,7 +322,6 @@
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.registerAudioThread(mServiceStreamHandle,
- getpid(),
gettid(),
getPeriodNanoseconds());
}
@@ -357,7 +330,22 @@
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, getpid(), gettid());
+ return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
+}
+
+aaudio_result_t AudioStreamInternal::startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return mServiceInterface.startClient(mServiceStreamHandle, client, clientHandle);
+}
+
+aaudio_result_t AudioStreamInternal::stopClient(audio_port_handle_t clientHandle) {
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return mServiceInterface.stopClient(mServiceStreamHandle, clientHandle);
}
aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
@@ -365,7 +353,7 @@
int64_t *timeNanoseconds) {
// TODO Generate in server and pass to client. Return latest.
int64_t time = AudioClock::getNanoseconds();
- *framePosition = mClockModel.convertTimeToPosition(time);
+ *framePosition = mClockModel.convertTimeToPosition(time) + mFramesOffsetFromService;
// TODO Get a more accurate timestamp from the service. This code just adds a fudge factor.
*timeNanoseconds = time + (6 * AAUDIO_NANOS_PER_MILLISECOND);
return AAUDIO_OK;
@@ -378,31 +366,28 @@
return processCommands();
}
-#if LOG_TIMESTAMPS
-static void AudioStreamInternal_logTimestamp(AAudioServiceMessage &command) {
+void AudioStreamInternal::logTimestamp(AAudioServiceMessage &command) {
static int64_t oldPosition = 0;
static int64_t oldTime = 0;
int64_t framePosition = command.timestamp.position;
int64_t nanoTime = command.timestamp.timestamp;
- ALOGD("AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %lld",
+ ALOGD("AudioStreamInternal: timestamp says framePosition = %08lld at nanoTime %lld",
(long long) framePosition,
(long long) nanoTime);
int64_t nanosDelta = nanoTime - oldTime;
if (nanosDelta > 0 && oldTime > 0) {
int64_t framesDelta = framePosition - oldPosition;
int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
- ALOGD("AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
- ALOGD("AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
- ALOGD("AudioStreamInternal() - measured rate = %lld", (long long) rate);
+ ALOGD("AudioStreamInternal: framesDelta = %08lld, nanosDelta = %08lld, rate = %lld",
+ (long long) framesDelta, (long long) nanosDelta, (long long) rate);
}
oldPosition = framePosition;
oldTime = nanoTime;
}
-#endif
aaudio_result_t AudioStreamInternal::onTimestampFromServer(AAudioServiceMessage *message) {
#if LOG_TIMESTAMPS
- AudioStreamInternal_logTimestamp(*message);
+ logTimestamp(*message);
#endif
processTimestamp(message->timestamp.position, message->timestamp.timestamp);
return AAUDIO_OK;
@@ -412,46 +397,48 @@
aaudio_result_t result = AAUDIO_OK;
switch (message->event.event) {
case AAUDIO_SERVICE_EVENT_STARTED:
- ALOGD("processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
+ ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_STARTED");
if (getState() == AAUDIO_STREAM_STATE_STARTING) {
setState(AAUDIO_STREAM_STATE_STARTED);
}
break;
case AAUDIO_SERVICE_EVENT_PAUSED:
- ALOGD("processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
+ ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_PAUSED");
if (getState() == AAUDIO_STREAM_STATE_PAUSING) {
setState(AAUDIO_STREAM_STATE_PAUSED);
}
break;
case AAUDIO_SERVICE_EVENT_STOPPED:
- ALOGD("processCommands() got AAUDIO_SERVICE_EVENT_STOPPED");
+ ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_STOPPED");
if (getState() == AAUDIO_STREAM_STATE_STOPPING) {
setState(AAUDIO_STREAM_STATE_STOPPED);
}
break;
case AAUDIO_SERVICE_EVENT_FLUSHED:
- ALOGD("processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD("AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_FLUSHED");
if (getState() == AAUDIO_STREAM_STATE_FLUSHING) {
setState(AAUDIO_STREAM_STATE_FLUSHED);
onFlushFromServer();
}
break;
case AAUDIO_SERVICE_EVENT_CLOSED:
- ALOGD("processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
+ ALOGD("AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_CLOSED");
setState(AAUDIO_STREAM_STATE_CLOSED);
break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
- ALOGW("WARNING - processCommands() AAUDIO_SERVICE_EVENT_DISCONNECTED");
+ ALOGW("WARNING - AudioStreamInternal::onEventFromServer()"
+ " AAUDIO_SERVICE_EVENT_DISCONNECTED");
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
- mVolumeRamp.setTarget((float) message->event.dataDouble);
- ALOGD("processCommands() AAUDIO_SERVICE_EVENT_VOLUME %lf",
+ mStreamVolume = (float)message->event.dataDouble;
+ doSetVolume();
+ ALOGD("AudioStreamInternal::onEventFromServer() AAUDIO_SERVICE_EVENT_VOLUME %lf",
message->event.dataDouble);
break;
default:
- ALOGW("WARNING - processCommands() Unrecognized event = %d",
+ ALOGW("WARNING - AudioStreamInternal::onEventFromServer() Unrecognized event = %d",
(int) message->event.event);
break;
}
@@ -491,29 +478,29 @@
aaudio_result_t AudioStreamInternal::processData(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
{
- const char * traceName = (mInService) ? "aaWrtS" : "aaWrtC";
+ const char * traceName = "aaProc";
+ const char * fifoName = "aaRdy";
ATRACE_BEGIN(traceName);
+ if (ATRACE_ENABLED()) {
+ int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ ATRACE_INT(fifoName, fullFrames);
+ }
+
aaudio_result_t result = AAUDIO_OK;
int32_t loopCount = 0;
uint8_t* audioData = (uint8_t*)buffer;
int64_t currentTimeNanos = AudioClock::getNanoseconds();
- int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
+ const int64_t entryTimeNanos = currentTimeNanos;
+ const int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
int32_t framesLeft = numFrames;
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
- if (ATRACE_ENABLED()) {
- const char * traceName = (mInService) ? "aaFullS" : "aaFullC";
- ATRACE_INT(traceName, fullFrames);
- }
-
// Loop until all the data has been processed or until a timeout occurs.
while (framesLeft > 0) {
- // The call to processDataNow() will not block. It will just read as much as it can.
+ // The call to processDataNow() will not block. It will just process as much as it can.
int64_t wakeTimeNanos = 0;
aaudio_result_t framesProcessed = processDataNow(audioData, framesLeft,
currentTimeNanos, &wakeTimeNanos);
if (framesProcessed < 0) {
- ALOGE("AudioStreamInternal::processData() loop: framesProcessed = %d", framesProcessed);
result = framesProcessed;
break;
}
@@ -524,28 +511,52 @@
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (framesLeft > 0) {
- // clip the wake time to something reasonable
- if (wakeTimeNanos < currentTimeNanos) {
- wakeTimeNanos = currentTimeNanos;
+ if (!mAudioEndpoint.isFreeRunning()) {
+ // If there is software on the other end of the FIFO then it may get delayed.
+ // So wake up just a little after we expect it to be ready.
+ wakeTimeNanos += mWakeupDelayNanos;
}
+
if (wakeTimeNanos > deadlineNanos) {
// If we time out, just return the framesWritten so far.
// TODO remove after we fix the deadline bug
- ALOGE("AudioStreamInternal::processData(): timed out after %lld nanos",
+ ALOGW("AudioStreamInternal::processData(): entered at %lld nanos, currently %lld",
+ (long long) entryTimeNanos, (long long) currentTimeNanos);
+ ALOGW("AudioStreamInternal::processData(): timed out after %lld nanos",
(long long) timeoutNanoseconds);
- ALOGE("AudioStreamInternal::processData(): wakeTime = %lld, deadline = %lld nanos",
+ ALOGW("AudioStreamInternal::processData(): wakeTime = %lld, deadline = %lld nanos",
(long long) wakeTimeNanos, (long long) deadlineNanos);
- ALOGE("AudioStreamInternal::processData(): past deadline by %d micros",
+ ALOGW("AudioStreamInternal::processData(): past deadline by %d micros",
(int)((wakeTimeNanos - deadlineNanos) / AAUDIO_NANOS_PER_MICROSECOND));
+ mClockModel.dump();
+ mAudioEndpoint.dump();
break;
}
- int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
- AudioClock::sleepForNanos(sleepForNanos);
+ currentTimeNanos = AudioClock::getNanoseconds();
+ int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
+ // Guarantee a minimum sleep time.
+ if (wakeTimeNanos < earliestWakeTime) {
+ wakeTimeNanos = earliestWakeTime;
+ }
+
+ if (ATRACE_ENABLED()) {
+ int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ ATRACE_INT(fifoName, fullFrames);
+ int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
+ ATRACE_INT("aaSlpNs", (int32_t)sleepForNanos);
+ }
+
+ AudioClock::sleepUntilNanoTime(wakeTimeNanos);
currentTimeNanos = AudioClock::getNanoseconds();
}
}
+ if (ATRACE_ENABLED()) {
+ int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ ATRACE_INT(fifoName, fullFrames);
+ }
+
// return error or framesProcessed
(void) loopCount;
ATRACE_END();
@@ -588,3 +599,32 @@
aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) {
return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst()));
}
+
+void AudioStreamInternal::doSetVolume() {
+ // No pan and only left volume is taken into account from IPLayer interface
+ mVolumeRamp.setTarget(mStreamVolume * mVolumeMultiplierL /* * mPanMultiplierL */);
+}
+
+
+//------------------------------------------------------------------------------
+// Implementation of PlayerBase
+status_t AudioStreamInternal::playerStart() {
+ return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.startStream(mServiceStreamHandle));
+}
+
+status_t AudioStreamInternal::playerPause() {
+ return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.pauseStream(mServiceStreamHandle));
+}
+
+status_t AudioStreamInternal::playerStop() {
+ return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.stopStream(mServiceStreamHandle));
+}
+
+status_t AudioStreamInternal::playerSetVolume() {
+ doSetVolume();
+ return NO_ERROR;
+}
+
+void AudioStreamInternal::destroy() {
+ baseDestroy();
+}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index a11f309..1b991de 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -18,6 +18,7 @@
#define ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
#include <stdint.h>
+#include <media/PlayerBase.h>
#include <aaudio/AAudio.h>
#include "binding/IAAudioService.h"
@@ -26,6 +27,7 @@
#include "client/IsochronousClockModel.h"
#include "client/AudioEndpoint.h"
#include "core/AudioStream.h"
+#include "utility/AudioClock.h"
#include "utility/LinearRamp.h"
using android::sp;
@@ -34,19 +36,14 @@
namespace aaudio {
// A stream that talks to the AAudioService or directly to a HAL.
-class AudioStreamInternal : public AudioStream {
+class AudioStreamInternal : public AudioStream, public android::PlayerBase {
public:
AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService);
virtual ~AudioStreamInternal();
- // =========== Begin ABSTRACT methods ===========================
aaudio_result_t requestStart() override;
- aaudio_result_t requestPause() override;
-
- aaudio_result_t requestFlush() override;
-
aaudio_result_t requestStop() override;
aaudio_result_t getTimestamp(clockid_t clockId,
@@ -54,7 +51,6 @@
int64_t *timeNanoseconds) override;
virtual aaudio_result_t updateStateWhileWaiting() override;
- // =========== End ABSTRACT methods ===========================
aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -89,6 +85,14 @@
// Calculate timeout based on framesPerBurst
int64_t calculateReasonableTimeout();
+ //PlayerBase virtuals
+ virtual void destroy();
+
+ aaudio_result_t startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle);
+
+ aaudio_result_t stopClient(audio_port_handle_t clientHandle);
+
protected:
aaudio_result_t processData(void *buffer,
@@ -109,21 +113,30 @@
aaudio_result_t processCommands();
- aaudio_result_t requestPauseInternal();
aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
- void onFlushFromServer();
+ virtual void onFlushFromServer() {}
aaudio_result_t onEventFromServer(AAudioServiceMessage *message);
aaudio_result_t onTimestampFromServer(AAudioServiceMessage *message);
+ void logTimestamp(AAudioServiceMessage &message);
+
// Calculate timeout for an operation involving framesPerOperation.
int64_t calculateReasonableTimeout(int32_t framesPerOperation);
+ void doSetVolume();
+
+ //PlayerBase virtuals
+ virtual status_t playerStart();
+ virtual status_t playerPause();
+ virtual status_t playerStop();
+ virtual status_t playerSetVolume();
+
aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
IsochronousClockModel mClockModel; // timing model for chasing the HAL
@@ -135,6 +148,7 @@
int32_t mXRunCount = 0; // how many underrun events?
LinearRamp mVolumeRamp;
+ float mStreamVolume;
// Offset from underlying frame position.
int64_t mFramesOffsetFromService = 0; // offset for timestamps
@@ -142,6 +156,11 @@
uint8_t *mCallbackBuffer = nullptr;
int32_t mCallbackFrames = 0;
+ // The service uses this for SHARED mode.
+ bool mInService = false; // Is this running in the client or the service?
+
+ AAudioServiceInterface &mServiceInterface; // abstract interface to the service
+
private:
/*
* Asynchronous write with data conversion.
@@ -155,12 +174,13 @@
// Adjust timing model based on timestamp from service.
void processTimestamp(uint64_t position, int64_t time);
+ // Thread on other side of FIFO will have wakeup jitter.
+ // By delaying slightly we can avoid waking up before other side is ready.
+ const int32_t mWakeupDelayNanos; // delay past typical wakeup jitter
+ const int32_t mMinimumSleepNanos; // minimum sleep while polling
+
AudioEndpointParcelable mEndPointParcelable; // description of the buffers filled by service
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
- AAudioServiceInterface &mServiceInterface; // abstract interface to the service
-
- // The service uses this for SHARED mode.
- bool mInService = false; // Is this running in the client or the service?
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 93693bd..7b1e53e 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -14,15 +14,19 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG (mInService ? "AAudioService" : "AAudio")
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <algorithm>
#include <aaudio/AAudio.h>
#include "client/AudioStreamInternalCapture.h"
#include "utility/AudioClock.h"
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+
using android::WrappingBuffer;
using namespace aaudio;
@@ -35,7 +39,6 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
-
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -51,6 +54,9 @@
return result;
}
+ const char *traceName = "aaRdNow";
+ ATRACE_BEGIN(traceName);
+
if (mAudioEndpoint.isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
@@ -62,6 +68,9 @@
// If the write index passed the read index then consider it an overrun.
if (mAudioEndpoint.getEmptyFramesAvailable() < 0) {
mXRunCount++;
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT("aaOverRuns", mXRunCount);
+ }
}
// Read some data from the buffer.
@@ -69,6 +78,9 @@
int32_t framesProcessed = readNowWithConversion(buffer, numFrames);
//ALOGD("AudioStreamInternalCapture::processDataNow() - tried to read %d frames, read %d",
// numFrames, framesProcessed);
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT("aaRead", framesProcessed);
+ }
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr && framesProcessed >= 0) {
@@ -81,14 +93,14 @@
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_STARTING:
break;
- case AAUDIO_STREAM_STATE_STARTED: // When do we expect the next read burst to occur?
+ case AAUDIO_STREAM_STATE_STARTED:
{
- uint32_t burstSize = mFramesPerBurst;
- if (burstSize < 32) {
- burstSize = 32; // TODO review
- }
+ // When do we expect the next write burst to occur?
- uint64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() + burstSize;
+ // Calculate frame position based off of the readCounter because
+ // the writeCounter might have just advanced in the background,
+ // causing us to sleep until a later burst.
+ int64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
@@ -98,10 +110,8 @@
*wakeTimePtr = wakeTime;
}
-// ALOGD("AudioStreamInternalCapture::readNow finished: now = %llu, read# = %llu, wrote# = %llu",
-// (unsigned long long)currentNanoTime,
-// (unsigned long long)mAudioEndpoint.getDataReadCounter(),
-// (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
+
+ ATRACE_END();
return framesProcessed;
}
@@ -155,29 +165,27 @@
int32_t framesProcessed = numFrames - framesLeft;
mAudioEndpoint.advanceReadIndex(framesProcessed);
- incrementFramesRead(framesProcessed);
//ALOGD("AudioStreamInternalCapture::readNowWithConversion() returns %d", framesProcessed);
return framesProcessed;
}
-int64_t AudioStreamInternalCapture::getFramesWritten()
-{
- int64_t frames =
- mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- + mFramesOffsetFromService;
- // Prevent retrograde motion.
- if (frames < mLastFramesWritten) {
- frames = mLastFramesWritten;
+int64_t AudioStreamInternalCapture::getFramesWritten() {
+ int64_t framesWrittenHardware;
+ if (isActive()) {
+ framesWrittenHardware = mClockModel.convertTimeToPosition(AudioClock::getNanoseconds());
} else {
- mLastFramesWritten = frames;
+ framesWrittenHardware = mAudioEndpoint.getDataWriteCounter();
}
- //ALOGD("AudioStreamInternalCapture::getFramesWritten() returns %lld", (long long)frames);
- return frames;
+ // Prevent retrograde motion.
+ mLastFramesWritten = std::max(mLastFramesWritten,
+ framesWrittenHardware + mFramesOffsetFromService);
+ //ALOGD("AudioStreamInternalCapture::getFramesWritten() returns %lld",
+ // (long long)mLastFramesWritten);
+ return mLastFramesWritten;
}
-int64_t AudioStreamInternalCapture::getFramesRead()
-{
+int64_t AudioStreamInternalCapture::getFramesRead() {
int64_t frames = mAudioEndpoint.getDataWriteCounter()
+ mFramesOffsetFromService;
//ALOGD("AudioStreamInternalCapture::getFramesRead() returns %lld", (long long)frames);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index fc9766f..31e0a40 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG (mInService ? "AAudioService" : "AAudio")
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include <utils/Trace.h>
+
#include "client/AudioStreamInternalPlay.h"
#include "utility/AudioClock.h"
@@ -34,6 +38,55 @@
AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
+aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
+{
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ mClockModel.stop(AudioClock::getNanoseconds());
+ setState(AAUDIO_STREAM_STATE_PAUSING);
+ return AAudioConvert_androidToAAudioResult(pauseWithStatus());
+}
+
+aaudio_result_t AudioStreamInternalPlay::requestPause()
+{
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+ result = requestPauseInternal();
+ return result;
+}
+
+aaudio_result_t AudioStreamInternalPlay::requestFlush() {
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ setState(AAUDIO_STREAM_STATE_FLUSHING);
+ return mServiceInterface.flushStream(mServiceStreamHandle);
+}
+
+void AudioStreamInternalPlay::onFlushFromServer() {
+ int64_t readCounter = mAudioEndpoint.getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+
+ // Bump offset so caller does not see the retrograde motion in getFramesRead().
+ int64_t framesFlushed = writeCounter - readCounter;
+ mFramesOffsetFromService += framesFlushed;
+ ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
+ (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
+
+ // Flush written frames by forcing writeCounter to readCounter.
+ // This is because we cannot move the read counter in the hardware.
+ mAudioEndpoint.setDataWriteCounter(readCounter);
+}
+
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -50,17 +103,23 @@
return result;
}
+ const char *traceName = "aaWrNow";
+ ATRACE_BEGIN(traceName);
+
+ // If a DMA channel or DSP is reading the other end then we have to update the readCounter.
if (mAudioEndpoint.isFreeRunning()) {
- //ALOGD("AudioStreamInternal::processDataNow() - update read counter");
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
+ // ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
}
- // TODO else query from endpoint cuz set by actual reader, maybe
// If the read index passed the write index then consider it an underrun.
if (mAudioEndpoint.getFullFramesAvailable() < 0) {
mXRunCount++;
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT("aaUnderRuns", mXRunCount);
+ }
}
// Write some data to the buffer.
@@ -68,6 +127,9 @@
int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
//ALOGD("AudioStreamInternal::processDataNow() - tried to write %d frames, wrote %d",
// numFrames, framesWritten);
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT("aaWrote", framesWritten);
+ }
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr && framesWritten >= 0) {
@@ -84,14 +146,15 @@
wakeTime = currentNanoTime;
}
break;
- case AAUDIO_STREAM_STATE_STARTED: // When do we expect the next read burst to occur?
+ case AAUDIO_STREAM_STATE_STARTED:
{
- uint32_t burstSize = mFramesPerBurst;
- if (burstSize < 32) {
- burstSize = 32; // TODO review
- }
+ // When do we expect the next read burst to occur?
- uint64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + burstSize;
+ // Calculate frame position based off of the writeCounter because
+ // the readCounter might have just advanced in the background,
+ // causing us to sleep until a later burst.
+ int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
+ - mAudioEndpoint.getBufferSizeInFrames();
wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
@@ -101,10 +164,8 @@
*wakeTimePtr = wakeTime;
}
-// ALOGD("AudioStreamInternal::processDataNow finished: now = %llu, read# = %llu, wrote# = %llu",
-// (unsigned long long)currentNanoTime,
-// (unsigned long long)mAudioEndpoint.getDataReadCounter(),
-// (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
+
+ ATRACE_END();
return framesWritten;
}
@@ -119,7 +180,7 @@
mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
- // Read data in one or two parts.
+ // Write data in one or two parts.
int partIndex = 0;
while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
int32_t framesToWrite = framesLeft;
@@ -201,9 +262,6 @@
int32_t framesWritten = numFrames - framesLeft;
mAudioEndpoint.advanceWriteIndex(framesWritten);
- if (framesWritten > 0) {
- incrementFramesWritten(framesWritten);
- }
// ALOGD("AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
return framesWritten;
}
@@ -211,25 +269,29 @@
int64_t AudioStreamInternalPlay::getFramesRead()
{
- int64_t framesRead =
- mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- + mFramesOffsetFromService;
+ int64_t framesReadHardware;
+ if (isActive()) {
+ framesReadHardware = mClockModel.convertTimeToPosition(AudioClock::getNanoseconds());
+ } else {
+ framesReadHardware = mAudioEndpoint.getDataReadCounter();
+ }
+ int64_t framesRead = framesReadHardware + mFramesOffsetFromService;
// Prevent retrograde motion.
if (framesRead < mLastFramesRead) {
framesRead = mLastFramesRead;
} else {
mLastFramesRead = framesRead;
}
- ALOGD("AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
+ //ALOGD("AudioStreamInternalPlay::getFramesRead() returns %lld", (long long)framesRead);
return framesRead;
}
int64_t AudioStreamInternalPlay::getFramesWritten()
{
- int64_t getFramesWritten = mAudioEndpoint.getDataWriteCounter()
+ int64_t framesWritten = mAudioEndpoint.getDataWriteCounter()
+ mFramesOffsetFromService;
- ALOGD("AudioStreamInternal::getFramesWritten() returns %lld", (long long)getFramesWritten);
- return getFramesWritten;
+ //ALOGD("AudioStreamInternalPlay::getFramesWritten() returns %lld", (long long)framesWritten);
+ return framesWritten;
}
@@ -239,6 +301,7 @@
aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
AAudioStream_dataCallback appCallback = getDataCallbackProc();
if (appCallback == nullptr) return NULL;
+ int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
@@ -250,10 +313,7 @@
mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
- // Write audio data to stream.
- int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
-
- // This is a BLOCKING WRITE!
+ // Write audio data to stream. This is a BLOCKING WRITE!
result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
ALOGE("AudioStreamInternalPlay(): callbackLoop: write() returned %d", result);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index b043f67..e59d02c 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -33,6 +33,10 @@
AudioStreamInternalPlay(AAudioServiceInterface &serviceInterface, bool inService = false);
virtual ~AudioStreamInternalPlay();
+ aaudio_result_t requestPause() override;
+
+ aaudio_result_t requestFlush() override;
+
aaudio_result_t write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
@@ -47,6 +51,11 @@
}
protected:
+
+ aaudio_result_t requestPauseInternal();
+
+ void onFlushFromServer() override;
+
/**
* Low level write that will not block. It will just write as much as it can.
*
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index edf6e97..c06c8a9 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -40,6 +40,13 @@
IsochronousClockModel::~IsochronousClockModel() {
}
+void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
+ ALOGV("IsochronousClockModel::setPositionAndTime(%lld, %lld)",
+ (long long) framePosition, (long long) nanoTime);
+ mMarkerFramePosition = framePosition;
+ mMarkerNanoTime = nanoTime;
+}
+
void IsochronousClockModel::start(int64_t nanoTime) {
ALOGD("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
mMarkerNanoTime = nanoTime;
@@ -48,8 +55,8 @@
void IsochronousClockModel::stop(int64_t nanoTime) {
ALOGD("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
- mMarkerNanoTime = nanoTime;
- mMarkerFramePosition = convertTimeToPosition(nanoTime); // TODO should we do this?
+ setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
+ // TODO should we set position?
mState = STATE_STOPPED;
}
@@ -78,15 +85,13 @@
case STATE_STOPPED:
break;
case STATE_STARTING:
- mMarkerFramePosition = framePosition;
- mMarkerNanoTime = nanoTime;
+ setPositionAndTime(framePosition, nanoTime);
mState = STATE_SYNCING;
break;
case STATE_SYNCING:
// This will handle a burst of rapid transfer at the beginning.
if (nanosDelta < expectedNanosDelta) {
- mMarkerFramePosition = framePosition;
- mMarkerNanoTime = nanoTime;
+ setPositionAndTime(framePosition, nanoTime);
} else {
// ALOGD("processTimestamp() - advance to STATE_RUNNING");
mState = STATE_RUNNING;
@@ -97,17 +102,15 @@
// Earlier than expected timestamp.
// This data is probably more accurate so use it.
// or we may be drifting due to a slow HW clock.
- mMarkerFramePosition = framePosition;
- mMarkerNanoTime = nanoTime;
// ALOGD("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
// (int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000));
+ setPositionAndTime(framePosition, nanoTime);
} else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
// Later than expected timestamp.
- mMarkerFramePosition = framePosition;
- mMarkerNanoTime = nanoTime - mMaxLatenessInNanos;
// ALOGD("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
// (int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000),
// (int) (mMaxLatenessInNanos / 1000));
+ setPositionAndTime(framePosition - mFramesPerBurst, nanoTime - mMaxLatenessInNanos);
}
break;
default:
@@ -130,8 +133,7 @@
mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS;
}
-int64_t IsochronousClockModel::convertDeltaPositionToTime(
- int64_t framesDelta) const {
+int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
return (AAUDIO_NANOS_PER_SECOND * framesDelta) / mSampleRate;
}
@@ -147,7 +149,7 @@
int64_t nextBurstPosition = mFramesPerBurst * nextBurstIndex;
int64_t framesDelta = nextBurstPosition - mMarkerFramePosition;
int64_t nanosDelta = convertDeltaPositionToTime(framesDelta);
- int64_t time = (int64_t) (mMarkerNanoTime + nanosDelta);
+ int64_t time = mMarkerNanoTime + nanosDelta;
// ALOGD("IsochronousClockModel::convertPositionToTime: pos = %llu --> time = %llu",
// (unsigned long long)framePosition,
// (unsigned long long)time);
@@ -170,3 +172,12 @@
// (long long) framesDelta, mFramesPerBurst);
return position;
}
+
+void IsochronousClockModel::dump() const {
+ ALOGD("IsochronousClockModel::mMarkerFramePosition = %lld", (long long) mMarkerFramePosition);
+ ALOGD("IsochronousClockModel::mMarkerNanoTime = %lld", (long long) mMarkerNanoTime);
+ ALOGD("IsochronousClockModel::mSampleRate = %6d", mSampleRate);
+ ALOGD("IsochronousClockModel::mFramesPerBurst = %6d", mFramesPerBurst);
+ ALOGD("IsochronousClockModel::mMaxLatenessInNanos = %6d", mMaxLatenessInNanos);
+ ALOGD("IsochronousClockModel::mState = %6d", mState);
+}
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 0314f55..585f53a 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -43,6 +43,8 @@
*/
void setSampleRate(int32_t sampleRate);
+ void setPositionAndTime(int64_t framePosition, int64_t nanoTime);
+
int32_t getSampleRate() const {
return mSampleRate;
}
@@ -86,6 +88,8 @@
*/
int64_t convertDeltaTimeToPosition(int64_t nanosDelta) const;
+ void dump() const;
+
private:
enum clock_model_state_t {
STATE_STOPPED,
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 76f98fa..3f5de77 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -253,8 +253,10 @@
AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- ALOGD("AAudioStream_requestStart(%p)", stream);
- return audioStream->requestStart();
+ ALOGD("AAudioStream_requestStart(%p) called --------------", stream);
+ aaudio_result_t result = audioStream->requestStart();
+ ALOGD("AAudioStream_requestStart(%p) returned ------------", stream);
+ return result;
}
AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream)
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
new file mode 100644
index 0000000..65c2b46
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 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 "AAudio"
+#include <utils/Log.h>
+#include <hardware/audio.h>
+
+#include "AAudioStreamParameters.h"
+
+using namespace aaudio;
+
+// TODO These defines should be moved to a central place in audio.
+#define SAMPLES_PER_FRAME_MIN 1
+// TODO Remove 8 channel limitation.
+#define SAMPLES_PER_FRAME_MAX FCC_8
+#define SAMPLE_RATE_HZ_MIN 8000
+// HDMI supports up to 32 channels at 1536000 Hz.
+#define SAMPLE_RATE_HZ_MAX 1600000
+
+AAudioStreamParameters::AAudioStreamParameters() {}
+AAudioStreamParameters::~AAudioStreamParameters() {}
+
+aaudio_result_t AAudioStreamParameters::validate() const {
+ if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
+ && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
+ ALOGE("AAudioStreamParameters: channelCount out of range = %d", mSamplesPerFrame);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ if (mDeviceId < 0) {
+ ALOGE("AAudioStreamParameters: deviceId out of range = %d", mDeviceId);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ switch (mSharingMode) {
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ case AAUDIO_SHARING_MODE_SHARED:
+ break;
+ default:
+ ALOGE("AAudioStreamParameters: illegal sharingMode = %d", mSharingMode);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ // break;
+ }
+
+ switch (mAudioFormat) {
+ case AAUDIO_FORMAT_UNSPECIFIED:
+ case AAUDIO_FORMAT_PCM_I16:
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ break; // valid
+ default:
+ ALOGE("AAudioStreamParameters: audioFormat not valid = %d", mAudioFormat);
+ return AAUDIO_ERROR_INVALID_FORMAT;
+ // break;
+ }
+
+ if (mSampleRate != AAUDIO_UNSPECIFIED
+ && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
+ ALOGE("AAudioStreamParameters: sampleRate out of range = %d", mSampleRate);
+ return AAUDIO_ERROR_INVALID_RATE;
+ }
+
+ if (mBufferCapacity < 0) {
+ ALOGE("AAudioStreamParameters: bufferCapacity out of range = %d", mBufferCapacity);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ return AAUDIO_OK;
+}
+
+void AAudioStreamParameters::dump() const {
+ ALOGD("AAudioStreamParameters mDeviceId = %d", mDeviceId);
+ ALOGD("AAudioStreamParameters mSampleRate = %d", mSampleRate);
+ ALOGD("AAudioStreamParameters mSamplesPerFrame = %d", mSamplesPerFrame);
+ ALOGD("AAudioStreamParameters mSharingMode = %d", (int)mSharingMode);
+ ALOGD("AAudioStreamParameters mAudioFormat = %d", (int)mAudioFormat);
+ ALOGD("AAudioStreamParameters mBufferCapacity = %d", mBufferCapacity);
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
new file mode 100644
index 0000000..97379cc
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 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 AAUDIO_STREAM_PARAMETERS_H
+#define AAUDIO_STREAM_PARAMETERS_H
+
+#include <stdint.h>
+
+#include <aaudio/AAudio.h>
+
+namespace aaudio {
+
+class AAudioStreamParameters {
+public:
+ AAudioStreamParameters();
+ virtual ~AAudioStreamParameters();
+
+ int32_t getDeviceId() const {
+ return mDeviceId;
+ }
+
+ void setDeviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ }
+
+ int32_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ int32_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ /**
+ * This is also known as channelCount.
+ */
+ void setSamplesPerFrame(int32_t samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ }
+
+ aaudio_format_t getFormat() const {
+ return mAudioFormat;
+ }
+
+ void setFormat(aaudio_format_t audioFormat) {
+ mAudioFormat = audioFormat;
+ }
+
+ aaudio_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ void setSharingMode(aaudio_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ }
+
+ int32_t getBufferCapacity() const {
+ return mBufferCapacity;
+ }
+
+ void setBufferCapacity(int32_t frames) {
+ mBufferCapacity = frames;
+ }
+
+ virtual aaudio_result_t validate() const;
+
+ void dump() const;
+
+private:
+ int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceId = AAUDIO_UNSPECIFIED;
+ aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_STREAM_PARAMETERS_H
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index e1e3c55..4859c69 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -36,8 +36,30 @@
setPeriodNanoseconds(0);
}
+static const char *AudioStream_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) {
+ const char *result;
+ switch (sharingMode) {
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ result = "EX";
+ break;
+ case AAUDIO_SHARING_MODE_SHARED:
+ result = "SH";
+ break;
+ default:
+ result = "?!";
+ break;
+ }
+ return result;
+}
+
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
{
+ // Call here as well because the AAudioService will call this without calling build().
+ aaudio_result_t result = builder.validate();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
// Copy parameters from the Builder because the Builder may be deleted after this call.
mSamplesPerFrame = builder.getSamplesPerFrame();
mSampleRate = builder.getSampleRate();
@@ -56,44 +78,14 @@
mErrorCallbackUserData = builder.getErrorCallbackUserData();
// This is very helpful for debugging in the future. Please leave it in.
- ALOGI("AudioStream::open() rate = %d, channels = %d, format = %d, sharing = %d, dir = %s",
- mSampleRate, mSamplesPerFrame, mFormat, mSharingMode,
+ ALOGI("AudioStream::open() rate = %d, channels = %d, format = %d, sharing = %s, dir = %s",
+ mSampleRate, mSamplesPerFrame, mFormat,
+ AudioStream_convertSharingModeToShortText(mSharingMode),
(getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "OUTPUT" : "INPUT");
- ALOGI("AudioStream::open() device = %d, perfMode = %d, callbackFrames = %d",
- mDeviceId, mPerformanceMode, mFramesPerDataCallback);
-
- // Check for values that are ridiculously out of range to prevent math overflow exploits.
- // The service will do a better check.
- if (mSamplesPerFrame < 0 || mSamplesPerFrame > 128) {
- ALOGE("AudioStream::open(): samplesPerFrame out of range = %d", mSamplesPerFrame);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
-
- switch(mFormat) {
- case AAUDIO_FORMAT_UNSPECIFIED:
- case AAUDIO_FORMAT_PCM_I16:
- case AAUDIO_FORMAT_PCM_FLOAT:
- break; // valid
- default:
- ALOGE("AudioStream::open(): audioFormat not valid = %d", mFormat);
- return AAUDIO_ERROR_INVALID_FORMAT;
- // break;
- }
-
- if (mSampleRate != AAUDIO_UNSPECIFIED && (mSampleRate < 8000 || mSampleRate > 1000000)) {
- ALOGE("AudioStream::open(): mSampleRate out of range = %d", mSampleRate);
- return AAUDIO_ERROR_INVALID_RATE;
- }
-
- switch(mPerformanceMode) {
- case AAUDIO_PERFORMANCE_MODE_NONE:
- case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
- case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
- break;
- default:
- ALOGE("AudioStream::open(): illegal performanceMode %d", mPerformanceMode);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- }
+ ALOGI("AudioStream::open() device = %d, perfMode = %d, callback: %s with frames = %d",
+ mDeviceId, mPerformanceMode,
+ (mDataCallbackProc == nullptr ? "OFF" : "ON"),
+ mFramesPerDataCallback);
return AAUDIO_OK;
}
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 39c9f9c..e5fdcc6 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -48,8 +48,18 @@
* Use waitForStateChange() to wait for completion.
*/
virtual aaudio_result_t requestStart() = 0;
- virtual aaudio_result_t requestPause() = 0;
- virtual aaudio_result_t requestFlush() = 0;
+
+ virtual aaudio_result_t requestPause()
+ {
+ // Only implement this for OUTPUT streams.
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual aaudio_result_t requestFlush() {
+ // Only implement this for OUTPUT streams.
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
virtual aaudio_result_t requestStop() = 0;
virtual aaudio_result_t getTimestamp(clockid_t clockId,
@@ -84,9 +94,7 @@
return AAUDIO_OK;
}
- virtual aaudio_result_t setBufferSize(int32_t requestedFrames) {
- return AAUDIO_ERROR_UNIMPLEMENTED;
- }
+ virtual aaudio_result_t setBufferSize(int32_t requestedFrames) = 0;
virtual aaudio_result_t createThread(int64_t periodNanoseconds,
aaudio_audio_thread_proc_t threadProc,
@@ -186,13 +194,9 @@
return AAudioConvert_formatToSizeInBytes(mFormat);
}
- virtual int64_t getFramesWritten() {
- return mFramesWritten.get();
- }
+ virtual int64_t getFramesWritten() = 0;
- virtual int64_t getFramesRead() {
- return mFramesRead.get();
- }
+ virtual int64_t getFramesRead() = 0;
AAudioStream_dataCallback getDataCallbackProc() const {
return mDataCallbackProc;
@@ -218,27 +222,20 @@
// ============== I/O ===========================
// A Stream will only implement read() or write() depending on its direction.
- virtual aaudio_result_t write(const void *buffer,
- int32_t numFrames,
- int64_t timeoutNanoseconds) {
+ virtual aaudio_result_t write(const void *buffer __unused,
+ int32_t numFrames __unused,
+ int64_t timeoutNanoseconds __unused) {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- virtual aaudio_result_t read(void *buffer,
- int32_t numFrames,
- int64_t timeoutNanoseconds) {
+ virtual aaudio_result_t read(void *buffer __unused,
+ int32_t numFrames __unused,
+ int64_t timeoutNanoseconds __unused) {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
protected:
- virtual int64_t incrementFramesWritten(int32_t frames) {
- return mFramesWritten.increment(frames);
- }
-
- virtual int64_t incrementFramesRead(int32_t frames) {
- return mFramesRead.increment(frames);
- }
/**
* This should not be called after the open() call.
@@ -281,8 +278,6 @@
std::atomic<bool> mCallbackEnabled;
protected:
- MonotonicCounter mFramesWritten;
- MonotonicCounter mFramesRead;
void setPeriodNanoseconds(int64_t periodNanoseconds) {
mPeriodNanoseconds.store(periodNanoseconds, std::memory_order_release);
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 4262f27..43a1ef1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -37,6 +37,19 @@
#define AAUDIO_MMAP_POLICY_DEFAULT AAUDIO_POLICY_NEVER
#define AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT AAUDIO_POLICY_NEVER
+// These values are for a pre-check before we ask the lower level service to open a stream.
+// So they are just outside the maximum conceivable range of value,
+// on the edge of being ridiculous.
+// TODO These defines should be moved to a central place in audio.
+#define SAMPLES_PER_FRAME_MIN 1
+// TODO Remove 8 channel limitation.
+#define SAMPLES_PER_FRAME_MAX FCC_8
+#define SAMPLE_RATE_HZ_MIN 8000
+// HDMI supports up to 32 channels at 1536000 Hz.
+#define SAMPLE_RATE_HZ_MAX 1600000
+#define FRAMES_PER_DATA_CALLBACK_MIN 1
+#define FRAMES_PER_DATA_CALLBACK_MAX (1024 * 1024)
+
/*
* AudioStreamBuilder
*/
@@ -85,8 +98,17 @@
// Exact behavior is controlled by MMapPolicy.
aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {
AudioStream *audioStream = nullptr;
+ if (streamPtr == nullptr) {
+ ALOGE("AudioStreamBuilder::build() streamPtr is null");
+ return AAUDIO_ERROR_NULL;
+ }
*streamPtr = nullptr;
+ aaudio_result_t result = validate();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
// The API setting is the highest priority.
aaudio_policy_t mmapPolicy = AAudio_getMMapPolicy();
// If not specified then get from a system property.
@@ -116,8 +138,13 @@
bool allowMMap = mmapPolicy != AAUDIO_POLICY_NEVER;
bool allowLegacy = mmapPolicy != AAUDIO_POLICY_ALWAYS;
- aaudio_result_t result = builder_createStream(getDirection(), sharingMode,
- allowMMap, &audioStream);
+ // TODO Support other performance settings in MMAP mode.
+ // Disable MMAP if low latency not requested.
+ if (getPerformanceMode() != AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) {
+ allowMMap = false;
+ }
+
+ result = builder_createStream(getDirection(), sharingMode, allowMMap, &audioStream);
if (result == AAUDIO_OK) {
// Open the stream using the parameters from the builder.
result = audioStream->open(*this);
@@ -147,3 +174,45 @@
return result;
}
+
+aaudio_result_t AudioStreamBuilder::validate() const {
+
+ // Check for values that are ridiculously out of range to prevent math overflow exploits.
+ // The service will do a better check.
+ aaudio_result_t result = AAudioStreamParameters::validate();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
+ switch (mDirection) {
+ case AAUDIO_DIRECTION_INPUT:
+ case AAUDIO_DIRECTION_OUTPUT:
+ break; // valid
+ default:
+ ALOGE("AudioStreamBuilder: direction not valid = %d", mDirection);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ // break;
+ }
+
+ switch (mPerformanceMode) {
+ case AAUDIO_PERFORMANCE_MODE_NONE:
+ case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+ case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+ break;
+ default:
+ ALOGE("AudioStreamBuilder: illegal performanceMode = %d", mPerformanceMode);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ // break;
+ }
+
+ // Prevent ridiculous values from causing problems.
+ if (mFramesPerDataCallback != AAUDIO_UNSPECIFIED
+ && (mFramesPerDataCallback < FRAMES_PER_DATA_CALLBACK_MIN
+ || mFramesPerDataCallback > FRAMES_PER_DATA_CALLBACK_MAX)) {
+ ALOGE("AudioStreamBuilder: framesPerDataCallback out of range = %d",
+ mFramesPerDataCallback);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ return AAUDIO_OK;
+}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index fd416c4..6e548b1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -21,6 +21,7 @@
#include <aaudio/AAudio.h>
+#include "AAudioStreamParameters.h"
#include "AudioStream.h"
namespace aaudio {
@@ -28,24 +29,12 @@
/**
* Factory class for an AudioStream.
*/
-class AudioStreamBuilder {
+class AudioStreamBuilder : public AAudioStreamParameters {
public:
AudioStreamBuilder();
~AudioStreamBuilder();
- int getSamplesPerFrame() const {
- return mSamplesPerFrame;
- }
-
- /**
- * This is also known as channelCount.
- */
- AudioStreamBuilder* setSamplesPerFrame(int samplesPerFrame) {
- mSamplesPerFrame = samplesPerFrame;
- return this;
- }
-
aaudio_direction_t getDirection() const {
return mDirection;
}
@@ -55,33 +44,6 @@
return this;
}
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- AudioStreamBuilder* setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- return this;
- }
-
- aaudio_format_t getFormat() const {
- return mFormat;
- }
-
- AudioStreamBuilder *setFormat(aaudio_format_t format) {
- mFormat = format;
- return this;
- }
-
- aaudio_sharing_mode_t getSharingMode() const {
- return mSharingMode;
- }
-
- AudioStreamBuilder* setSharingMode(aaudio_sharing_mode_t sharingMode) {
- mSharingMode = sharingMode;
- return this;
- }
-
bool isSharingModeMatchRequired() const {
return mSharingModeMatchRequired;
}
@@ -91,15 +53,6 @@
return this;
}
- int32_t getBufferCapacity() const {
- return mBufferCapacity;
- }
-
- AudioStreamBuilder* setBufferCapacity(int32_t frames) {
- mBufferCapacity = frames;
- return this;
- }
-
int32_t getPerformanceMode() const {
return mPerformanceMode;
}
@@ -109,15 +62,6 @@
return this;
}
- int32_t getDeviceId() const {
- return mDeviceId;
- }
-
- AudioStreamBuilder* setDeviceId(int32_t deviceId) {
- mDeviceId = deviceId;
- return this;
- }
-
AAudioStream_dataCallback getDataCallbackProc() const {
return mDataCallbackProc;
}
@@ -165,15 +109,11 @@
aaudio_result_t build(AudioStream **streamPtr);
+ virtual aaudio_result_t validate() const override;
+
private:
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mDeviceId = AAUDIO_UNSPECIFIED;
- aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
bool mSharingModeMatchRequired = false; // must match sharing mode requested
- aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
- int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp
index 6b4a772..8d2c62d 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -105,16 +105,18 @@
}
-void FifoBuffer::getFullDataAvailable(WrappingBuffer *wrappingBuffer) {
+fifo_frames_t FifoBuffer::getFullDataAvailable(WrappingBuffer *wrappingBuffer) {
fifo_frames_t framesAvailable = mFifo->getFullFramesAvailable();
fifo_frames_t startIndex = mFifo->getReadIndex();
fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex);
+ return framesAvailable;
}
-void FifoBuffer::getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer) {
+fifo_frames_t FifoBuffer::getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer) {
fifo_frames_t framesAvailable = mFifo->getEmptyFramesAvailable();
fifo_frames_t startIndex = mFifo->getWriteIndex();
fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex);
+ return framesAvailable;
}
fifo_frames_t FifoBuffer::read(void *buffer, fifo_frames_t numFrames) {
diff --git a/media/libaaudio/src/fifo/FifoBuffer.h b/media/libaaudio/src/fifo/FifoBuffer.h
index 2b262a1..a94e9b0 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.h
+++ b/media/libaaudio/src/fifo/FifoBuffer.h
@@ -64,16 +64,18 @@
* if the data is split across the end of the FIFO then set data2 and numFrames2.
* Other wise set them to null
* @param wrappingBuffer
+ * @return total full frames available
*/
- void getFullDataAvailable(WrappingBuffer *wrappingBuffer);
+ fifo_frames_t getFullDataAvailable(WrappingBuffer *wrappingBuffer);
/**
* Return pointer to available empty frames in data1 and set size in numFrames1.
* if the room is split across the end of the FIFO then set data2 and numFrames2.
* Other wise set them to null
* @param wrappingBuffer
+ * @return total empty frames available
*/
- void getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer);
+ fifo_frames_t getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer);
/**
* Copy data from the FIFO into the buffer.
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 0ded8e1..d2ef3c7 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -74,6 +74,15 @@
virtual int64_t incrementClientFrameCounter(int32_t frames) = 0;
+
+ virtual int64_t getFramesWritten() override {
+ return mFramesWritten.get();
+ }
+
+ virtual int64_t getFramesRead() override {
+ return mFramesRead.get();
+ }
+
protected:
class StreamDeviceCallback : public android::AudioSystem::AudioDeviceCallback
@@ -103,6 +112,17 @@
void onStart() { mCallbackEnabled.store(true); }
void onStop() { mCallbackEnabled.store(false); }
+ int64_t incrementFramesWritten(int32_t frames) {
+ return mFramesWritten.increment(frames);
+ }
+
+ int64_t incrementFramesRead(int32_t frames) {
+ return mFramesRead.increment(frames);
+ }
+
+ MonotonicCounter mFramesWritten;
+ MonotonicCounter mFramesRead;
+
FixedBlockAdapter *mBlockAdapter = nullptr;
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
int32_t mCallbackBufferSize = 0;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 156e83d..8e8070c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -55,7 +55,7 @@
// Try to create an AudioRecord
- // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ // TODO Support UNSPECIFIED in AudioRecord. For now, use stereo if unspecified.
int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
? 2 : getSamplesPerFrame();
audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
@@ -130,8 +130,8 @@
return AAudioConvert_androidToAAudioResult(status);
}
- // Get the actual rate.
- setSampleRate(mAudioRecord->getSampleRate());
+ // Get the actual values from the AudioRecord.
+ setSamplesPerFrame(mAudioRecord->channelCount());
setFormat(AAudioConvert_androidToAAudioDataFormat(mAudioRecord->format()));
int32_t actualSampleRate = mAudioRecord->getSampleRate();
@@ -223,18 +223,6 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamRecord::requestPause()
-{
- // This does not make sense for an input stream.
- // There is no real difference between pause() and stop().
- return AAUDIO_ERROR_UNIMPLEMENTED;
-}
-
-aaudio_result_t AudioStreamRecord::requestFlush() {
- // This does not make sense for an input stream.
- return AAUDIO_ERROR_UNIMPLEMENTED;
-}
-
aaudio_result_t AudioStreamRecord::requestStop() {
if (mAudioRecord.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 90000fc..2c6a7eb 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -41,8 +41,6 @@
aaudio_result_t close() override;
aaudio_result_t requestStart() override;
- aaudio_result_t requestPause() override;
- aaudio_result_t requestFlush() override;
aaudio_result_t requestStop() override;
virtual aaudio_result_t getTimestamp(clockid_t clockId,
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 7e39908..77f31e2 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -423,7 +423,7 @@
default:
break;
}
- return AudioStream::getFramesRead();
+ return AudioStreamLegacy::getFramesRead();
}
aaudio_result_t AudioStreamTrack::getTimestamp(clockid_t clockId,
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 164784d..2450920 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -208,9 +208,12 @@
status_t status;
switch (result) {
case AAUDIO_ERROR_DISCONNECTED:
- case AAUDIO_ERROR_INVALID_HANDLE:
+ case AAUDIO_ERROR_NO_SERVICE:
status = DEAD_OBJECT;
break;
+ case AAUDIO_ERROR_INVALID_HANDLE:
+ status = BAD_TYPE;
+ break;
case AAUDIO_ERROR_INVALID_STATE:
status = INVALID_OPERATION;
break;
@@ -226,14 +229,16 @@
case AAUDIO_ERROR_NULL:
status = UNEXPECTED_NULL;
break;
+ case AAUDIO_ERROR_UNAVAILABLE:
+ status = NOT_ENOUGH_DATA;
+ break;
+
// TODO translate these result codes
case AAUDIO_ERROR_INTERNAL:
case AAUDIO_ERROR_UNIMPLEMENTED:
- case AAUDIO_ERROR_UNAVAILABLE:
case AAUDIO_ERROR_NO_FREE_HANDLES:
case AAUDIO_ERROR_NO_MEMORY:
case AAUDIO_ERROR_TIMEOUT:
- case AAUDIO_ERROR_NO_SERVICE:
default:
status = UNKNOWN_ERROR;
break;
@@ -257,15 +262,18 @@
case INVALID_OPERATION:
result = AAUDIO_ERROR_INVALID_STATE;
break;
- case UNEXPECTED_NULL:
- result = AAUDIO_ERROR_NULL;
- break;
- case BAD_VALUE:
- result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- break;
+ case UNEXPECTED_NULL:
+ result = AAUDIO_ERROR_NULL;
+ break;
+ case BAD_VALUE:
+ result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ break;
case WOULD_BLOCK:
result = AAUDIO_ERROR_WOULD_BLOCK;
break;
+ case NOT_ENOUGH_DATA:
+ result = AAUDIO_ERROR_UNAVAILABLE;
+ break;
default:
result = AAUDIO_ERROR_INTERNAL;
break;
@@ -361,12 +369,43 @@
return prop;
}
+int32_t AAudioProperty_getWakeupDelayMicros() {
+ const int32_t minMicros = 0; // arbitrary
+ const int32_t defaultMicros = 200; // arbitrary, based on some observed jitter
+ const int32_t maxMicros = 5000; // arbitrary, probably don't want more than 500
+ int32_t prop = property_get_int32(AAUDIO_PROP_WAKEUP_DELAY_USEC, defaultMicros);
+ if (prop < minMicros) {
+ ALOGW("AAudioProperty_getWakeupDelayMicros: clipped %d to %d", prop, minMicros);
+ prop = minMicros;
+ } else if (prop > maxMicros) {
+ ALOGW("AAudioProperty_getWakeupDelayMicros: clipped %d to %d", prop, maxMicros);
+ prop = maxMicros;
+ }
+ return prop;
+}
+
+int32_t AAudioProperty_getMinimumSleepMicros() {
+ const int32_t minMicros = 20; // arbitrary
+ const int32_t defaultMicros = 200; // arbitrary
+ const int32_t maxMicros = 2000; // arbitrary
+ int32_t prop = property_get_int32(AAUDIO_PROP_MINIMUM_SLEEP_USEC, defaultMicros);
+ if (prop < minMicros) {
+ ALOGW("AAudioProperty_getMinimumSleepMicros: clipped %d to %d", prop, minMicros);
+ prop = minMicros;
+ } else if (prop > maxMicros) {
+ ALOGW("AAudioProperty_getMinimumSleepMicros: clipped %d to %d", prop, maxMicros);
+ prop = maxMicros;
+ }
+ return prop;
+}
+
int32_t AAudioProperty_getHardwareBurstMinMicros() {
const int32_t defaultMicros = 1000; // arbitrary
const int32_t maxMicros = 1000 * 1000; // arbitrary
int32_t prop = property_get_int32(AAUDIO_PROP_HW_BURST_MIN_USEC, defaultMicros);
if (prop < 1 || prop > maxMicros) {
- ALOGE("AAudioProperty_getHardwareBurstMinMicros: invalid = %d", prop);
+ ALOGE("AAudioProperty_getHardwareBurstMinMicros: invalid = %d, use %d",
+ prop, defaultMicros);
prop = defaultMicros;
}
return prop;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index f894bc0..acd319b 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -17,6 +17,8 @@
#ifndef UTILITY_AAUDIO_UTILITIES_H
#define UTILITY_AAUDIO_UTILITIES_H
+#include <algorithm>
+#include <functional>
#include <stdint.h>
#include <sys/types.h>
@@ -193,13 +195,35 @@
/**
* Read system property.
- * @return number of bursts per mixer cycle
+ * @return number of bursts per AAudio service mixer cycle
*/
int32_t AAudioProperty_getMixerBursts();
#define AAUDIO_PROP_HW_BURST_MIN_USEC "aaudio.hw_burst_min_usec"
/**
+ * Read a system property that specifies the number of extra microseconds that a thread
+ * should sleep when waiting for another thread to service a FIFO. This is used
+ * to avoid the waking thread from being overly optimistic about the other threads
+ * wakeup timing. This value should be set high enough to cover typical scheduling jitter
+ * for a real-time thread.
+ *
+ * @return number of microseconds to delay the wakeup.
+ */
+int32_t AAudioProperty_getWakeupDelayMicros();
+
+#define AAUDIO_PROP_WAKEUP_DELAY_USEC "aaudio.wakeup_delay_usec"
+
+/**
+ * Read a system property that specifies the minimum sleep time when polling the FIFO.
+ *
+ * @return minimum number of microseconds to sleep.
+ */
+int32_t AAudioProperty_getMinimumSleepMicros();
+
+#define AAUDIO_PROP_MINIMUM_SLEEP_USEC "aaudio.minimum_sleep_usec"
+
+/**
* Read system property.
* This is handy in case the DMA is bursting too quickly for the CPU to keep up.
* For example, there may be a DMA burst every 100 usec but you only
@@ -211,4 +235,27 @@
*/
int32_t AAudioProperty_getHardwareBurstMinMicros();
+/**
+ * Try a function f until it returns true.
+ *
+ * The function is always called at least once.
+ *
+ * @param f the function to evaluate, which returns a bool.
+ * @param times the number of times to evaluate f.
+ * @param sleepMs the sleep time per check of f, if greater than 0.
+ * @return true if f() eventually returns true.
+ */
+static inline bool AAudio_tryUntilTrue(
+ std::function<bool()> f, int times, int sleepMs) {
+ static const useconds_t US_PER_MS = 1000;
+
+ sleepMs = std::max(sleepMs, 0);
+ for (;;) {
+ if (f()) return true;
+ if (times <= 1) return false;
+ --times;
+ usleep(sleepMs * US_PER_MS);
+ }
+}
+
#endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/src/utility/HandleTracker.cpp b/media/libaaudio/src/utility/HandleTracker.cpp
index f957234..35ce95a 100644
--- a/media/libaaudio/src/utility/HandleTracker.cpp
+++ b/media/libaaudio/src/utility/HandleTracker.cpp
@@ -20,11 +20,15 @@
#include <utils/Log.h>
#include <assert.h>
+#include <functional>
+#include <iomanip>
#include <new>
+#include <sstream>
#include <stdint.h>
#include <utils/Mutex.h>
#include <aaudio/AAudio.h>
+#include "AAudioUtilities.h"
#include "HandleTracker.h"
using android::Mutex;
@@ -93,6 +97,47 @@
return mHandleAddresses != nullptr;
}
+
+
+std::string HandleTracker::dump() const {
+ if (!isInitialized()) {
+ return "HandleTracker is not initialized\n";
+ }
+
+ std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mLock.tryLock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "HandleTracker may be deadlocked\n";
+ }
+
+ result << "HandleTracker:\n";
+ result << " HandleHeaders:\n";
+ // atLineStart() can be changed to support an arbitrary line breaking algorithm;
+ // it should return true when a new line starts.
+ // For simplicity, we will use a constant 16 items per line.
+ const auto atLineStart = [](int index) -> bool {
+ // Magic constant of 0xf used for mask to detect start every 16 items.
+ return (index & 0xf) == 0; };
+ const auto atLineEnd = [this, &atLineStart](int index) -> bool {
+ return atLineStart(index + 1) || index == mMaxHandleCount - 1; };
+
+ for (int i = 0; i < mMaxHandleCount; ++i) {
+ if (atLineStart(i)) {
+ result << " ";
+ }
+ result << std::hex << std::setw(4) << std::setfill('0') << mHandleHeaders[i]
+ << (atLineEnd(i) ? "\n" : " ");
+ }
+
+ if (isLocked) {
+ mLock.unlock();
+ }
+ return result.str();
+}
+
handle_tracker_slot_t HandleTracker::allocateSlot_l() {
void **allocated = mNextFreeAddress;
if (allocated == nullptr) {
diff --git a/media/libaaudio/src/utility/HandleTracker.h b/media/libaaudio/src/utility/HandleTracker.h
index 23a73ed..a4c51c0 100644
--- a/media/libaaudio/src/utility/HandleTracker.h
+++ b/media/libaaudio/src/utility/HandleTracker.h
@@ -18,6 +18,7 @@
#define UTILITY_HANDLE_TRACKER_H
#include <stdint.h>
+#include <string>
#include <utils/Mutex.h>
typedef int32_t aaudio_handle_t;
@@ -53,6 +54,18 @@
bool isInitialized() const;
/**
+ * Returns HandleTracker information.
+ *
+ * Will attempt to get the object lock, but will proceed
+ * even if it cannot.
+ *
+ * Each line of information ends with a newline.
+ *
+ * @return a string representing the HandleTracker info.
+ */
+ std::string dump() const;
+
+ /**
* Store a pointer and return a handle that can be used to retrieve the pointer.
*
* It is safe to call put() or remove() from multiple threads.
@@ -99,7 +112,7 @@
// This Mutex protects the linked list of free nodes.
// The list is managed using mHandleAddresses and mNextFreeAddress.
// The data in mHandleHeaders is only changed by put() and remove().
- android::Mutex mLock;
+ mutable android::Mutex mLock;
/**
* Pull slot off of a list of empty slots.
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index afcdebf..e4eef06 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -6,9 +6,7 @@
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_handle_tracker.cpp
-LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
- libcutils liblog libmedia libutils libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_SHARED_LIBRARIES := libaaudio
LOCAL_MODULE := test_handle_tracker
include $(BUILD_NATIVE_TEST)
@@ -18,9 +16,7 @@
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_marshalling.cpp
-LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
- libcutils liblog libmedia libutils libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
LOCAL_MODULE := test_aaudio_marshalling
include $(BUILD_NATIVE_TEST)
@@ -30,9 +26,7 @@
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_block_adapter.cpp
-LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
- libcutils liblog libmedia libutils libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_SHARED_LIBRARIES := libaaudio
LOCAL_MODULE := test_block_adapter
include $(BUILD_NATIVE_TEST)
@@ -42,9 +36,7 @@
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_linear_ramp.cpp
-LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
- libcutils liblog libmedia libutils libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_SHARED_LIBRARIES := libaaudio
LOCAL_MODULE := test_linear_ramp
include $(BUILD_NATIVE_TEST)
@@ -54,8 +46,36 @@
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_open_params.cpp
-LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
- libcutils liblog libmedia libutils libaudiomanager
-LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
LOCAL_MODULE := test_open_params
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_no_close.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_no_close
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_recovery.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_aaudio_recovery
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_n_streams.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
+LOCAL_MODULE := test_n_streams
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_marshalling.cpp b/media/libaaudio/tests/test_marshalling.cpp
index 79beed6..c51fbce 100644
--- a/media/libaaudio/tests/test_marshalling.cpp
+++ b/media/libaaudio/tests/test_marshalling.cpp
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <math.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <cutils/ashmem.h>
@@ -28,6 +29,7 @@
#include <aaudio/AAudio.h>
#include <binding/AudioEndpointParcelable.h>
+using android::base::unique_fd;
using namespace android;
using namespace aaudio;
@@ -48,7 +50,7 @@
SharedMemoryParcelable sharedMemoryA;
SharedMemoryParcelable sharedMemoryB;
const size_t memSizeBytes = 840;
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemoryA.setup(fd, memSizeBytes);
void *region1;
@@ -81,7 +83,7 @@
SharedRegionParcelable sharedRegionA;
SharedRegionParcelable sharedRegionB;
const size_t memSizeBytes = 840;
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemories[0].setup(fd, memSizeBytes);
int32_t regionOffset1 = 32;
@@ -119,7 +121,7 @@
const int32_t counterSizeBytes = sizeof(int64_t);
const size_t memSizeBytes = dataSizeBytes + (2 * counterSizeBytes);
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling Z", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemories[0].setup(fd, memSizeBytes);
diff --git a/media/libaaudio/tests/test_n_streams.cpp b/media/libaaudio/tests/test_n_streams.cpp
new file mode 100644
index 0000000..271d024
--- /dev/null
+++ b/media/libaaudio/tests/test_n_streams.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Try to create as many streams as possible and report the maximum.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+
+//#define MMAP_POLICY AAUDIO_UNSPECIFIED
+//#define MMAP_POLICY AAUDIO_POLICY_NEVER
+#define MMAP_POLICY AAUDIO_POLICY_AUTO
+//#define MMAP_POLICY AAUDIO_POLICY_ALWAYS
+
+#define MAX_STREAMS 200
+
+aaudio_result_t testMaxStreams(aaudio_direction_t direction) {
+ aaudio_result_t result = AAUDIO_OK;
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStreams[MAX_STREAMS];
+ int32_t numStreams = 0;
+
+ result = AAudio_createStreamBuilder(&aaudioBuilder);
+ if (result != AAUDIO_OK) {
+ return 1;
+ }
+
+ AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
+
+ for (int i = 0; i < MAX_STREAMS; i++) {
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStreams[i]);
+ if (result != AAUDIO_OK) {
+ printf("ERROR could not open AAudio stream, %d %s\n",
+ result, AAudio_convertResultToText(result));
+ break;
+ } else {
+ printf("AAudio stream[%2d] opened successfully. MMAP = %s\n",
+ i, AAudioStream_isMMapUsed(aaudioStreams[i]) ? "YES" : "NO");
+ numStreams++;
+ }
+ }
+
+ printf("Created %d streams!\n", numStreams);
+
+ // Close all the streams.
+ for (int i = 0; i < numStreams; i++) {
+ result = AAudioStream_close(aaudioStreams[i]);
+ if (result != AAUDIO_OK) {
+ printf("ERROR could not close AAudio stream, %d %s\n",
+ result, AAudio_convertResultToText(result));
+ break;
+ } else {
+ printf("AAudio stream[%2d] closed successfully.\n", i);
+ }
+ }
+
+ AAudioStreamBuilder_delete(aaudioBuilder);
+
+finish:
+ return result;
+}
+
+int main(int argc, char **argv) {
+ (void)argc; // unused
+ (void)argv; // unused
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, NULL, _IONBF, (size_t) 0);
+
+ printf("Try to open a maximum of %d streams.\n", MAX_STREAMS);
+
+ AAudio_setMMapPolicy(MMAP_POLICY);
+ printf("requested MMapPolicy = %d\n", AAudio_getMMapPolicy());
+
+ printf("Test AAUDIO_DIRECTION_OUTPUT ---------\n");
+ aaudio_result_t result = testMaxStreams(AAUDIO_DIRECTION_OUTPUT);
+ if (result == AAUDIO_OK) {
+ printf("Test AAUDIO_DIRECTION_INPUT ---------\n");
+ result = testMaxStreams(AAUDIO_DIRECTION_INPUT);
+ }
+
+ return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/media/libaaudio/tests/test_no_close.cpp b/media/libaaudio/tests/test_no_close.cpp
new file mode 100644
index 0000000..2dbf153
--- /dev/null
+++ b/media/libaaudio/tests/test_no_close.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Try to create a resource leak in the server by opening a stream and then not closing it.
+// Return 0 if the stream opened, 1 if it failed.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <aaudio/AAudio.h>
+
+int main(int argc, char **argv)
+{
+ (void)argc; // unused
+ (void)argv; // unused
+
+ aaudio_result_t result = AAUDIO_OK;
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+ result = AAudio_createStreamBuilder(&aaudioBuilder);
+ if (result != AAUDIO_OK) {
+ goto finish;
+ }
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("ERROR could not open AAudio stream, %d\n", result);
+ goto finish;
+ } else {
+ printf("AAudio stream opened successfully.\n");
+ }
+
+ printf("Exit without closing the stream!\n");
+
+finish:
+ return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/media/libaaudio/tests/test_recovery.cpp b/media/libaaudio/tests/test_recovery.cpp
new file mode 100644
index 0000000..7268a30
--- /dev/null
+++ b/media/libaaudio/tests/test_recovery.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Play silence and recover from dead servers or disconnected devices.
+
+#include <stdio.h>
+
+#include <aaudio/AAudio.h>
+
+
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+
+static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
+ const char *modeText = "unknown";
+ switch (mode) {
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ modeText = "EXCLUSIVE";
+ break;
+ case AAUDIO_SHARING_MODE_SHARED:
+ modeText = "SHARED";
+ break;
+ default:
+ break;
+ }
+ return modeText;
+}
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void *)argv;
+
+ aaudio_result_t result = AAUDIO_OK;
+
+ int32_t triesLeft = 3;
+ int32_t bufferCapacity;
+ int32_t framesPerBurst = 0;
+ float *buffer = nullptr;
+
+ int32_t actualChannelCount = 0;
+ int32_t actualSampleRate = 0;
+ aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
+ aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
+
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("TestRecovery:\n");
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ result = AAudio_createStreamBuilder(&aaudioBuilder);
+ if (result != AAUDIO_OK) {
+ printf("AAudio_createStreamBuilder returned %s",
+ AAudio_convertResultToText(result));
+ goto finish;
+ }
+
+ // Request stream properties.
+ AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
+
+ while (triesLeft-- > 0) {
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStreamBuilder_openStream returned %s",
+ AAudio_convertResultToText(result));
+ goto finish;
+ }
+
+ // Check to see what kind of stream we actually got.
+ actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
+ actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
+ actualDataFormat = AAudioStream_getFormat(aaudioStream);
+
+ printf("-------- chans = %3d, rate = %6d format = %d\n",
+ actualChannelCount, actualSampleRate, actualDataFormat);
+
+ // This is the number of frames that are read in one chunk by a DMA controller
+ // or a DSP or a mixer.
+ framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
+ bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
+ printf(" bufferCapacity = %d, framesPerBurst = %d\n",
+ bufferCapacity, framesPerBurst);
+
+ int samplesPerBurst = framesPerBurst * actualChannelCount;
+ buffer = new float[samplesPerBurst];
+
+ result = AAudioStream_requestStart(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStart returned %s",
+ AAudio_convertResultToText(result));
+ goto finish;
+ }
+
+ // Play silence for awhile.
+ int32_t framesMax = actualSampleRate * 20;
+ int64_t framesTotal = 0;
+ int64_t printAt = actualSampleRate;
+ while (result == AAUDIO_OK && framesTotal < framesMax) {
+ int32_t framesWritten = AAudioStream_write(aaudioStream,
+ buffer, framesPerBurst,
+ DEFAULT_TIMEOUT_NANOS);
+ if (framesWritten < 0) {
+ result = framesWritten;
+ printf("write() returned %s, frames = %d\n",
+ AAudio_convertResultToText(result), (int)framesTotal);
+ printf(" frames = %d\n", (int)framesTotal);
+ } else if (framesWritten != framesPerBurst) {
+ printf("write() returned %d, frames = %d\n", framesWritten, (int)framesTotal);
+ result = AAUDIO_ERROR_TIMEOUT;
+ } else {
+ framesTotal += framesWritten;
+ if (framesTotal >= printAt) {
+ printf("frames = %d\n", (int)framesTotal);
+ printAt += actualSampleRate;
+ }
+ }
+ }
+ result = AAudioStream_requestStop(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStop returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ result = AAudioStream_close(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_close returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ aaudioStream = nullptr;
+ }
+
+finish:
+ if (aaudioStream != nullptr) {
+ AAudioStream_close(aaudioStream);
+ }
+ AAudioStreamBuilder_delete(aaudioBuilder);
+ delete[] buffer;
+ printf(" result = %d = %s\n", result, AAudio_convertResultToText(result));
+}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index e749ac4..611cde7 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -69,7 +69,8 @@
: mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mPortId(AUDIO_PORT_HANDLE_NONE)
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mPortId(AUDIO_PORT_HANDLE_NONE)
{
}
@@ -503,10 +504,29 @@
if (mInput == AUDIO_IO_HANDLE_NONE) {
return AUDIO_PORT_HANDLE_NONE;
}
- return AudioSystem::getDeviceIdForIo(mInput);
+ // if the input stream does not have an active audio patch, use either the device initially
+ // selected by audio policy manager or the last routed device
+ audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mInput);
+ if (deviceId == AUDIO_PORT_HANDLE_NONE) {
+ deviceId = mRoutedDeviceId;
+ }
+ mRoutedDeviceId = deviceId;
+ return deviceId;
}
// -------------------------------------------------------------------------
+// TODO Move this macro to a common header file for enum to string conversion in audio framework.
+#define MEDIA_CASE_ENUM(name) case name: return #name
+const char * AudioRecord::convertTransferToText(transfer_type transferType) {
+ switch (transferType) {
+ MEDIA_CASE_ENUM(TRANSFER_DEFAULT);
+ MEDIA_CASE_ENUM(TRANSFER_CALLBACK);
+ MEDIA_CASE_ENUM(TRANSFER_OBTAIN);
+ MEDIA_CASE_ENUM(TRANSFER_SYNC);
+ default:
+ return "UNRECOGNIZED";
+ }
+}
// must be called with mLock held
status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
@@ -538,13 +558,14 @@
.channel_mask = mChannelMask,
.format = mFormat
};
+ mRoutedDeviceId = mSelectedDeviceId;
status = AudioSystem::getInputForAttr(&mAttributes, &input,
mSessionId,
// FIXME compare to AudioTrack
mClientPid,
mClientUid,
&config,
- mFlags, mSelectedDeviceId, &mPortId);
+ mFlags, &mRoutedDeviceId, &mPortId);
if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE) {
ALOGE("Could not get audio input for session %d, record source %d, sample rate %u, "
@@ -590,12 +611,20 @@
(mTransfer == TRANSFER_SYNC) ||
// use case 3: obtain/release mode
(mTransfer == TRANSFER_OBTAIN);
+ if (!useCaseAllowed) {
+ ALOGW("AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s",
+ convertTransferToText(mTransfer));
+ }
+
// sample rates must also match
- bool fastAllowed = useCaseAllowed && (mSampleRate == afSampleRate);
+ bool sampleRateAllowed = mSampleRate == afSampleRate;
+ if (!sampleRateAllowed) {
+ ALOGW("AUDIO_INPUT_FLAG_FAST denied, rates do not match %u Hz, require %u Hz",
+ mSampleRate, afSampleRate);
+ }
+
+ bool fastAllowed = useCaseAllowed && sampleRateAllowed;
if (!fastAllowed) {
- ALOGW("AUDIO_INPUT_FLAG_FAST denied by client; transfer %d, "
- "track %u Hz, input %u Hz",
- mTransfer, mSampleRate, afSampleRate);
mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
AUDIO_INPUT_FLAG_RAW));
AudioSystem::releaseInput(input, mSessionId);
@@ -1272,6 +1301,7 @@
{
AutoMutex _l(mMyLock);
if (mPaused) {
+ // TODO check return value and handle or log
mMyCond.wait(mMyLock);
// caller will check for exitPending()
return true;
@@ -1282,8 +1312,10 @@
}
if (mPausedInt) {
if (mPausedNs > 0) {
+ // TODO check return value and handle or log
(void) mMyCond.waitRelative(mMyLock, mPausedNs);
} else {
+ // TODO check return value and handle or log
mMyCond.wait(mMyLock);
}
mPausedInt = false;
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 9ef1db7..2f710bd 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -819,7 +819,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -863,7 +863,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
@@ -1225,6 +1225,13 @@
return aps->getMasterMono(mono);
}
+float AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return NAN;
+ return aps->getStreamVolumeDB(stream, index, device);
+}
+
// ---------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ffb7703..b0b01db 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -184,6 +184,7 @@
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
mPortId(AUDIO_PORT_HANDLE_NONE)
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
@@ -908,13 +909,13 @@
effectiveRate, effectiveSpeed, effectivePitch);
if (!isAudioPlaybackRateValid(playbackRateTemp)) {
- ALOGV("setPlaybackRate(%f, %f) failed (effective rate out of bounds)",
+ ALOGW("setPlaybackRate(%f, %f) failed (effective rate out of bounds)",
playbackRate.mSpeed, playbackRate.mPitch);
return BAD_VALUE;
}
// Check if the buffer size is compatible.
if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) {
- ALOGV("setPlaybackRate(%f, %f) failed (buffer size)",
+ ALOGW("setPlaybackRate(%f, %f) failed (buffer size)",
playbackRate.mSpeed, playbackRate.mPitch);
return BAD_VALUE;
}
@@ -922,13 +923,13 @@
// Check resampler ratios are within bounds
if ((uint64_t)effectiveRate > (uint64_t)mSampleRate *
(uint64_t)AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
- ALOGV("setPlaybackRate(%f, %f) failed. Resample rate exceeds max accepted value",
+ ALOGW("setPlaybackRate(%f, %f) failed. Resample rate exceeds max accepted value",
playbackRate.mSpeed, playbackRate.mPitch);
return BAD_VALUE;
}
if ((uint64_t)effectiveRate * (uint64_t)AUDIO_RESAMPLER_UP_RATIO_MAX < (uint64_t)mSampleRate) {
- ALOGV("setPlaybackRate(%f, %f) failed. Resample rate below min accepted value",
+ ALOGW("setPlaybackRate(%f, %f) failed. Resample rate below min accepted value",
playbackRate.mSpeed, playbackRate.mPitch);
return BAD_VALUE;
}
@@ -1226,7 +1227,14 @@
if (mOutput == AUDIO_IO_HANDLE_NONE) {
return AUDIO_PORT_HANDLE_NONE;
}
- return AudioSystem::getDeviceIdForIo(mOutput);
+ // if the output stream does not have an active audio patch, use either the device initially
+ // selected by audio policy manager or the last routed device
+ audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mOutput);
+ if (deviceId == AUDIO_PORT_HANDLE_NONE) {
+ deviceId = mRoutedDeviceId;
+ }
+ mRoutedDeviceId = deviceId;
+ return deviceId;
}
status_t AudioTrack::attachAuxEffect(int effectId)
@@ -1247,9 +1255,41 @@
return mStreamType;
}
+uint32_t AudioTrack::latency()
+{
+ AutoMutex lock(mLock);
+ updateLatency_l();
+ return mLatency;
+}
+
// -------------------------------------------------------------------------
// must be called with mLock held
+void AudioTrack::updateLatency_l()
+{
+ status_t status = AudioSystem::getLatency(mOutput, &mAfLatency);
+ if (status != NO_ERROR) {
+ ALOGW("getLatency(%d) failed status %d", mOutput, status);
+ } else {
+ // FIXME don't believe this lie
+ mLatency = mAfLatency + (1000 * mFrameCount) / mSampleRate;
+ }
+}
+
+// TODO Move this macro to a common header file for enum to string conversion in audio framework.
+#define MEDIA_CASE_ENUM(name) case name: return #name
+const char * AudioTrack::convertTransferToText(transfer_type transferType) {
+ switch (transferType) {
+ MEDIA_CASE_ENUM(TRANSFER_DEFAULT);
+ MEDIA_CASE_ENUM(TRANSFER_CALLBACK);
+ MEDIA_CASE_ENUM(TRANSFER_OBTAIN);
+ MEDIA_CASE_ENUM(TRANSFER_SYNC);
+ MEDIA_CASE_ENUM(TRANSFER_SHARED);
+ default:
+ return "UNRECOGNIZED";
+ }
+}
+
status_t AudioTrack::createTrack_l()
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -1274,10 +1314,11 @@
config.channel_mask = mChannelMask;
config.format = mFormat;
config.offload_info = mOffloadInfoCopy;
+ mRoutedDeviceId = mSelectedDeviceId;
status = AudioSystem::getOutputForAttr(attr, &output,
mSessionId, &streamType, mClientUid,
&config,
- mFlags, mSelectedDeviceId, &mPortId);
+ mFlags, &mRoutedDeviceId, &mPortId);
if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u,"
@@ -1325,22 +1366,32 @@
// Client can only express a preference for FAST. Server will perform additional tests.
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
- bool useCaseAllowed =
- // either of these use cases:
- // use case 1: shared buffer
- (mSharedBuffer != 0) ||
+ // either of these use cases:
+ // use case 1: shared buffer
+ bool sharedBuffer = mSharedBuffer != 0;
+ bool transferAllowed =
// use case 2: callback transfer mode
(mTransfer == TRANSFER_CALLBACK) ||
// use case 3: obtain/release mode
(mTransfer == TRANSFER_OBTAIN) ||
// use case 4: synchronous write
((mTransfer == TRANSFER_SYNC) && mThreadCanCallJava);
+
+ bool useCaseAllowed = sharedBuffer || transferAllowed;
+ if (!useCaseAllowed) {
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied, not shared buffer and transfer = %s",
+ convertTransferToText(mTransfer));
+ }
+
// sample rates must also match
- bool fastAllowed = useCaseAllowed && (mSampleRate == mAfSampleRate);
+ bool sampleRateAllowed = mSampleRate == mAfSampleRate;
+ if (!sampleRateAllowed) {
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied, rates do not match %u Hz, require %u Hz",
+ mSampleRate, mAfSampleRate);
+ }
+
+ bool fastAllowed = useCaseAllowed && sampleRateAllowed;
if (!fastAllowed) {
- ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client; transfer %d, "
- "track %u Hz, output %u Hz",
- mTransfer, mSampleRate, mAfSampleRate);
mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
}
@@ -1416,6 +1467,9 @@
pid_t tid = -1;
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ // It is currently meaningless to request SCHED_FIFO for a Java thread. Even if the
+ // application-level code follows all non-blocking design rules, the language runtime
+ // doesn't also follow those rules, so the thread will not benefit overall.
if (mAudioTrackThread != 0 && !mThreadCanCallJava) {
tid = mAudioTrackThread->getTid();
}
@@ -1541,11 +1595,9 @@
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
- // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
- // FIXME don't believe this lie
- mLatency = mAfLatency + (1000*frameCount) / mSampleRate;
-
mFrameCount = frameCount;
+ updateLatency_l(); // this refetches mAfLatency and sets mLatency
+
// If IAudioTrack is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
if (frameCount > mReqFrameCount) {
@@ -2315,8 +2367,9 @@
return mPosition;
}
-bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed)
{
+ updateLatency_l();
// applicable for mixing tracks only (not offloaded or direct)
if (mStaticProxy != 0) {
return true; // static tracks do not have issues with buffer sizing.
@@ -2324,9 +2377,14 @@
const size_t minFrameCount =
calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed
/*, 0 mNotificationsPerBufferReq*/);
- ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu",
+ const bool allowed = mFrameCount >= minFrameCount;
+ ALOGD_IF(!allowed,
+ "isSampleRateSpeedAllowed_l denied "
+ "mAfLatency:%u mAfFrameCount:%zu mAfSampleRate:%u sampleRate:%u speed:%f "
+ "mFrameCount:%zu < minFrameCount:%zu",
+ mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed,
mFrameCount, minFrameCount);
- return mFrameCount >= minFrameCount;
+ return allowed;
}
status_t AudioTrack::setParameters(const String8& keyValuePairs)
@@ -2470,6 +2528,7 @@
status = ets.getBestTimestamp(×tamp, &location);
if (status == OK) {
+ updateLatency_l();
// It is possible that the best location has moved from the kernel to the server.
// In this case we adjust the position from the previous computed latency.
if (location == ExtendedTimestamp::LOCATION_SERVER) {
@@ -2941,6 +3000,7 @@
{
AutoMutex _l(mMyLock);
if (mPaused) {
+ // TODO check return value and handle or log
mMyCond.wait(mMyLock);
// caller will check for exitPending()
return true;
@@ -2950,9 +3010,12 @@
mPausedInt = false;
}
if (mPausedInt) {
+ // TODO use futex instead of condition, for event flag "or"
if (mPausedNs > 0) {
+ // TODO check return value and handle or log
(void) mMyCond.waitRelative(mMyLock, mPausedNs);
} else {
+ // TODO check return value and handle or log
mMyCond.wait(mMyLock);
}
mPausedInt = false;
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index 2ce6c63..08c37f8 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -111,7 +111,8 @@
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
struct timespec *elapsed)
{
- LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
+ LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
+ "%s: null or zero frame buffer, buffer:%p", __func__, buffer);
struct timespec total; // total elapsed time spent waiting
total.tv_sec = 0;
total.tv_nsec = 0;
@@ -345,7 +346,10 @@
buffer->mNonContig = 0;
return;
}
- LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount));
+ LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
+ "%s: mUnreleased out of range, "
+ "!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu), BufferSizeInFrames:%u",
+ __func__, stepCount, mUnreleased, mFrameCount, getBufferSizeInFrames());
mUnreleased -= stepCount;
audio_track_cblk_t* cblk = mCblk;
// Both of these barriers are required
@@ -674,7 +678,8 @@
__attribute__((no_sanitize("integer")))
status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
- LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
+ LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
+ "%s: null or zero frame buffer, buffer:%p", __func__, buffer);
if (mIsShutdown) {
goto no_init;
}
@@ -760,7 +765,10 @@
buffer->mNonContig = 0;
return;
}
- LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount));
+ LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
+ "%s: mUnreleased out of range, "
+ "!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu)",
+ __func__, stepCount, mUnreleased, mFrameCount);
mUnreleased -= stepCount;
audio_track_cblk_t* cblk = mCblk;
if (mIsOut) {
@@ -1029,7 +1037,9 @@
}
// As mFramesReady is the total remaining frames in the static audio track,
// it is always larger or equal to avail.
- LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail);
+ LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail,
+ "%s: mFramesReady out of range, mFramesReady:%lld < avail:%zu",
+ __func__, (long long)mFramesReady, avail);
buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail);
if (!ackFlush) {
mUnreleased = avail;
@@ -1040,8 +1050,14 @@
void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
{
size_t stepCount = buffer->mFrameCount;
- LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady));
- LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased));
+ LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady),
+ "%s: stepCount out of range, "
+ "!(stepCount:%zu <= mFramesReady:%lld)",
+ __func__, stepCount, (long long)mFramesReady);
+ LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased),
+ "%s: stepCount out of range, "
+ "!(stepCount:%zu <= mUnreleased:%zu)",
+ __func__, stepCount, mUnreleased);
if (stepCount == 0) {
// prevent accidental re-use of buffer
buffer->mRaw = NULL;
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 858b5cc..14feada 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -62,7 +62,7 @@
SET_VOICE_VOLUME,
GET_RENDER_POSITION,
GET_INPUT_FRAMES_LOST,
- NEW_AUDIO_SESSION_ID,
+ NEW_AUDIO_UNIQUE_ID,
ACQUIRE_AUDIO_SESSION_ID,
RELEASE_AUDIO_SESSION_ID,
QUERY_NUM_EFFECTS,
@@ -80,7 +80,7 @@
RELEASE_AUDIO_PATCH,
LIST_AUDIO_PATCHES,
SET_AUDIO_PORT_CONFIG,
- GET_AUDIO_HW_SYNC,
+ GET_AUDIO_HW_SYNC_FOR_SESSION,
SYSTEM_READY,
FRAME_COUNT_HAL,
};
@@ -628,8 +628,8 @@
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32((int32_t) use);
- status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply);
- audio_unique_id_t id = AUDIO_SESSION_ALLOCATE;
+ status_t status = remote()->transact(NEW_AUDIO_UNIQUE_ID, data, &reply);
+ audio_unique_id_t id = AUDIO_UNIQUE_ID_ALLOCATE;
if (status == NO_ERROR) {
id = reply.readInt32();
}
@@ -912,7 +912,7 @@
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(sessionId);
- status_t status = remote()->transact(GET_AUDIO_HW_SYNC, data, &reply);
+ status_t status = remote()->transact(GET_AUDIO_HW_SYNC_FOR_SESSION, data, &reply);
if (status != NO_ERROR) {
return AUDIO_HW_SYNC_INVALID;
}
@@ -1262,7 +1262,7 @@
reply->writeInt32((int32_t) getInputFramesLost(ioHandle));
return NO_ERROR;
} break;
- case NEW_AUDIO_SESSION_ID: {
+ case NEW_AUDIO_UNIQUE_ID: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
reply->writeInt32(newAudioUniqueId((audio_unique_id_use_t) data.readInt32()));
return NO_ERROR;
@@ -1466,7 +1466,7 @@
reply->writeInt32(status);
return NO_ERROR;
} break;
- case GET_AUDIO_HW_SYNC: {
+ case GET_AUDIO_HW_SYNC_FOR_SESSION: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
reply->writeInt32(getAudioHwSyncForSession((audio_session_t) data.readInt32()));
return NO_ERROR;
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index f0f413d..ceba211 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <stdint.h>
+#include <math.h>
#include <sys/types.h>
#include <binder/Parcel.h>
@@ -77,6 +78,7 @@
SET_AUDIO_PORT_CALLBACK_ENABLED,
SET_MASTER_MONO,
GET_MASTER_MONO,
+ GET_STREAM_VOLUME_DB
};
#define MAX_ITEMS_PER_LIST 1024
@@ -191,7 +193,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
Parcel data, reply;
@@ -210,6 +212,10 @@
ALOGE("getOutputForAttr NULL output - shouldn't happen");
return BAD_VALUE;
}
+ if (selectedDeviceId == NULL) {
+ ALOGE("getOutputForAttr NULL selectedDeviceId - shouldn't happen");
+ return BAD_VALUE;
+ }
if (portId == NULL) {
ALOGE("getOutputForAttr NULL portId - shouldn't happen");
return BAD_VALUE;
@@ -230,7 +236,7 @@
data.writeInt32(uid);
data.write(config, sizeof(audio_config_t));
data.writeInt32(static_cast <uint32_t>(flags));
- data.writeInt32(selectedDeviceId);
+ data.writeInt32(*selectedDeviceId);
data.writeInt32(*portId);
status_t status = remote()->transact(GET_OUTPUT_FOR_ATTR, data, &reply);
if (status != NO_ERROR) {
@@ -245,6 +251,7 @@
if (stream != NULL) {
*stream = lStream;
}
+ *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
*portId = (audio_port_handle_t)reply.readInt32();
return status;
}
@@ -294,7 +301,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
Parcel data, reply;
@@ -307,17 +314,22 @@
ALOGE("getInputForAttr NULL input - shouldn't happen");
return BAD_VALUE;
}
+ if (selectedDeviceId == NULL) {
+ ALOGE("getInputForAttr NULL selectedDeviceId - shouldn't happen");
+ return BAD_VALUE;
+ }
if (portId == NULL) {
ALOGE("getInputForAttr NULL portId - shouldn't happen");
return BAD_VALUE;
}
data.write(attr, sizeof(audio_attributes_t));
+ data.writeInt32(*input);
data.writeInt32(session);
data.writeInt32(pid);
data.writeInt32(uid);
data.write(config, sizeof(audio_config_base_t));
data.writeInt32(flags);
- data.writeInt32(selectedDeviceId);
+ data.writeInt32(*selectedDeviceId);
data.writeInt32(*portId);
status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply);
if (status != NO_ERROR) {
@@ -328,6 +340,7 @@
return status;
}
*input = (audio_io_handle_t)reply.readInt32();
+ *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
*portId = (audio_port_handle_t)reply.readInt32();
return NO_ERROR;
}
@@ -815,6 +828,20 @@
}
return status;
}
+
+ virtual float getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <int32_t>(stream));
+ data.writeInt32(static_cast <int32_t>(index));
+ data.writeUint32(static_cast <uint32_t>(device));
+ status_t status = remote()->transact(GET_STREAM_VOLUME_DB, data, &reply);
+ if (status != NO_ERROR) {
+ return NAN;
+ }
+ return reply.readFloat();
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -952,10 +979,11 @@
status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
&output, session, &stream, uid,
&config,
- flags, selectedDeviceId, &portId);
+ flags, &selectedDeviceId, &portId);
reply->writeInt32(status);
reply->writeInt32(output);
reply->writeInt32(stream);
+ reply->writeInt32(selectedDeviceId);
reply->writeInt32(portId);
return NO_ERROR;
} break;
@@ -997,6 +1025,7 @@
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_attributes_t attr;
data.read(&attr, sizeof(audio_attributes_t));
+ audio_io_handle_t input = (audio_io_handle_t)data.readInt32();
audio_session_t session = (audio_session_t)data.readInt32();
pid_t pid = (pid_t)data.readInt32();
uid_t uid = (uid_t)data.readInt32();
@@ -1006,13 +1035,13 @@
audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
- audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
status_t status = getInputForAttr(&attr, &input, session, pid, uid,
&config,
- flags, selectedDeviceId, &portId);
+ flags, &selectedDeviceId, &portId);
reply->writeInt32(status);
if (status == NO_ERROR) {
reply->writeInt32(input);
+ reply->writeInt32(selectedDeviceId);
reply->writeInt32(portId);
}
return NO_ERROR;
@@ -1405,6 +1434,17 @@
return NO_ERROR;
} break;
+ case GET_STREAM_VOLUME_DB: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_stream_type_t stream =
+ static_cast <audio_stream_type_t>(data.readInt32());
+ int index = static_cast <int>(data.readInt32());
+ audio_devices_t device =
+ static_cast <audio_devices_t>(data.readUint32());
+ reply->writeFloat(getStreamVolumeDB(stream, index, device));
+ return NO_ERROR;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libaudioclient/PlayerBase.cpp b/media/libaudioclient/PlayerBase.cpp
index cbef1b3..7868318 100644
--- a/media/libaudioclient/PlayerBase.cpp
+++ b/media/libaudioclient/PlayerBase.cpp
@@ -79,7 +79,7 @@
}
}
-//FIXME temporary method while some AudioTrack state is outside of this class
+//FIXME temporary method while some player state is outside of this class
void PlayerBase::reportEvent(player_state_t event) {
servicePlayerEvent(event);
}
@@ -87,10 +87,30 @@
status_t PlayerBase::startWithStatus() {
status_t status = playerStart();
if (status == NO_ERROR) {
- ALOGD("PlayerBase::start() from IPlayer");
servicePlayerEvent(PLAYER_STATE_STARTED);
} else {
- ALOGD("PlayerBase::start() no AudioTrack to start from IPlayer");
+ ALOGW("PlayerBase::start() error %d", status);
+ }
+ return status;
+}
+
+status_t PlayerBase::pauseWithStatus() {
+ status_t status = playerPause();
+ if (status == NO_ERROR) {
+ servicePlayerEvent(PLAYER_STATE_PAUSED);
+ } else {
+ ALOGW("PlayerBase::pause() error %d", status);
+ }
+ return status;
+}
+
+
+status_t PlayerBase::stopWithStatus() {
+ status_t status = playerStop();
+ if (status == NO_ERROR) {
+ servicePlayerEvent(PLAYER_STATE_STOPPED);
+ } else {
+ ALOGW("PlayerBase::stop() error %d", status);
}
return status;
}
@@ -98,42 +118,36 @@
//------------------------------------------------------------------------------
// Implementation of IPlayer
void PlayerBase::start() {
+ ALOGD("PlayerBase::start() from IPlayer");
(void)startWithStatus();
}
void PlayerBase::pause() {
- if (playerPause() == NO_ERROR) {
- ALOGD("PlayerBase::pause() from IPlayer");
- servicePlayerEvent(PLAYER_STATE_PAUSED);
- } else {
- ALOGD("PlayerBase::pause() no AudioTrack to pause from IPlayer");
- }
+ ALOGD("PlayerBase::pause() from IPlayer");
+ (void)pauseWithStatus();
}
void PlayerBase::stop() {
- if (playerStop() == NO_ERROR) {
- ALOGD("PlayerBase::stop() from IPlayer");
- servicePlayerEvent(PLAYER_STATE_STOPPED);
- } else {
- ALOGD("PlayerBase::stop() no AudioTrack to stop from IPlayer");
- }
+ ALOGD("PlayerBase::stop() from IPlayer");
+ (void)stopWithStatus();
}
void PlayerBase::setVolume(float vol) {
+ ALOGD("PlayerBase::setVolume() from IPlayer");
{
Mutex::Autolock _l(mSettingsLock);
mVolumeMultiplierL = vol;
mVolumeMultiplierR = vol;
}
- if (playerSetVolume() == NO_ERROR) {
- ALOGD("PlayerBase::setVolume() from IPlayer");
- } else {
- ALOGD("PlayerBase::setVolume() no AudioTrack for volume control from IPlayer");
+ status_t status = playerSetVolume();
+ if (status != NO_ERROR) {
+ ALOGW("PlayerBase::setVolume() error %d", status);
}
}
void PlayerBase::setPan(float pan) {
+ ALOGD("PlayerBase::setPan() from IPlayer");
{
Mutex::Autolock _l(mSettingsLock);
pan = min(max(-1.0f, pan), 1.0f);
@@ -145,10 +159,9 @@
mPanMultiplierR = 1.0f + pan;
}
}
- if (playerSetVolume() == NO_ERROR) {
- ALOGD("PlayerBase::setPan() from IPlayer");
- } else {
- ALOGD("PlayerBase::setPan() no AudioTrack for volume control from IPlayer");
+ status_t status = playerSetVolume();
+ if (status != NO_ERROR) {
+ ALOGW("PlayerBase::setPan() error %d", status);
}
}
diff --git a/media/libaudioclient/include/media/AudioClient.h b/media/libaudioclient/include/media/AudioClient.h
new file mode 100644
index 0000000..9efd76d
--- /dev/null
+++ b/media/libaudioclient/include/media/AudioClient.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_AUDIO_CLIENT_H
+#define ANDROID_AUDIO_CLIENT_H
+
+#include <system/audio.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class AudioClient {
+ public:
+ AudioClient() :
+ clientUid(-1), clientPid(-1), packageName("") {}
+
+ uid_t clientUid;
+ pid_t clientPid;
+ String16 packageName;
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_CLIENT_H
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 87ada76..2bd2d01 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -286,7 +286,7 @@
process_hook_t hook; // one of process__*, never NULL
int32_t *outputTemp;
int32_t *resampleTemp;
- NBLog::Writer* mLog;
+ NBLog::Writer* mNBLogWriter; // associated NBLog::Writer or &mDummyLog
int32_t reserved[1];
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
@@ -301,9 +301,11 @@
const uint32_t mSampleRate;
- NBLog::Writer mDummyLog;
+ NBLog::Writer mDummyLogWriter;
public:
- void setLog(NBLog::Writer* log);
+ // Called by FastMixer to inform AudioMixer of it's associated NBLog::Writer.
+ // FIXME It would be safer to use TLS for this, so we don't accidentally use wrong one.
+ void setNBLogWriter(NBLog::Writer* log);
private:
state_t mState __attribute__((aligned(32)));
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 1b034b5..e6a5efb 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -334,6 +334,12 @@
*/
status_t getTimestamp(ExtendedTimestamp *timestamp);
+ /**
+ * @param transferType
+ * @return text string that matches the enum name
+ */
+ static const char * convertTransferToText(transfer_type transferType);
+
/* Returns a handle on the audio input used by this AudioRecord.
*
* Parameters:
@@ -655,7 +661,10 @@
// For Device Selection API
// a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
- audio_port_handle_t mSelectedDeviceId;
+ audio_port_handle_t mSelectedDeviceId; // Device requested by the application.
+ audio_port_handle_t mRoutedDeviceId; // Device actually selected by audio policy manager:
+ // May not match the app selection depending on other
+ // activity and connected devices
sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
audio_port_handle_t mPortId; // unique ID allocated by audio policy
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 853d318..2e39d23 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -224,7 +224,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -245,7 +245,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
static status_t startInput(audio_io_handle_t input,
@@ -338,6 +338,9 @@
static status_t setMasterMono(bool mono);
static status_t getMasterMono(bool *mono);
+ static float getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device);
+
// ----------------------------------------------------------------------------
class AudioPortCallback : public RefBase
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index a4c8d53..b168fc9 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -326,7 +326,7 @@
* This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
* and audio hardware driver.
*/
- uint32_t latency() const { return mLatency; }
+ uint32_t latency();
/* Returns the number of application-level buffer underruns
* since the AudioTrack was created.
@@ -564,6 +564,12 @@
*/
status_t reload();
+ /**
+ * @param transferType
+ * @return text string that matches the enum name
+ */
+ static const char * convertTransferToText(transfer_type transferType);
+
/* Returns a handle on the audio output used by this AudioTrack.
*
* Parameters:
@@ -927,6 +933,8 @@
// caller must hold lock on mLock for all _l methods
+ void updateLatency_l(); // updates mAfLatency and mLatency from AudioSystem cache
+
status_t createTrack_l();
// can only be called when mState != STATE_ACTIVE
@@ -962,7 +970,7 @@
Modulo<uint32_t> updateAndGetPosition_l();
// check sample rate and speed is compatible with AudioTrack
- bool isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+ bool isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed);
void restartIfDisabled();
@@ -1133,7 +1141,10 @@
// For Device Selection API
// a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
- audio_port_handle_t mSelectedDeviceId;
+ audio_port_handle_t mSelectedDeviceId; // Device requested by the application.
+ audio_port_handle_t mRoutedDeviceId; // Device actually selected by audio policy manager:
+ // May not match the app selection depending on other
+ // activity and connected devices.
sp<VolumeHandler> mVolumeHandler;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index d111fd2..9b3e35e 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -68,7 +68,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -86,7 +86,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session) = 0;
@@ -169,6 +169,8 @@
virtual status_t setMasterMono(bool mono) = 0;
virtual status_t getMasterMono(bool *mono) = 0;
+ virtual float getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device) = 0;
};
diff --git a/media/libaudioclient/include/media/PlayerBase.h b/media/libaudioclient/include/media/PlayerBase.h
index fe1db7b..e63090b 100644
--- a/media/libaudioclient/include/media/PlayerBase.h
+++ b/media/libaudioclient/include/media/PlayerBase.h
@@ -48,6 +48,8 @@
status_t startWithStatus();
+ status_t pauseWithStatus();
+ status_t stopWithStatus();
//FIXME temporary method while some player state is outside of this class
void reportEvent(player_state_t event);
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index ae1be09..238925d 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -114,7 +114,7 @@
mState.hook = process__nop;
mState.outputTemp = NULL;
mState.resampleTemp = NULL;
- mState.mLog = &mDummyLog;
+ mState.mNBLogWriter = &mDummyLogWriter;
// mState.reserved
// FIXME Most of the following initialization is probably redundant since
@@ -145,9 +145,9 @@
delete [] mState.resampleTemp;
}
-void AudioMixer::setLog(NBLog::Writer *log)
+void AudioMixer::setNBLogWriter(NBLog::Writer *logWriter)
{
- mState.mLog = log;
+ mState.mNBLogWriter = logWriter;
}
static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) {
diff --git a/media/libcpustats/Android.bp b/media/libcpustats/Android.bp
index 3ea96c9..8fcd8a4 100644
--- a/media/libcpustats/Android.bp
+++ b/media/libcpustats/Android.bp
@@ -10,4 +10,13 @@
"-Werror",
"-Wall",
],
+
+ host_supported: true,
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
}
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index b43b36c..4b7549f 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -26,6 +26,11 @@
#include <cpustats/ThreadCpuUsage.h>
+// implemented by host, but not declared in <string.h> as FreeBSD does
+extern "C" {
+ extern size_t strlcpy(char *dst, const char *src, size_t dstsize);
+}
+
namespace android {
bool ThreadCpuUsage::setEnabled(bool isEnabled)
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index f27d5ca..7c685ec 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -447,6 +447,10 @@
return -EINVAL;
}
effect_param_t *cmd = (effect_param_t *) pCmdData;
+ if (cmd->psize != sizeof(int32_t)) {
+ android_errorWriteLog(0x534e4554, "63662938");
+ return -EINVAL;
+ }
*(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
cmd->vsize, cmd->data + sizeof(int32_t));
break;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 12a038f..6d7d4cd 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -181,12 +181,13 @@
void Reverb_free (ReverbContext *pContext);
int Reverb_setConfig (ReverbContext *pContext, effect_config_t *pConfig);
void Reverb_getConfig (ReverbContext *pContext, effect_config_t *pConfig);
-int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue);
+int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue, int vsize);
int Reverb_getParameter (ReverbContext *pContext,
void *pParam,
uint32_t *pValueSize,
void *pValue);
int Reverb_LoadPreset (ReverbContext *pContext);
+int Reverb_paramValueSize (int32_t param);
/* Effect Library Interface Implementation */
@@ -1746,12 +1747,13 @@
// pContext - handle to instance data
// pParam - pointer to parameter
// pValue - pointer to value
+// vsize - value size
//
// Outputs:
//
//----------------------------------------------------------------------------
-int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue){
+int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue, int vsize){
int status = 0;
int16_t level;
int16_t ratio;
@@ -1775,6 +1777,11 @@
return 0;
}
+ if (vsize < Reverb_paramValueSize(param)) {
+ android_errorWriteLog(0x534e4554, "63526567");
+ return -EINVAL;
+ }
+
switch (param){
case REVERB_PARAM_PROPERTIES:
ALOGV("\tReverb_setParameter() REVERB_PARAM_PROPERTIES");
@@ -1850,6 +1857,31 @@
return status;
} /* end Reverb_setParameter */
+
+/**
+ * returns the size in bytes of the value of each environmental reverb parameter
+ */
+int Reverb_paramValueSize(int32_t param) {
+ switch (param) {
+ case REVERB_PARAM_ROOM_LEVEL:
+ case REVERB_PARAM_ROOM_HF_LEVEL:
+ case REVERB_PARAM_REFLECTIONS_LEVEL:
+ case REVERB_PARAM_REVERB_LEVEL:
+ return sizeof(int16_t); // millibel
+ case REVERB_PARAM_DECAY_TIME:
+ case REVERB_PARAM_REFLECTIONS_DELAY:
+ case REVERB_PARAM_REVERB_DELAY:
+ return sizeof(uint32_t); // milliseconds
+ case REVERB_PARAM_DECAY_HF_RATIO:
+ case REVERB_PARAM_DIFFUSION:
+ case REVERB_PARAM_DENSITY:
+ return sizeof(int16_t); // permille
+ case REVERB_PARAM_PROPERTIES:
+ return sizeof(s_reverb_settings); // struct of all reverb properties
+ }
+ return sizeof(int32_t);
+}
+
} // namespace
} // namespace
@@ -2020,7 +2052,8 @@
*(int *)pReplyData = android::Reverb_setParameter(pContext,
(void *)p->data,
- p->data + p->psize);
+ p->data + p->psize,
+ p->vsize);
} break;
case EFFECT_CMD_ENABLE:
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index 0502638..358da8b 100644
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -3,6 +3,7 @@
# audio preprocessing wrapper
include $(CLEAR_VARS)
+LOCAL_VENDOR_MODULE := true
LOCAL_MODULE:= libaudiopreprocessing
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_RELATIVE_PATH := soundfx
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 750ff2d..bbe97ee 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -13,9 +13,12 @@
"-Wno-error=deprecated-declarations",
"-Wall",
],
- shared: {
- shared_libs: ["libutils", "liblog"],
- },
+ shared_libs: ["libutils", "liblog", "libgui"],
+ header_libs: [
+ "libmedia_headers",
+ "libaudioclient_headers",
+ "libaudio_system_headers",
+ ],
clang: true,
}
diff --git a/media/libmedia/aidl/android/IGraphicBufferSource.aidl b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
index f3c7abc..12c2767 100644
--- a/media/libmedia/aidl/android/IGraphicBufferSource.aidl
+++ b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
@@ -31,6 +31,7 @@
void setTimeLapseConfig(double fps, double captureFps);
void setStartTimeUs(long startTimeUs);
void setStopTimeUs(long stopTimeUs);
+ long getStopTimeOffsetUs();
void setColorAspects(int aspects);
void setTimeOffsetUs(long timeOffsetsUs);
void signalEndOfInputStream();
diff --git a/media/libmedia/include/media/OMXBuffer.h b/media/libmedia/include/media/OMXBuffer.h
index 6f79182..3e84858 100644
--- a/media/libmedia/include/media/OMXBuffer.h
+++ b/media/libmedia/include/media/OMXBuffer.h
@@ -19,7 +19,6 @@
#include <cutils/native_handle.h>
#include <media/IOMX.h>
-#include <system/window.h>
#include <utils/StrongPointer.h>
#include <hidl/HidlSupport.h>
diff --git a/media/libmedia/include/media/convert.h b/media/libmedia/include/media/convert.h
index 980b5d5..036c611 100644
--- a/media/libmedia/include/media/convert.h
+++ b/media/libmedia/include/media/convert.h
@@ -119,7 +119,7 @@
/* Check for a '-' in string. If type is unsigned and a - is found, the
* parsing fails. This is made necessary because "-1" is read as 65535 for
* uint16_t, for example */
- if (str.find("-") != std::string::npos
+ if (str.find('-') != std::string::npos
&& !std::numeric_limits<T>::is_signed) {
return false;
}
diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
index 4c543fa..31d1df9 100644
--- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
@@ -67,6 +67,14 @@
return toBinderStatus(mBase->setStopTimeUs(stopTimeUs));
}
+BnStatus LWGraphicBufferSource::getStopTimeOffsetUs(
+ int64_t *stopTimeOffsetUs) {
+ return toBinderStatus(mBase->getStopTimeOffsetUs(
+ [stopTimeOffsetUs](auto, auto offsetUs) {
+ *stopTimeOffsetUs = offsetUs;
+ }));
+}
+
BnStatus LWGraphicBufferSource::setColorAspects(
int32_t aspects) {
return toBinderStatus(mBase->setColorAspects(
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 18fd857..942a8fc 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -83,6 +83,9 @@
#include "HTTPBase.h"
#include "RemoteDisplay.h"
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleepUs = 20000;
+
namespace {
using android::media::Metadata;
using android::status_t;
@@ -392,12 +395,32 @@
snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
mPid, mConnId, mStatus, mLoop?"true": "false");
result.append(buffer);
- write(fd, result.string(), result.size());
- if (mPlayer != NULL) {
- mPlayer->dump(fd, args);
+
+ sp<MediaPlayerBase> p;
+ sp<AudioOutput> audioOutput;
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleepUs);
}
- if (mAudioOutput != 0) {
- mAudioOutput->dump(fd, args);
+
+ if (locked) {
+ p = mPlayer;
+ audioOutput = mAudioOutput;
+ mLock.unlock();
+ } else {
+ result.append(" lock is taken, no dump from player and audio output\n");
+ }
+ write(fd, result.string(), result.size());
+
+ if (p != NULL) {
+ p->dump(fd, args);
+ }
+ if (audioOutput != 0) {
+ audioOutput->dump(fd, args);
}
write(fd, "\n", 1);
return NO_ERROR;
@@ -577,7 +600,10 @@
MediaPlayerService::Client::~Client()
{
ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
- mAudioOutput.clear();
+ {
+ Mutex::Autolock l(mLock);
+ mAudioOutput.clear();
+ }
wp<Client> client(this);
disconnect();
mService->removeClient(client);
@@ -597,10 +623,9 @@
Mutex::Autolock l(mLock);
p = mPlayer;
mClient.clear();
+ mPlayer.clear();
}
- mPlayer.clear();
-
// clear the notification to prevent callbacks to dead client
// and reset the player. We assume the player will serialize
// access to itself if necessary.
@@ -621,7 +646,7 @@
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// determine if we have the right player type
- sp<MediaPlayerBase> p = mPlayer;
+ sp<MediaPlayerBase> p = getPlayer();
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();
@@ -772,6 +797,7 @@
}
if (mStatus == OK) {
+ Mutex::Autolock l(mLock);
mPlayer = p;
}
}
@@ -2292,6 +2318,33 @@
}
}
} else {
+ // VolumeShapers are not affected when a track moves between players for
+ // gapless playback (setNextMediaPlayer).
+ // We forward VolumeShaper operations that do not change configuration
+ // to the new player so that unducking may occur as expected.
+ // Unducking is an idempotent operation, same if applied back-to-back.
+ if (configuration->getType() == VolumeShaper::Configuration::TYPE_ID
+ && mNextOutput != nullptr) {
+ ALOGV("applyVolumeShaper: Attempting to forward missed operation: %s %s",
+ configuration->toString().c_str(), operation->toString().c_str());
+ Mutex::Autolock nextLock(mNextOutput->mLock);
+
+ // recycled track should be forwarded from this AudioSink by switchToNextOutput
+ sp<AudioTrack> track = mNextOutput->mRecycledTrack;
+ if (track != nullptr) {
+ ALOGD("Forward VolumeShaper operation to recycled track %p", track.get());
+ (void)track->applyVolumeShaper(configuration, operation);
+ } else {
+ // There is a small chance that the unduck occurs after the next
+ // player has already started, but before it is registered to receive
+ // the unduck command.
+ track = mNextOutput->mTrack;
+ if (track != nullptr) {
+ ALOGD("Forward VolumeShaper operation to track %p", track.get());
+ (void)track->applyVolumeShaper(configuration, operation);
+ }
+ }
+ }
status = mVolumeHandler->applyVolumeShaper(configuration, operation);
}
return status;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index e1d762f..89354d6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1864,7 +1864,7 @@
mStartTimeOffsetMs = mEncoderProfiles->getStartTimeOffsetMs(mCameraId);
} else if (mVideoSource == VIDEO_SOURCE_SURFACE) {
// surface source doesn't need large initial delay
- mStartTimeOffsetMs = 200;
+ mStartTimeOffsetMs = 100;
}
if (mStartTimeOffsetMs > 0) {
writer->setStartTimeOffsetMs(mStartTimeOffsetMs);
@@ -1982,10 +1982,12 @@
mCameraSourceTimeLapse = NULL;
}
- if (mVideoEncoderSource != NULL) {
- int64_t stopTimeUs = systemTime() / 1000;
- sp<MetaData> meta = new MetaData;
- err = mVideoEncoderSource->setStopStimeUs(stopTimeUs);
+ int64_t stopTimeUs = systemTime() / 1000;
+ for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {
+ if (source != nullptr && OK != source->setStopTimeUs(stopTimeUs)) {
+ ALOGW("Failed to set stopTime %lld us for %s",
+ (long long)stopTimeUs, source->isVideo() ? "Video" : "Audio");
+ }
}
if (mWriter != NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d83c406..aa21fff 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1242,6 +1242,16 @@
mAudioLastDequeueTimeUs = seekTimeUs;
}
+ if (mSubtitleTrack.mSource != NULL) {
+ mSubtitleTrack.mPackets->clear();
+ mFetchSubtitleDataGeneration++;
+ }
+
+ if (mTimedTextTrack.mSource != NULL) {
+ mTimedTextTrack.mPackets->clear();
+ mFetchTimedTextDataGeneration++;
+ }
+
// If currently buffering, post kWhatBufferingEnd first, so that
// NuPlayer resumes. Otherwise, if cache hits high watermark
// before new polling happens, no one will resume the playback.
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 9cccfc4..ebb90c8 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -14,10 +14,85 @@
* limitations under the License.
*/
+/*
+* Documentation: Workflow summary for histogram data processing:
+* For more details on FIFO, please see system/media/audio_utils; doxygen
+* TODO: add this documentation to doxygen once it is further developed
+* 1) writing the data to a buffer
+* onWork
+* Called every period length (e.g., 4ms)
+* Calls LOG_HIST_TS
+* LOG_HIST_TS
+* Hashes file name and line number
+* calls NBLOG::Writer::logHistTS once
+* NBLOG::Writer::logHistTS
+* calls NBLOG::Writer::log on hash and current timestamp
+* time is in CLOCK_MONOTONIC converted to ns
+* NBLOG::Writer::log(Event, const void*, size_t)
+* Initializes Entry, a struct containing one log entry
+* Entry contains the event type (mEvent), data length (mLength),
+* and data pointer (mData)
+* TODO: why mLength (max length of buffer data) must be <= kMaxLength = 255?
+* calls NBLOG::Writer::log(Entry *, bool)
+* NBLog::Writer::log(Entry *, bool)
+* Calls copyEntryDataAt to format data as follows in temp array:
+* [type][length][data ... ][length]
+* calls audio_utils_fifo_writer.write on temp
+* audio_utils_fifo_writer.write
+* calls obtain(), memcpy (reference in doxygen)
+* returns number of frames written
+* ssize_t audio_utils_fifo_reader::obtain
+* Determines readable buffer section via pointer arithmetic on reader
+* and writer pointers
+*
+* 2) reading the data from shared memory
+* Thread::threadloop()
+* TODO: add description?
+* NBLog::MergeThread::threadLoop()
+* calls NBLog::Merger::merge
+* NBLog::Merger::merge
+* for each reader in vector of class NamedReader,
+* callsNamedReader::reader()->getSnapshot
+* TODO: check whether the rest of this function is relevant
+* NBLog::Reader::getSnapshot
+* copies snapshot of reader's fifo buffer into its own buffer
+* calls mFifoReader->obtain to find readable data
+* sets snapshot.begin() and .end() iterators to boundaries of valid entries
+* moves the fifo reader index to after the last entry read
+* in this case, the buffer is in shared memory. in (3), the buffer is private
+*
+* 3) reading the data from private buffer
+* MediaLogService::dump
+* calls NBLog::Reader::dump(int) on instance of subclass mergeReader
+* NBLog::Reader::dump(int)
+* calls getSnapshot on the current reader
+* calls dump(int, size_t, Snapshot)
+* NBLog::Reader::dump(int, size, snapshot)
+* iterates through snapshot's events and switches based on their type
+* (string, timestamp, etc...)
+* In the case of EVENT_HISTOGRAM_ENTRY_TS, adds a list of timestamp sequences
+* (histogram entry) to NBLog::mHists
+* In the case of EVENT_HISTOGRAM_FLUSH, calls drawHistogram on each element in
+* the list and erases it
+* TODO: when do these events occur?
+* NBLog::drawHistogram
+* input: timestamp array
+* buckets this to a histogram and prints
+*
+*/
+
#define LOG_TAG "NBLog"
//#define LOG_NDEBUG 0
+#include <algorithm>
#include <climits>
+#include <deque>
+#include <fstream>
+// #include <inttypes.h>
+#include <iostream>
+#include <math.h>
+#include <numeric>
+#include <vector>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -27,16 +102,18 @@
#include <new>
#include <audio_utils/roundup.h>
#include <media/nbaio/NBLog.h>
+// #include <utils/CallStack.h> // used to print callstack
#include <utils/Log.h>
#include <utils/String8.h>
#include <queue>
+#include <utility>
namespace android {
-int NBLog::Entry::readAt(size_t offset) const
+int NBLog::Entry::copyEntryDataAt(size_t offset) const
{
- // FIXME This is too slow, despite the name it is used during writing
+ // FIXME This is too slow
if (offset == 0)
return mEvent;
else if (offset == 1)
@@ -51,12 +128,29 @@
// ---------------------------------------------------------------------------
-NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
- ALOGW_IF(entry[offsetof(struct entry, type)] != EVENT_START_FMT,
- "Created format entry with invalid event type %d", entry[offsetof(struct entry, type)]);
+/*static*/
+std::unique_ptr<NBLog::AbstractEntry> NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) {
+ const uint8_t type = EntryIterator(ptr)->type;
+ switch (type) {
+ case EVENT_START_FMT:
+ return std::make_unique<FormatEntry>(FormatEntry(ptr));
+ case EVENT_HISTOGRAM_FLUSH:
+ case EVENT_HISTOGRAM_ENTRY_TS:
+ return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
+ default:
+ ALOGW("Tried to create AbstractEntry of type %d", type);
+ return nullptr;
+ }
}
-NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
+NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) {
+}
+
+// ---------------------------------------------------------------------------
+
+NBLog::EntryIterator NBLog::FormatEntry::begin() const {
+ return EntryIterator(mEntry);
+}
const char *NBLog::FormatEntry::formatString() const {
return (const char*) mEntry + offsetof(entry, data);
@@ -66,12 +160,14 @@
return mEntry[offsetof(entry, length)];
}
-NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
+NBLog::EntryIterator NBLog::FormatEntry::args() const {
auto it = begin();
// skip start fmt
++it;
// skip timestamp
++it;
+ // skip hash
+ ++it;
// Skip author if present
if (it->type == EVENT_AUTHOR) {
++it;
@@ -79,19 +175,33 @@
return it;
}
-timespec NBLog::FormatEntry::timestamp() const {
+int64_t NBLog::FormatEntry::timestamp() const {
auto it = begin();
// skip start fmt
++it;
- return it.payload<timespec>();
+ return it.payload<int64_t>();
}
-pid_t NBLog::FormatEntry::author() const {
+NBLog::log_hash_t NBLog::FormatEntry::hash() const {
auto it = begin();
// skip start fmt
++it;
// skip timestamp
++it;
+ // unaligned 64-bit read not supported
+ log_hash_t hash;
+ memcpy(&hash, it->data, sizeof(hash));
+ return hash;
+}
+
+int NBLog::FormatEntry::author() const {
+ auto it = begin();
+ // skip start fmt
+ ++it;
+ // skip timestamp
+ ++it;
+ // skip hash
+ ++it;
// if there is an author entry, return it, return -1 otherwise
if (it->type == EVENT_AUTHOR) {
return it.payload<int>();
@@ -99,12 +209,13 @@
return -1;
}
-NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
+NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor(
std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
auto it = begin();
// copy fmt start entry
it.copyTo(dst);
// copy timestamp
+ (++it).copyTo(dst); // copy hash
(++it).copyTo(dst);
// insert author entry
size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
@@ -124,71 +235,103 @@
return it;
}
-void NBLog::FormatEntry::iterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
+void NBLog::EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
dst->write(ptr, length);
}
-void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
+void NBLog::EntryIterator::copyData(uint8_t *dst) const {
memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
}
-NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
- return iterator(mEntry);
-}
-
-NBLog::FormatEntry::iterator::iterator()
+NBLog::EntryIterator::EntryIterator()
: ptr(nullptr) {}
-NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
+NBLog::EntryIterator::EntryIterator(const uint8_t *entry)
: ptr(entry) {}
-NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
+NBLog::EntryIterator::EntryIterator(const NBLog::EntryIterator &other)
: ptr(other.ptr) {}
-const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
+const NBLog::entry& NBLog::EntryIterator::operator*() const {
return *(entry*) ptr;
}
-const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
+const NBLog::entry* NBLog::EntryIterator::operator->() const {
return (entry*) ptr;
}
-NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
+NBLog::EntryIterator& NBLog::EntryIterator::operator++() {
ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
return *this;
}
-NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
+NBLog::EntryIterator& NBLog::EntryIterator::operator--() {
ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
return *this;
}
-NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
- iterator aux(*this);
+NBLog::EntryIterator NBLog::EntryIterator::next() const {
+ EntryIterator aux(*this);
return ++aux;
}
-NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
- iterator aux(*this);
+NBLog::EntryIterator NBLog::EntryIterator::prev() const {
+ EntryIterator aux(*this);
return --aux;
}
-int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
+int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const {
return ptr - other.ptr;
}
-bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
+bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const {
return ptr != other.ptr;
}
-bool NBLog::FormatEntry::iterator::hasConsistentLength() const {
+bool NBLog::EntryIterator::hasConsistentLength() const {
return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] +
NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset];
}
// ---------------------------------------------------------------------------
+int64_t NBLog::HistogramEntry::timestamp() const {
+ return EntryIterator(mEntry).payload<HistTsEntry>().ts;
+}
+
+NBLog::log_hash_t NBLog::HistogramEntry::hash() const {
+ return EntryIterator(mEntry).payload<HistTsEntry>().hash;
+}
+
+int NBLog::HistogramEntry::author() const {
+ EntryIterator it(mEntry);
+ if (it->length == sizeof(HistTsEntryWithAuthor)) {
+ return it.payload<HistTsEntryWithAuthor>().author;
+ } else {
+ return -1;
+ }
+}
+
+NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor(
+ std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
+ // Current histogram entry has {type, length, struct HistTsEntry, length}.
+ // We now want {type, length, struct HistTsEntryWithAuthor, length}
+ uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)];
+ // Copy content until the point we want to add the author
+ memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry));
+ // Copy the author
+ *(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author;
+ // Update lengths
+ buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor);
+ buffer[sizeof(buffer) + Entry::kPreviousLengthOffset] = sizeof(HistTsEntryWithAuthor);
+ // Write new buffer into FIFO
+ dst->write(buffer, sizeof(buffer));
+ return EntryIterator(mEntry).next();
+}
+
+// ---------------------------------------------------------------------------
+
#if 0 // FIXME see note in NBLog.h
NBLog::Timeline::Timeline(size_t size, void *shared)
: mSize(roundup(size)), mOwn(shared == NULL),
@@ -301,13 +444,15 @@
if (!mEnabled) {
return;
}
- struct timespec ts;
- if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ int64_t ts = get_monotonic_ns();
+ if (ts > 0) {
log(EVENT_TIMESTAMP, &ts, sizeof(ts));
+ } else {
+ ALOGE("Failed to get timestamp");
}
}
-void NBLog::Writer::logTimestamp(const struct timespec &ts)
+void NBLog::Writer::logTimestamp(const int64_t ts)
{
if (!mEnabled) {
return;
@@ -360,19 +505,57 @@
log(&entry, true);
}
-void NBLog::Writer::logFormat(const char *fmt, ...)
+void NBLog::Writer::logHash(log_hash_t hash)
+{
+ if (!mEnabled) {
+ return;
+ }
+ log(EVENT_HASH, &hash, sizeof(hash));
+}
+
+void NBLog::Writer::logHistTS(log_hash_t hash)
+{
+ if (!mEnabled) {
+ return;
+ }
+ HistTsEntry data;
+ data.hash = hash;
+ data.ts = get_monotonic_ns();
+ if (data.ts > 0) {
+ log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data));
+ } else {
+ ALOGE("Failed to get timestamp");
+ }
+}
+
+void NBLog::Writer::logHistFlush(log_hash_t hash)
+{
+ if (!mEnabled) {
+ return;
+ }
+ HistTsEntry data;
+ data.hash = hash;
+ data.ts = get_monotonic_ns();
+ if (data.ts > 0) {
+ log(EVENT_HISTOGRAM_FLUSH, &data, sizeof(data));
+ } else {
+ ALOGE("Failed to get timestamp");
+ }
+}
+
+void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...)
{
if (!mEnabled) {
return;
}
va_list ap;
- va_start(ap, fmt);
- Writer::logVFormat(fmt, ap);
+ va_start(ap, hash);
+ Writer::logVFormat(fmt, hash, ap);
va_end(ap);
}
-void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
+void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp)
{
if (!mEnabled) {
return;
@@ -381,8 +564,9 @@
int i;
double f;
char* s;
- struct timespec t;
+ int64_t t;
Writer::logTimestamp();
+ Writer::logHash(hash);
for (const char *p = fmt; *p != '\0'; p++) {
// TODO: implement more complex formatting such as %.3f
if (*p != '%') {
@@ -395,7 +579,7 @@
break;
case 't': // timestamp
- t = va_arg(argp, struct timespec);
+ t = va_arg(argp, int64_t);
Writer::logTimestamp(t);
break;
@@ -440,40 +624,35 @@
// a confusion for a programmer debugging their code.
return;
}
- switch (event) {
- case EVENT_STRING:
- case EVENT_TIMESTAMP:
- case EVENT_INTEGER:
- case EVENT_FLOAT:
- case EVENT_PID:
- case EVENT_START_FMT:
- break;
- case EVENT_RESERVED:
- default:
+ // Ignore if invalid event
+ if (event == EVENT_RESERVED || event >= EVENT_UPPER_BOUND) {
return;
}
- Entry entry(event, data, length);
- log(&entry, true /*trusted*/);
+ Entry etr(event, data, length);
+ log(&etr, true /*trusted*/);
}
-void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
+void NBLog::Writer::log(const NBLog::Entry *etr, bool trusted)
{
if (!mEnabled) {
return;
}
if (!trusted) {
- log(entry->mEvent, entry->mData, entry->mLength);
+ log(etr->mEvent, etr->mData, etr->mLength);
return;
}
- size_t need = entry->mLength + Entry::kOverhead; // mEvent, mLength, data[length], mLength
- // need = number of bytes remaining to write
+ size_t need = etr->mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength
+ // need = number of bytes written to FIFO
// FIXME optimize this using memcpy for the data part of the Entry.
// The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
+ // checks size of a single log Entry: type, length, data pointer and ending
uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
+ // write this data to temp array
for (size_t i = 0; i < need; i++) {
- temp[i] = entry->readAt(i);
+ temp[i] = etr->copyEntryDataAt(i);
}
+ // write to circular buffer
mFifoWriter->write(temp, need);
}
@@ -531,7 +710,7 @@
Writer::logTimestamp();
}
-void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
+void NBLog::LockedWriter::logTimestamp(const int64_t ts)
{
Mutex::Autolock _l(mLock);
Writer::logTimestamp(ts);
@@ -568,6 +747,12 @@
Writer::logEnd();
}
+void NBLog::LockedWriter::logHash(log_hash_t hash)
+{
+ Mutex::Autolock _l(mLock);
+ Writer::logHash(hash);
+}
+
bool NBLog::LockedWriter::isEnabled() const
{
Mutex::Autolock _l(mLock);
@@ -582,13 +767,19 @@
// ---------------------------------------------------------------------------
+const std::set<NBLog::Event> NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT,
+ NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS};
+const std::set<NBLog::Event> NBLog::Reader::endingTypes {NBLog::Event::EVENT_END_FMT,
+ NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
+ NBLog::Event::EVENT_HISTOGRAM_FLUSH};
NBLog::Reader::Reader(const void *shared, size_t size)
: mShared((/*const*/ Shared *) shared), /*mIMemory*/
mFd(-1), mIndent(0),
mFifo(mShared != NULL ?
new audio_utils_fifo(size, sizeof(uint8_t),
mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
- mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL)
+ mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL),
+ findGlitch(false)
{
}
@@ -604,16 +795,50 @@
delete mFifo;
}
-uint8_t *NBLog::Reader::findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type) {
+inline static int deltaMs(int64_t ns1, int64_t ns2) {
+ return (ns2 - ns1) / (1000 * 1000);
+}
+
+// Produces a log warning if the timing of recent buffer periods caused a glitch
+// Computes sum of running window of three buffer periods
+// Checks whether the buffer periods leave enough CPU time for the next one
+// e.g. if a buffer period is expected to be 4 ms and a buffer requires 3 ms of CPU time,
+// here are some glitch cases:
+// 4 + 4 + 6 ; 5 + 4 + 5; 2 + 2 + 10
+// TODO: develop this code to track changes in histogram distribution in addition
+// to / instead of glitches
+void NBLog::Reader::alertIfGlitch(const std::vector<int64_t> &samples) {
+ //TODO: measure kPeriodLen and kRatio from the data as they may change.
+ static const int kPeriodLen = 4; // current period length is ideally 4 ms
+ static const double kRatio = 0.75; // estimate of CPU time as ratio of period length
+ // DAC processing time for 4 ms buffer
+ static const int kPeriodTime = static_cast<int>(round(kPeriodLen * kRatio));
+ static const int kNumBuff = 3; // number of buffers considered in local history
+ std::deque<int> periods(kNumBuff, kPeriodLen);
+ for (size_t i = 2; i < samples.size(); ++i) { // skip first time entry
+ periods.push_front(deltaMs(samples[i - 1], samples[i]));
+ periods.pop_back();
+ // TODO: check that all glitch cases are covered
+ if (std::accumulate(periods.begin(), periods.end(), 0) > kNumBuff * kPeriodLen +
+ kPeriodLen - kPeriodTime) {
+ ALOGW("A glitch occurred");
+ periods.assign(kNumBuff, kPeriodLen);
+ }
+ }
+ return;
+}
+
+const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
+ const std::set<Event> &types) {
while (back + Entry::kPreviousLengthOffset >= front) {
- uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
- if (prev < front || prev + prev[offsetof(FormatEntry::entry, length)] +
+ const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
+ if (prev < front || prev + prev[offsetof(entry, length)] +
Entry::kOverhead != back) {
// prev points to an out of limits or inconsistent entry
return nullptr;
}
- if (prev[offsetof(FormatEntry::entry, type)] == type) {
+ if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) {
return prev;
}
back = prev;
@@ -652,21 +877,21 @@
// it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
// TODO: handle client corruption (in the middle of a buffer)
- uint8_t *back = snapshot->mData + availToRead;
- uint8_t *front = snapshot->mData;
+ const uint8_t *back = snapshot->mData + availToRead;
+ const uint8_t *front = snapshot->mData;
// Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
// We go backwards until we find an EVENT_END_FMT.
- uint8_t *lastEnd = findLastEntryOfType(front, back, EVENT_END_FMT);
+ const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes);
if (lastEnd == nullptr) {
- snapshot->mEnd = snapshot->mBegin = FormatEntry::iterator(front);
+ snapshot->mEnd = snapshot->mBegin = EntryIterator(front);
} else {
// end of snapshot points to after last END_FMT entry
- snapshot->mEnd = FormatEntry::iterator(lastEnd + Entry::kOverhead);
+ snapshot->mEnd = EntryIterator(lastEnd).next();
// find first START_FMT
- uint8_t *firstStart = nullptr;
- uint8_t *firstStartTmp = lastEnd;
- while ((firstStartTmp = findLastEntryOfType(front, firstStartTmp, EVENT_START_FMT))
+ const uint8_t *firstStart = nullptr;
+ const uint8_t *firstStartTmp = snapshot->mEnd;
+ while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes))
!= nullptr) {
firstStart = firstStartTmp;
}
@@ -674,7 +899,7 @@
if (firstStart == nullptr) {
snapshot->mBegin = snapshot->mEnd;
} else {
- snapshot->mBegin = FormatEntry::iterator(firstStart);
+ snapshot->mBegin = EntryIterator(firstStart);
}
}
@@ -686,8 +911,37 @@
}
+// writes sample deltas to file, either truncating or appending
+inline void writeHistToFile(const std::vector<int64_t> &samples, bool append) {
+ // name of file on audioserver
+ static const char* const kName = (char *)"/data/misc/audioserver/sample_results.txt";
+ // stores deltas between the samples
+ std::vector<int64_t> intervals;
+ for (size_t i = 1; i < samples.size(); ++i) {
+ intervals.push_back(deltaMs(samples[i - 1], samples[i]));
+ }
+ if (intervals.empty()) return;
+ // Deletes maximum value in a histogram. Temp quick fix.
+ // FIXME: need to find root cause of approx. 35th element from the end
+ // consistently being an outlier in the first histogram of a flush
+ // ALOGW("%" PRId64 "before", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
+ intervals.erase(std::max_element(intervals.begin(), intervals.end()));
+ // ALOGW("%" PRId64 "after", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
+ std::ofstream ofs;
+ ofs.open(kName, append ? std::ios::app : std::ios::trunc);
+ if (!ofs) {
+ ALOGW("couldn't open file %s", kName);
+ return;
+ }
+ for (size_t i = 0; i < intervals.size(); ++i) {
+ ofs << intervals[i] << "\n";
+ }
+ ofs.close();
+}
+
void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
{
+ // CallStack cs(LOG_TAG);
#if 0
struct timespec ts;
time_t maxSec = -1;
@@ -712,11 +966,11 @@
mFd = fd;
mIndent = indent;
String8 timestamp, body;
- size_t lost = snapshot.lost() + (snapshot.begin() - FormatEntry::iterator(snapshot.data()));
+ size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
if (lost > 0) {
body.appendFormat("warning: lost %zu bytes worth of events", lost);
// TODO timestamp empty here, only other choice to wait for the first timestamp event in the
- // log to push it out. Consider keeping the timestamp/body between calls to readAt().
+ // log to push it out. Consider keeping the timestamp/body between calls to copyEntryDataAt().
dumpLine(timestamp, body);
}
#if 0
@@ -730,6 +984,7 @@
}
bool deferredTimestamp = false;
#endif
+
for (auto entry = snapshot.begin(); entry != snapshot.end();) {
switch (entry->type) {
#if 0
@@ -798,9 +1053,53 @@
break;
#endif
case EVENT_START_FMT:
- // right now, this is the only supported case
entry = handleFormat(FormatEntry(entry), ×tamp, &body);
break;
+ case EVENT_HISTOGRAM_ENTRY_TS: {
+ HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data);
+ // TODO This memcpies are here to avoid unaligned memory access crash.
+ // There's probably a more efficient way to do it
+ log_hash_t hash;
+ memcpy(&hash, &(data->hash), sizeof(hash));
+ int64_t ts;
+ memcpy(&ts, &data->ts, sizeof(ts));
+ const std::pair<log_hash_t, int> key(hash, data->author);
+ // TODO might want to filter excessively high outliers, which are usually caused
+ // by the thread being inactive.
+ mHists[key].push_back(ts);
+ ++entry;
+ break;
+ }
+ // draws histograms stored in global Reader::mHists and erases them
+ case EVENT_HISTOGRAM_FLUSH: {
+ HistogramEntry histEntry(entry);
+ // Log timestamp
+ // Timestamp of call to drawHistogram, not when audio was generated
+ const int64_t ts = histEntry.timestamp();
+ timestamp.clear();
+ timestamp.appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+ (int) ((ts / (1000 * 1000)) % 1000));
+ // Log histograms
+ setFindGlitch(true);
+ body.appendFormat("Histogram flush - ");
+ handleAuthor(histEntry, &body);
+ for (auto hist = mHists.begin(); hist != mHists.end();) {
+ if (hist->first.second == histEntry.author()) {
+ body.appendFormat("%X", (int)hist->first.first);
+ if (findGlitch) {
+ alertIfGlitch(hist->second);
+ }
+ // set file to empty and write data for all histograms in this set
+ writeHistToFile(hist->second, hist != mHists.begin());
+ drawHistogram(&body, hist->second, true, indent);
+ hist = mHists.erase(hist);
+ } else {
+ ++hist;
+ }
+ }
+ ++entry;
+ break;
+ }
case EVENT_END_FMT:
body.appendFormat("warning: got to end format event");
++entry;
@@ -814,12 +1113,8 @@
if (!body.isEmpty()) {
dumpLine(timestamp, body);
- // deferredTimestamp = false;
}
}
- // if (deferredTimestamp) {
- // dumpLine(timestamp, body);
- // }
}
void NBLog::Reader::dump(int fd, size_t indent)
@@ -844,11 +1139,23 @@
return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
}
+void NBLog::Reader::setFindGlitch(bool s)
+{
+ findGlitch = s;
+}
+
+bool NBLog::Reader::isFindGlitch() const
+{
+ return findGlitch;
+}
+
+// ---------------------------------------------------------------------------
+
void NBLog::appendTimestamp(String8 *body, const void *data) {
- struct timespec ts;
- memcpy(&ts, data, sizeof(struct timespec));
- body->appendFormat("[%d.%03d]", (int) ts.tv_sec,
- (int) (ts.tv_nsec / 1000000));
+ int64_t ts;
+ memcpy(&ts, data, sizeof(ts));
+ body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+ (int) ((ts / (1000 * 1000)) % 1000));
}
void NBLog::appendInt(String8 *body, const void *data) {
@@ -868,20 +1175,42 @@
body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
}
-NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
+String8 NBLog::bufferDump(const uint8_t *buffer, size_t size)
+{
+ String8 str;
+ str.append("[ ");
+ for(size_t i = 0; i < size; i++)
+ {
+ str.appendFormat("%d ", buffer[i]);
+ }
+ str.append("]");
+ return str;
+}
+
+String8 NBLog::bufferDump(const EntryIterator &it)
+{
+ return bufferDump(it, it->length + Entry::kOverhead);
+}
+
+NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
String8 *timestamp,
String8 *body) {
// log timestamp
- struct timespec ts = fmtEntry.timestamp();
+ int64_t ts = fmtEntry.timestamp();
timestamp->clear();
- timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
- (int) (ts.tv_nsec / 1000000));
+ timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
+ (int) ((ts / (1000 * 1000)) % 1000));
+
+ // log unique hash
+ log_hash_t hash = fmtEntry.hash();
+ // print only lower 16bit of hash as hex and line as int to reduce spam in the log
+ body->appendFormat("%.4X-%d ", (int)(hash >> 16) & 0xFFFF, (int) hash & 0xFFFF);
// log author (if present)
handleAuthor(fmtEntry, body);
// log string
- NBLog::FormatEntry::iterator arg = fmtEntry.args();
+ NBLog::EntryIterator arg = fmtEntry.args();
const char* fmt = fmtEntry.formatString();
size_t fmt_length = fmtEntry.formatStringLength();
@@ -954,7 +1283,125 @@
return arg;
}
-// ---------------------------------------------------------------------------
+static int widthOf(int x) {
+ int width = 0;
+ while (x > 0) {
+ ++width;
+ x /= 10;
+ }
+ return width;
+}
+
+static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
+ // TODO allow buckets of variable resolution
+ std::map<int, int> buckets;
+ for (size_t i = 1; i < samples.size(); ++i) {
+ ++buckets[deltaMs(samples[i - 1], samples[i])];
+ }
+ return buckets;
+}
+
+static inline uint32_t log2(uint32_t x) {
+ // This works for x > 0
+ return 31 - __builtin_clz(x);
+}
+
+// TODO put this function in separate file. Make it return a std::string instead of modifying body
+/*
+Example output:
+[54.234] Histogram flush - AudioOut_D:
+Histogram 33640BF1
+ [ 1][ 1][ 1][ 3][54][69][ 1][ 2][ 1]
+ 64| []
+ 32| [] []
+ 16| [] []
+ 8| [] []
+ 4| [] []
+ 2|______________[]__[]__[]______[]____
+ 4 5 6 8 9 10 11 13 15
+Notice that all values that fall in the same row have the same height (65 and 127 are displayed
+identically). That's why exact counts are added at the top.
+*/
+void NBLog::Reader::drawHistogram(String8 *body,
+ const std::vector<int64_t> &samples,
+ bool logScale,
+ int indent,
+ int maxHeight) {
+ // this avoids some corner cases
+ if (samples.size() <= 1) {
+ return;
+ }
+ // temp code for debugging the outlier timestamp
+ const int kMaxMs = 100;
+ for (size_t i = 1; i < samples.size()-1; ++i) {
+ const int currDelta = deltaMs(samples[i - 1], samples[i]);
+ if (currDelta > kMaxMs) {
+ body->appendFormat("\nlocation: %zu, size: %zu, pos from end: %zu, %d\t", i,
+ samples.size(), samples.size() - i, currDelta);
+ }
+ }
+ // FIXME: as can be seen when printing the values, the outlier timestamps typically occur
+ // in the first histogram 35 to 38 indices from the end (most often 35).
+ // TODO: build histogram buckets earlier and discard timestamps to save memory
+ std::map<int, int> buckets = buildBuckets(samples);
+ // TODO consider changing all ints to uint32_t or uint64_t
+
+ // underscores and spaces length corresponds to maximum width of histogram
+ static const int kLen = 40;
+ std::string underscores(kLen, '-');
+ std::string spaces(kLen, ' ');
+
+ auto it = buckets.begin();
+ int maxDelta = it->first;
+ int maxCount = it->second;
+ // Compute maximum values
+ while (++it != buckets.end()) {
+ if (it->first > maxDelta) {
+ maxDelta = it->first;
+ }
+ if (it->second > maxCount) {
+ maxCount = it->second;
+ }
+ }
+ int height = logScale ? log2(maxCount) + 1 : maxCount; // maxCount > 0, safe to call log2
+ const int leftPadding = widthOf(logScale ? pow(2, height) : maxCount);
+ const int colWidth = std::max(std::max(widthOf(maxDelta) + 1, 3), leftPadding + 2);
+ int scalingFactor = 1;
+ // scale data if it exceeds maximum height
+ if (height > maxHeight) {
+ scalingFactor = (height + maxHeight) / maxHeight;
+ height /= scalingFactor;
+ }
+ body->appendFormat("\n%*s", leftPadding + 11, "Occurrences");
+ // write histogram label line with bucket values
+ body->appendFormat("\n%*s", indent, " ");
+ body->appendFormat("%*s", leftPadding, " ");
+ for (auto const &x : buckets) {
+ body->appendFormat("%*d", colWidth, x.second);
+ }
+ // write histogram ascii art
+ body->appendFormat("\n%*s", indent, " ");
+ for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) {
+ const int value = logScale ? (1 << row) : row;
+ body->appendFormat("%.*s", leftPadding, spaces.c_str());
+ for (auto const &x : buckets) {
+ body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|");
+ }
+ body->appendFormat("\n%*s", indent, " ");
+ }
+ // print x-axis
+ const int columns = static_cast<int>(buckets.size());
+ body->appendFormat("%*c", leftPadding, ' ');
+ body->appendFormat("%.*s", (columns + 1) * colWidth, underscores.c_str());
+ body->appendFormat("\n%*s", indent, " ");
+
+ // write footer with bucket labels
+ body->appendFormat("%*s", leftPadding, " ");
+ for (auto const &x : buckets) {
+ body->appendFormat("%*d", colWidth, x.first);
+ }
+ body->appendFormat("%.*s%s", colWidth, spaces.c_str(), "ms\n");
+}
NBLog::Merger::Merger(const void *shared, size_t size):
mShared((Shared *) shared),
@@ -965,6 +1412,8 @@
{}
void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
+ // FIXME This is called by binder thread in MediaLogService::registerWriter
+ // but the access to shared variable mNamedReaders is not yet protected by a lock.
mNamedReaders.push_back(reader);
}
@@ -972,26 +1421,27 @@
// composed by a timestamp and the index of the snapshot where the timestamp came from
struct MergeItem
{
- struct timespec ts;
+ int64_t ts;
int index;
- MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
+ MergeItem(int64_t ts, int index): ts(ts), index(index) {}
};
// operators needed for priority queue in merge
-bool operator>(const struct timespec &t1, const struct timespec &t2) {
- return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
-}
+// bool operator>(const int64_t &t1, const int64_t &t2) {
+// return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
+// }
bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) {
- return i1.ts > i2.ts ||
- (i1.ts.tv_sec == i2.ts.tv_sec && i1.ts.tv_nsec == i2.ts.tv_nsec && i1.index > i2.index);
+ return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index);
}
// Merge registered readers, sorted by timestamp
void NBLog::Merger::merge() {
+ // FIXME This is called by merge thread
+ // but the access to shared variable mNamedReaders is not yet protected by a lock.
int nLogs = mNamedReaders.size();
std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
- std::vector<NBLog::FormatEntry::iterator> offsets(nLogs);
+ std::vector<NBLog::EntryIterator> offsets(nLogs);
for (int i = 0; i < nLogs; ++i) {
snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
offsets[i] = snapshots[i]->begin();
@@ -1003,7 +1453,7 @@
for (int i = 0; i < nLogs; ++i)
{
if (offsets[i] != snapshots[i]->end()) {
- timespec ts = FormatEntry(offsets[i]).timestamp();
+ int64_t ts = AbstractEntry::buildEntry(offsets[i])->timestamp();
timestamps.emplace(ts, i);
}
}
@@ -1012,30 +1462,36 @@
// find minimum timestamp
int index = timestamps.top().index;
// copy it to the log, increasing offset
- offsets[index] = FormatEntry(offsets[index]).copyWithAuthor(mFifoWriter, index);
+ offsets[index] = AbstractEntry::buildEntry(offsets[index])->copyWithAuthor(mFifoWriter,
+ index);
// update data structures
timestamps.pop();
if (offsets[index] != snapshots[index]->end()) {
- timespec ts = FormatEntry(offsets[index]).timestamp();
+ int64_t ts = AbstractEntry::buildEntry(offsets[index])->timestamp();
timestamps.emplace(ts, index);
}
}
}
-const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
- return &mNamedReaders;
+const std::vector<NBLog::NamedReader>& NBLog::Merger::getNamedReaders() const {
+ // FIXME This is returning a reference to a shared variable that needs a lock
+ return mNamedReaders;
}
+// ---------------------------------------------------------------------------
+
NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
: Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
-size_t NBLog::MergeReader::handleAuthor(const NBLog::FormatEntry &fmtEntry, String8 *body) {
- int author = fmtEntry.author();
- const char* name = (*mNamedReaders)[author].name();
+void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) {
+ int author = entry.author();
+ // FIXME Needs a lock
+ const char* name = mNamedReaders[author].name();
body->appendFormat("%s: ", name);
- return NBLog::Entry::kOverhead + sizeof(author);
}
+// ---------------------------------------------------------------------------
+
NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
: mMerger(merger),
mTimeoutUs(0) {}
diff --git a/media/libnbaio/include/NBLog.h b/media/libnbaio/include/NBLog.h
index bcebe9e..785b9c2 100644
--- a/media/libnbaio/include/NBLog.h
+++ b/media/libnbaio/include/NBLog.h
@@ -24,6 +24,8 @@
#include <utils/Mutex.h>
#include <utils/threads.h>
+#include <map>
+#include <set>
#include <vector>
namespace android {
@@ -34,12 +36,16 @@
public:
+typedef uint64_t log_hash_t;
+
+// FIXME Everything needed for client (writer API and registration) should be isolated
+// from the rest of the implementation.
class Writer;
class Reader;
private:
-enum Event {
+enum Event : uint8_t {
EVENT_RESERVED,
EVENT_STRING, // ASCII string, not NUL-terminated
// TODO: make timestamp optional
@@ -50,7 +56,13 @@
EVENT_AUTHOR, // author index (present in merged logs) tracks entry's original log
EVENT_START_FMT, // logFormat start event: entry includes format string, following
// entries contain format arguments
+ EVENT_HASH, // unique HASH of log origin, originates from hash of file name
+ // and line number
+ EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
+ EVENT_HISTOGRAM_FLUSH, // show histogram on log
EVENT_END_FMT, // end of logFormat argument list
+
+ EVENT_UPPER_BOUND, // to check for invalid events
};
@@ -60,94 +72,146 @@
// a formatted entry has the following structure:
// * START_FMT entry, containing the format string
// * TIMESTAMP entry
+// * HASH entry
// * author entry of the thread that generated it (optional, present in merged log)
// * format arg1
// * format arg2
// * ...
// * END_FMT entry
-class FormatEntry {
+// entry representation in memory
+struct entry {
+ const uint8_t type;
+ const uint8_t length;
+ const uint8_t data[0];
+};
+
+// entry tail representation (after data)
+struct ending {
+ uint8_t length;
+ uint8_t next[0];
+};
+
+// entry iterator
+class EntryIterator {
public:
- // build a Format Entry starting in the given pointer
- class iterator;
- explicit FormatEntry(const uint8_t *entry);
- explicit FormatEntry(const iterator &it);
- virtual ~FormatEntry() {}
+ EntryIterator();
+ explicit EntryIterator(const uint8_t *entry);
+ EntryIterator(const EntryIterator &other);
- // entry representation in memory
- struct entry {
- const uint8_t type;
- const uint8_t length;
- const uint8_t data[0];
- };
+ // dereference underlying entry
+ const entry& operator*() const;
+ const entry* operator->() const;
+ // advance to next entry
+ EntryIterator& operator++(); // ++i
+ // back to previous entry
+ EntryIterator& operator--(); // --i
+ EntryIterator next() const;
+ EntryIterator prev() const;
+ bool operator!=(const EntryIterator &other) const;
+ int operator-(const EntryIterator &other) const;
- // entry tail representation (after data)
- struct ending {
- uint8_t length;
- uint8_t next[0];
- };
+ bool hasConsistentLength() const;
+ void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
+ void copyData(uint8_t *dst) const;
- // entry iterator
- class iterator {
- public:
- iterator();
- iterator(const uint8_t *entry);
- iterator(const iterator &other);
+ template<typename T>
+ inline const T& payload() {
+ return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
+ }
- // dereference underlying entry
- const entry& operator*() const;
- const entry* operator->() const;
- // advance to next entry
- iterator& operator++(); // ++i
- // back to previous entry
- iterator& operator--(); // --i
- iterator next() const;
- iterator prev() const;
- bool operator!=(const iterator &other) const;
- int operator-(const iterator &other) const;
+ inline operator const uint8_t*() const {
+ return ptr;
+ }
- bool hasConsistentLength() const;
- void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
- void copyData(uint8_t *dst) const;
+private:
+ const uint8_t *ptr;
+};
- template<typename T>
- inline const T& payload() {
- return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
- }
+class AbstractEntry {
+public:
- private:
- friend class FormatEntry;
- const uint8_t *ptr;
- };
+ // Entry starting in the given pointer
+ explicit AbstractEntry(const uint8_t *entry);
+ virtual ~AbstractEntry() {}
- // Entry's format string
- const char* formatString() const;
-
- // Enrty's format string length
- size_t formatStringLength() const;
-
- // Format arguments (excluding format string, timestamp and author)
- iterator args() const;
+ // build concrete entry of appropriate class from pointer
+ static std::unique_ptr<AbstractEntry> buildEntry(const uint8_t *ptr);
// get format entry timestamp
- timespec timestamp() const;
+ // TODO consider changing to uint64_t
+ virtual int64_t timestamp() const = 0;
+
+ // get format entry's unique id
+ virtual log_hash_t hash() const = 0;
// entry's author index (-1 if none present)
// a Merger has a vector of Readers, author simply points to the index of the
// Reader that originated the entry
- int author() const;
+ // TODO consider changing to uint32_t
+ virtual int author() const = 0;
- // copy entry, adding author before timestamp, returns size of original entry
- iterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const;
+ // copy entry, adding author before timestamp, returns iterator to end of entry
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const = 0;
- iterator begin() const;
-
-private:
+protected:
// copies ordinary entry from src to dst, and returns length of entry
// size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it);
const uint8_t *mEntry;
};
+class FormatEntry : public AbstractEntry {
+public:
+ // explicit FormatEntry(const EntryIterator &it);
+ explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
+ virtual ~FormatEntry() {}
+
+ EntryIterator begin() const;
+
+ // Entry's format string
+ const char* formatString() const;
+
+ // Enrty's format string length
+ size_t formatStringLength() const;
+
+ // Format arguments (excluding format string, timestamp and author)
+ EntryIterator args() const;
+
+ // get format entry timestamp
+ virtual int64_t timestamp() const override;
+
+ // get format entry's unique id
+ virtual log_hash_t hash() const override;
+
+ // entry's author index (-1 if none present)
+ // a Merger has a vector of Readers, author simply points to the index of the
+ // Reader that originated the entry
+ virtual int author() const override;
+
+ // copy entry, adding author before timestamp, returns size of original entry
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const override;
+
+};
+
+class HistogramEntry : public AbstractEntry {
+public:
+ explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
+ }
+ virtual ~HistogramEntry() {}
+
+ virtual int64_t timestamp() const override;
+
+ virtual log_hash_t hash() const override;
+
+ virtual int author() const override;
+
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const override;
+
+};
+
// ---------------------------------------------------------------------------
// representation of a single log entry in private memory
@@ -156,7 +220,8 @@
: mEvent(event), mLength(length), mData(data) { }
/*virtual*/ ~Entry() { }
- int readAt(size_t offset) const;
+ // used during writing to format Entry information as follows: [type][length][data ... ][length]
+ int copyEntryDataAt(size_t offset) const;
private:
friend class Writer;
@@ -166,12 +231,28 @@
static const size_t kMaxLength = 255;
public:
// mEvent, mLength, mData[...], duplicate mLength
- static const size_t kOverhead = sizeof(FormatEntry::entry) + sizeof(FormatEntry::ending);
+ static const size_t kOverhead = sizeof(entry) + sizeof(ending);
// endind length of previous entry
- static const size_t kPreviousLengthOffset = - sizeof(FormatEntry::ending) +
- offsetof(FormatEntry::ending, length);
+ static const size_t kPreviousLengthOffset = - sizeof(ending) +
+ offsetof(ending, length);
};
+struct HistTsEntry {
+ log_hash_t hash;
+ int64_t ts;
+}; //TODO __attribute__((packed));
+
+struct HistTsEntryWithAuthor {
+ log_hash_t hash;
+ int64_t ts;
+ int author;
+}; //TODO __attribute__((packed));
+
+struct HistIntEntry {
+ log_hash_t hash;
+ int value;
+}; //TODO __attribute__((packed));
+
// representation of a single log entry in shared memory
// byte[0] mEvent
// byte[1] mLength
@@ -188,7 +269,8 @@
static void appendPID(String8 *body, const void *data, size_t length);
static void appendTimestamp(String8 *body, const void *data);
static size_t fmtEntryLength(const uint8_t *data);
-
+ static String8 bufferDump(const uint8_t *buffer, size_t size);
+ static String8 bufferDump(const EntryIterator &it);
public:
// Located in shared memory, must be POD.
@@ -245,19 +327,22 @@
virtual ~Writer();
+ // FIXME needs comments, and some should be private
virtual void log(const char *string);
virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
virtual void logvf(const char *fmt, va_list ap);
virtual void logTimestamp();
- virtual void logTimestamp(const struct timespec &ts);
+ virtual void logTimestamp(const int64_t ts);
virtual void logInteger(const int x);
virtual void logFloat(const float x);
virtual void logPID();
- virtual void logFormat(const char *fmt, ...);
- virtual void logVFormat(const char *fmt, va_list ap);
+ virtual void logFormat(const char *fmt, log_hash_t hash, ...);
+ virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
virtual void logStart(const char *fmt);
virtual void logEnd();
-
+ virtual void logHash(log_hash_t hash);
+ virtual void logHistTS(log_hash_t hash);
+ virtual void logHistFlush(log_hash_t hash);
virtual bool isEnabled() const;
@@ -270,7 +355,9 @@
private:
// 0 <= length <= kMaxLength
+ // writes a single Entry to the FIFO
void log(Event event, const void *data, size_t length);
+ // checks validity of an event before calling log above this one
void log(const Entry *entry, bool trusted = false);
Shared* const mShared; // raw pointer to shared memory
@@ -299,12 +386,13 @@
virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
virtual void logvf(const char *fmt, va_list ap);
virtual void logTimestamp();
- virtual void logTimestamp(const struct timespec &ts);
+ virtual void logTimestamp(const int64_t ts);
virtual void logInteger(const int x);
virtual void logFloat(const float x);
virtual void logPID();
virtual void logStart(const char *fmt);
virtual void logEnd();
+ virtual void logHash(log_hash_t hash);
virtual bool isEnabled() const;
virtual bool setEnabled(bool enabled);
@@ -335,18 +423,17 @@
// iterator to beginning of readable segment of snapshot
// data between begin and end has valid entries
- FormatEntry::iterator begin() { return mBegin; }
+ EntryIterator begin() { return mBegin; }
// iterator to end of readable segment of snapshot
- FormatEntry::iterator end() { return mEnd; }
-
+ EntryIterator end() { return mEnd; }
private:
friend class Reader;
uint8_t *mData;
size_t mLost;
- FormatEntry::iterator mBegin;
- FormatEntry::iterator mEnd;
+ EntryIterator mBegin;
+ EntryIterator mEnd;
};
// Input parameter 'size' is the desired size of the timeline in byte units.
@@ -356,15 +443,22 @@
virtual ~Reader();
+ void alertIfGlitch(const std::vector<int64_t> &samples);
+
// get snapshot of readers fifo buffer, effectively consuming the buffer
std::unique_ptr<Snapshot> getSnapshot();
// dump a particular snapshot of the reader
void dump(int fd, size_t indent, Snapshot & snap);
- // dump the current content of the reader's buffer
+ // dump the current content of the reader's buffer (call getSnapshot() and previous dump())
void dump(int fd, size_t indent = 0);
bool isIMemory(const sp<IMemory>& iMemory) const;
+ // if findGlitch is true, log warning when buffer periods caused glitch
+ void setFindGlitch(bool s);
+ bool isFindGlitch() const;
private:
+ static const std::set<Event> startingTypes;
+ static const std::set<Event> endingTypes;
/*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not
// declared as const because audio_utils_fifo() constructor
sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
@@ -375,19 +469,33 @@
audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
// non-NULL unless constructor fails
+ // each pair contains a sequence of timestamps (one histogram's worth)
+ // pair's log_hash_t is the hash of the source code location where the timestamp was taken
+ // pair's int points to the Reader that originated the entry
+ std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;
+ // TODO: it might be clearer, instead of a direct map from source location to vector of
+ // timestamps, if we instead first mapped from source location to an object that
+ // represented that location. And one_of its fields would be a vector of timestamps.
+ // That would allow us to record other information about the source location beyond timestamps.
void dumpLine(const String8& timestamp, String8& body);
- FormatEntry::iterator handleFormat(const FormatEntry &fmtEntry,
+ EntryIterator handleFormat(const FormatEntry &fmtEntry,
String8 *timestamp,
String8 *body);
// dummy method for handling absent author entry
- virtual size_t handleAuthor(const FormatEntry& /*fmtEntry*/, String8* /*body*/) { return 0; }
+ virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
+
+ static void drawHistogram(String8 *body, const std::vector<int64_t> &samples,
+ bool logScale, int indent = 0, int maxHeight = 10);
// Searches for the last entry of type <type> in the range [front, back)
// back has to be entry-aligned. Returns nullptr if none enconuntered.
- static uint8_t *findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type);
+ static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
+ const std::set<Event> &types);
static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
+
+ bool findGlitch; // alert if a local buffer period sequence caused an audio glitch
};
// Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name
@@ -418,26 +526,30 @@
void addReader(const NamedReader &reader);
// TODO add removeReader
void merge();
- const std::vector<NamedReader> *getNamedReaders() const;
+ // FIXME This is returning a reference to a shared variable that needs a lock
+ const std::vector<NamedReader>& getNamedReaders() const;
private:
// vector of the readers the merger is supposed to merge from.
// every reader reads from a writer's buffer
+ // FIXME Needs to be protected by a lock
std::vector<NamedReader> mNamedReaders;
+
+ // TODO Need comments on all of these
Shared * const mShared;
std::unique_ptr<audio_utils_fifo> mFifo;
std::unique_ptr<audio_utils_fifo_writer> mFifoWriter;
-
- static struct timespec getTimestamp(const uint8_t *data);
};
class MergeReader : public Reader {
public:
MergeReader(const void *shared, size_t size, Merger &merger);
private:
- const std::vector<NamedReader> *mNamedReaders;
+ // FIXME Needs to be protected by a lock,
+ // because even though our use of it is read-only there may be asynchronous updates
+ const std::vector<NamedReader>& mNamedReaders;
// handle author entry by looking up the author's name and appending it to the body
// returns number of bytes read from fmtEntry
- size_t handleAuthor(const FormatEntry &fmtEntry, String8 *body);
+ void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
};
// MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
@@ -479,6 +591,15 @@
}; // class NBLog
+// TODO put somewhere else
+static inline int64_t get_monotonic_ns() {
+ timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ }
+ return 0; // should not happen.
+}
+
} // namespace android
#endif // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index f6107fd..6491ceb 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -960,7 +960,9 @@
return NO_MEMORY;
}
hidlMem = mapMemory(hidlMemToken);
-
+ if (hidlMem == nullptr) {
+ return NO_MEMORY;
+ }
err = mOMXNode->useBuffer(
portIndex, hidlMemToken, &info.mBufferID);
} else {
@@ -1008,6 +1010,9 @@
return NO_MEMORY;
}
hidlMem = mapMemory(hidlMemToken);
+ if (hidlMem == nullptr) {
+ return NO_MEMORY;
+ }
info.mData = new SharedMemoryBuffer(format, hidlMem);
info.mMemRef = hidlMem;
} else {
@@ -7243,6 +7248,16 @@
ALOGE("Failed to set parameter 'stop-time-us' (err %d)", err);
return err;
}
+
+ int64_t stopTimeOffsetUs;
+ err = statusFromBinderStatus(
+ mGraphicBufferSource->getStopTimeOffsetUs(&stopTimeOffsetUs));
+
+ if (err != OK) {
+ ALOGE("Failed to get stop time offset (err %d)", err);
+ return err;
+ }
+ mInputFormat->setInt64("android._stop-time-offset-us", stopTimeOffsetUs);
}
int32_t dummy;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 58611b5..81b4964 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -95,6 +95,7 @@
"libmediadrm",
"libmedia_helper",
+ "libstagefright_flacdec",
"libstagefright_foundation",
"libdl",
"libRScpp",
@@ -155,6 +156,7 @@
"codecs/*",
"colorconversion",
"filters",
+ "flac/dec",
"foundation",
"http",
"httplive",
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 4ccd2d0..f2b1f10 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -58,13 +58,16 @@
mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate),
mTrackMaxAmplitude(false),
mStartTimeUs(0),
+ mStopSystemTimeUs(-1),
+ mLastFrameTimestampUs(0),
mMaxAmplitude(0),
mPrevSampleTimeUs(0),
mInitialReadTimeUs(0),
mNumFramesReceived(0),
mNumFramesSkipped(0),
mNumFramesLost(0),
- mNumClientOwnedBuffers(0) {
+ mNumClientOwnedBuffers(0),
+ mNoMoreFramesToRead(false) {
ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
sampleRate, outSampleRate, channelCount);
CHECK(channelCount == 1 || channelCount == 2);
@@ -175,6 +178,8 @@
}
mStarted = false;
+ mStopSystemTimeUs = -1;
+ mNoMoreFramesToRead = false;
mFrameAvailableCondition.signal();
mRecord->stop();
@@ -243,6 +248,9 @@
while (mStarted && mBuffersReceived.empty()) {
mFrameAvailableCondition.wait(mLock);
+ if (mNoMoreFramesToRead) {
+ return OK;
+ }
}
if (!mStarted) {
return OK;
@@ -286,6 +294,21 @@
return OK;
}
+status_t AudioSource::setStopTimeUs(int64_t stopTimeUs) {
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("Set stoptime: %lld us", (long long)stopTimeUs);
+
+ if (stopTimeUs < -1) {
+ ALOGE("Invalid stop time %lld us", (long long)stopTimeUs);
+ return BAD_VALUE;
+ } else if (stopTimeUs == -1) {
+ ALOGI("reset stopTime to be -1");
+ }
+
+ mStopSystemTimeUs = stopTimeUs;
+ return OK;
+}
+
void AudioSource::signalBufferReturned(MediaBuffer *buffer) {
ALOGV("signalBufferReturned: %p", buffer->data());
Mutex::Autolock autoLock(mLock);
@@ -338,6 +361,14 @@
return OK;
}
+ if (mStopSystemTimeUs != -1 && timeUs >= mStopSystemTimeUs) {
+ ALOGV("Drop Audio frame at %lld stop time: %lld us",
+ (long long)timeUs, (long long)mStopSystemTimeUs);
+ mNoMoreFramesToRead = true;
+ mFrameAvailableCondition.signal();
+ return OK;
+ }
+
if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) {
mInitialReadTimeUs = timeUs;
// Initial delay
@@ -346,6 +377,7 @@
}
mPrevSampleTimeUs = mStartTimeUs;
}
+ mLastFrameTimestampUs = timeUs;
size_t numLostBytes = 0;
if (mNumFramesReceived > 0) { // Ignore earlier frame lost
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index a569f5d..6ed0d0e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -220,6 +220,7 @@
mNumFramesEncoded(0),
mTimeBetweenFrameCaptureUs(0),
mFirstFrameTimeUs(0),
+ mStopSystemTimeUs(-1),
mNumFramesDropped(0),
mNumGlitches(0),
mGlitchDurationThresholdUs(200000),
@@ -879,6 +880,7 @@
{
Mutex::Autolock autoLock(mLock);
mStarted = false;
+ mStopSystemTimeUs = -1;
mFrameAvailableCondition.signal();
int64_t token;
@@ -970,6 +972,14 @@
}
if (handle != nullptr) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
+ if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
+ ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)",
+ __FUNCTION__, heap->getHeapID(), mMemoryHeapBase->getHeapID());
+ return;
+ }
uint32_t batchSize = 0;
{
Mutex::Autolock autoLock(mBatchLock);
@@ -1095,12 +1105,33 @@
return OK;
}
+status_t CameraSource::setStopTimeUs(int64_t stopTimeUs) {
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("Set stoptime: %lld us", (long long)stopTimeUs);
+
+ if (stopTimeUs < -1) {
+ ALOGE("Invalid stop time %lld us", (long long)stopTimeUs);
+ return BAD_VALUE;
+ } else if (stopTimeUs == -1) {
+ ALOGI("reset stopTime to be -1");
+ }
+
+ mStopSystemTimeUs = stopTimeUs;
+ return OK;
+}
+
bool CameraSource::shouldSkipFrameLocked(int64_t timestampUs) {
if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
ALOGV("Drop frame at %lld/%lld us", (long long)timestampUs, (long long)mStartTimeUs);
return true;
}
+ if (mStopSystemTimeUs != -1 && timestampUs >= mStopSystemTimeUs) {
+ ALOGV("Drop Camera frame at %lld stop time: %lld us",
+ (long long)timestampUs, (long long)mStopSystemTimeUs);
+ return true;
+ }
+
// May need to skip frame or modify timestamp. Currently implemented
// by the subclass CameraSourceTimeLapse.
if (skipCurrentFrame(timestampUs)) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 1f05440..445a861 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1001,6 +1001,12 @@
ALOGE("moov: depth %d", depth);
return ERROR_MALFORMED;
}
+
+ if (chunk_type == FOURCC('m', 'o', 'o', 'v') && mInitCheck == OK) {
+ ALOGE("duplicate moov");
+ return ERROR_MALFORMED;
+ }
+
if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) {
// store the offset of the first segment
mMoofFound = true;
@@ -1075,6 +1081,12 @@
if (!mLastTrack->meta->findInt32(kKeyTrackID, &trackId)) {
mLastTrack->skipTrack = true;
}
+
+ status_t err = verifyTrack(mLastTrack);
+ if (err != OK) {
+ mLastTrack->skipTrack = true;
+ }
+
if (mLastTrack->skipTrack) {
Track *cur = mFirstTrack;
@@ -1094,12 +1106,6 @@
return OK;
}
-
- status_t err = verifyTrack(mLastTrack);
-
- if (err != OK) {
- return err;
- }
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mInitCheck = OK;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
old mode 100755
new mode 100644
index b48257f..93d4f57
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -112,6 +112,7 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
void writeTrackHeader(bool use32BitOffset = true);
+ int64_t getMinCttsOffsetTimeUs();
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
bool isHevc() const { return mIsHevc; }
@@ -307,10 +308,17 @@
ListTableEntries<uint32_t, 2> *mCttsTableEntries;
int64_t mMinCttsOffsetTimeUs;
- int64_t mMaxCttsOffsetTimeUs;
+ int64_t mMinCttsOffsetTicks;
+ int64_t mMaxCttsOffsetTicks;
- // Save the last 10 frames' timestamp for debug.
- std::list<std::pair<int64_t, int64_t>> mTimestampDebugHelper;
+ // Save the last 10 frames' timestamp and frame type for debug.
+ struct TimestampDebugHelperEntry {
+ int64_t pts;
+ int64_t dts;
+ std::string frameType;
+ };
+
+ std::list<TimestampDebugHelperEntry> mTimestampDebugHelper;
// Sequence parameter set or picture parameter set
struct AVCParamSet {
@@ -343,6 +351,7 @@
void dumpTimeStamps();
+ int64_t getStartTimeOffsetTimeUs() const;
int32_t getStartTimeOffsetScaledTime() const;
static void *ThreadWrapper(void *me);
@@ -1133,9 +1142,22 @@
writeUdtaBox();
}
writeMetaBox();
- int32_t id = 1;
+ // Loop through all the tracks to get the global time offset if there is
+ // any ctts table appears in a video track.
+ int64_t minCttsOffsetTimeUs = kMaxCttsOffsetTimeUs;
for (List<Track *>::iterator it = mTracks.begin();
- it != mTracks.end(); ++it, ++id) {
+ it != mTracks.end(); ++it) {
+ minCttsOffsetTimeUs =
+ std::min(minCttsOffsetTimeUs, (*it)->getMinCttsOffsetTimeUs());
+ }
+ ALOGI("Ajust the moov start time from %lld us -> %lld us",
+ (long long)mStartTimestampUs,
+ (long long)(mStartTimestampUs + minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs));
+ // Adjust the global start time.
+ mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
(*it)->writeTrackHeader(mUse32BitOffset);
}
endBox(); // moov
@@ -1625,10 +1647,14 @@
mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
+ mMinCttsOffsetTimeUs(0),
+ mMinCttsOffsetTicks(0),
+ mMaxCttsOffsetTicks(0),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
mGotAllCodecSpecificData(false),
mReachedEOS(false),
+ mStartTimestampUs(-1),
mRotation(0) {
getCodecSpecificDataFromInputFormatIfPossible();
@@ -1834,10 +1860,12 @@
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds(data, size);
- if (esds.getCodecSpecificInfo(&data, &size) != OK) {
- data = NULL;
- size = 0;
+ if (esds.getCodecSpecificInfo(&data, &size) == OK &&
+ data != NULL &&
+ copyCodecSpecificData((uint8_t*)data, size) == OK) {
+ mGotAllCodecSpecificData = true;
}
+ return;
}
}
if (data != NULL && copyCodecSpecificData((uint8_t *)data, size) == OK) {
@@ -2133,13 +2161,17 @@
if (mDone) {
return OK;
}
- mDone = true;
+
if (stopSource) {
ALOGD("%s track source stopping", getTrackType());
mSource->stop();
ALOGD("%s track source stopped", getTrackType());
}
+ // Set mDone to be true after sucessfully stop mSource as mSource may be still outputting
+ // buffers to the writer.
+ mDone = true;
+
void *dummy;
pthread_join(mThread, &dummy);
status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
@@ -2519,12 +2551,12 @@
}
void MPEG4Writer::Track::dumpTimeStamps() {
- ALOGE("Dumping %s track's last 10 frames timestamp ", getTrackType());
+ ALOGE("Dumping %s track's last 10 frames timestamp and frame type ", getTrackType());
std::string timeStampString;
- for (std::list<std::pair<int64_t, int64_t>>::iterator num = mTimestampDebugHelper.begin();
- num != mTimestampDebugHelper.end(); ++num) {
- timeStampString += "(" + std::to_string(num->first)+
- "us, " + std::to_string(num->second) + "us) ";
+ for (std::list<TimestampDebugHelperEntry>::iterator entry = mTimestampDebugHelper.begin();
+ entry != mTimestampDebugHelper.end(); ++entry) {
+ timeStampString += "(" + std::to_string(entry->pts)+
+ "us, " + std::to_string(entry->dts) + "us " + entry->frameType + ") ";
}
ALOGE("%s", timeStampString.c_str());
}
@@ -2734,9 +2766,9 @@
previousPausedDurationUs += pausedDurationUs - lastDurationUs;
mResumed = false;
}
- std::pair<int64_t, int64_t> timestampPair;
+ TimestampDebugHelperEntry timestampDebugEntry;
timestampUs -= previousPausedDurationUs;
- timestampPair.first = timestampUs;
+ timestampDebugEntry.pts = timestampUs;
if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) {
copy->release();
mSource->stop();
@@ -2766,6 +2798,14 @@
}
mLastDecodingTimeUs = decodingTimeUs;
+ timestampDebugEntry.dts = decodingTimeUs;
+ timestampDebugEntry.frameType = isSync ? "Key frame" : "Non-Key frame";
+ // Insert the timestamp into the mTimestampDebugHelper
+ if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
+ mTimestampDebugHelper.pop_front();
+ }
+ mTimestampDebugHelper.push_back(timestampDebugEntry);
+
cttsOffsetTimeUs =
timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) {
@@ -2808,16 +2848,16 @@
// Update ctts time offset range
if (mStszTableEntries->count() == 0) {
- mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
- mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ mMinCttsOffsetTicks = currCttsOffsetTimeTicks;
+ mMaxCttsOffsetTicks = currCttsOffsetTimeTicks;
} else {
- if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) {
- mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
- } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) {
- mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ if (currCttsOffsetTimeTicks > mMaxCttsOffsetTicks) {
+ mMaxCttsOffsetTicks = currCttsOffsetTimeTicks;
+ } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTicks) {
+ mMinCttsOffsetTicks = currCttsOffsetTimeTicks;
+ mMinCttsOffsetTimeUs = cttsOffsetTimeUs;
}
}
-
}
if (mOwner->isRealTimeRecording()) {
@@ -2895,12 +2935,6 @@
lastDurationUs = timestampUs - lastTimestampUs;
lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
- timestampPair.second = timestampUs;
- // Insert the timestamp into the mTimestampDebugHelper
- if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
- mTimestampDebugHelper.pop_front();
- }
- mTimestampDebugHelper.push_back(timestampPair);
if (isSync != 0) {
addOneStssTableEntry(mStszTableEntries->count());
@@ -3158,7 +3192,7 @@
}
int64_t MPEG4Writer::Track::getDurationUs() const {
- return mTrackDurationUs;
+ return mTrackDurationUs + getStartTimeOffsetTimeUs();
}
int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
@@ -3213,6 +3247,16 @@
mOwner->endBox(); // trak
}
+int64_t MPEG4Writer::Track::getMinCttsOffsetTimeUs() {
+ // For video tracks with ctts table, this should return the minimum ctts
+ // offset in the table. For non-video tracks or video tracks without ctts
+ // table, this will return kMaxCttsOffsetTimeUs.
+ if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
+ return kMaxCttsOffsetTimeUs;
+ }
+ return mMinCttsOffsetTimeUs;
+}
+
void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
mOwner->beginBox("stbl");
mOwner->beginBox("stsd");
@@ -3642,30 +3686,41 @@
mOwner->endBox(); // pasp
}
-int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
+int64_t MPEG4Writer::Track::getStartTimeOffsetTimeUs() const {
int64_t trackStartTimeOffsetUs = 0;
int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
- if (mStartTimestampUs != moovStartTimeUs) {
+ if (mStartTimestampUs != -1 && mStartTimestampUs != moovStartTimeUs) {
CHECK_GT(mStartTimestampUs, moovStartTimeUs);
trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
}
- return (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
+ return trackStartTimeOffsetUs;
+}
+
+int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
+ return (getStartTimeOffsetTimeUs() * mTimeScale + 500000LL) / 1000000LL;
}
void MPEG4Writer::Track::writeSttsBox() {
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- uint32_t duration;
- CHECK(mSttsTableEntries->get(duration, 1));
- duration = htonl(duration); // Back to host byte order
- mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1);
+ if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
+ // For non-vdeio tracks or video tracks without ctts table,
+ // adjust duration of first sample for tracks to account for
+ // first sample not starting at the media start time.
+ // TODO: consider signaling this using some offset
+ // as this is not quite correct.
+ uint32_t duration;
+ CHECK(mSttsTableEntries->get(duration, 1));
+ duration = htonl(duration); // Back to host byte order
+ mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1);
+ }
mSttsTableEntries->write(mOwner);
mOwner->endBox(); // stts
}
void MPEG4Writer::Track::writeCttsBox() {
// There is no B frame at all
- if (mMinCttsOffsetTimeUs == mMaxCttsOffsetTimeUs) {
+ if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
return;
}
@@ -3675,11 +3730,12 @@
}
ALOGV("ctts box has %d entries with range [%" PRId64 ", %" PRId64 "]",
- mCttsTableEntries->count(), mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs);
+ mCttsTableEntries->count(), mMinCttsOffsetTicks, mMaxCttsOffsetTicks);
mOwner->beginBox("ctts");
mOwner->writeInt32(0); // version=0, flags=0
- int64_t delta = mMinCttsOffsetTimeUs - getStartTimeOffsetScaledTime();
+ int64_t deltaTimeUs = kMaxCttsOffsetTimeUs - getStartTimeOffsetTimeUs();
+ int64_t delta = (deltaTimeUs * mTimeScale + 500000LL) / 1000000LL;
mCttsTableEntries->adjustEntries([delta](size_t /* ix */, uint32_t (&value)[2]) {
// entries are <count, ctts> pairs; adjust only ctts
uint32_t duration = htonl(value[1]); // back to host byte order
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index bb20850..d808e5b 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -44,6 +44,9 @@
const int32_t kDefaultVideoEncoderDataSpace = HAL_DATASPACE_V0_BT709;
const int kStopTimeoutUs = 300000; // allow 1 sec for shutting down encoder
+// allow maximum 1 sec for stop time offset. This limits the the delay in the
+// input source.
+const int kMaxStopTimeOffsetUs = 1000000;
struct MediaCodecSource::Puller : public AHandler {
explicit Puller(const sp<MediaSource> &source);
@@ -54,7 +57,7 @@
void stopSource();
void pause();
void resume();
-
+ status_t setStopTimeUs(int64_t stopTimeUs);
bool readBuffer(MediaBuffer **buffer);
protected:
@@ -66,6 +69,7 @@
kWhatStart = 'msta',
kWhatStop,
kWhatPull,
+ kWhatSetStopTimeUs,
};
sp<MediaSource> mSource;
@@ -161,6 +165,12 @@
return err;
}
+status_t MediaCodecSource::Puller::setStopTimeUs(int64_t stopTimeUs) {
+ sp<AMessage> msg = new AMessage(kWhatSetStopTimeUs, this);
+ msg->setInt64("stop-time-us", stopTimeUs);
+ return postSynchronouslyAndReturnError(msg);
+}
+
status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta, const sp<AMessage> ¬ify) {
ALOGV("puller (%s) start", mIsAudio ? "audio" : "video");
mLooper->start(
@@ -250,6 +260,20 @@
break;
}
+ case kWhatSetStopTimeUs:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ int64_t stopTimeUs;
+ CHECK(msg->findInt64("stop-time-us", &stopTimeUs));
+ status_t err = mSource->setStopTimeUs(stopTimeUs);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
case kWhatStop:
{
mSource->stop();
@@ -364,11 +388,8 @@
}
-status_t MediaCodecSource::setStopStimeUs(int64_t stopTimeUs) {
- if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
- return OK;
- }
- sp<AMessage> msg = new AMessage(kWhatSetStopTimeOffset, mReflector);
+status_t MediaCodecSource::setStopTimeUs(int64_t stopTimeUs) {
+ sp<AMessage> msg = new AMessage(kWhatSetStopTimeUs, mReflector);
msg->setInt64("stop-time-us", stopTimeUs);
return postSynchronouslyAndReturnError(msg);
}
@@ -986,12 +1007,29 @@
mStopping = true;
+ int64_t timeoutUs = kStopTimeoutUs;
// if using surface, signal source EOS and wait for EOS to come back.
// otherwise, stop puller (which also clears the input buffer queue)
// and wait for the EOS message. We cannot call source->stop() because
// the encoder may still be processing input buffers.
if (mFlags & FLAG_USE_SURFACE_INPUT) {
mEncoder->signalEndOfInputStream();
+ // Increase the timeout if there is delay in the GraphicBufferSource
+ sp<AMessage> inputFormat;
+ int64_t stopTimeOffsetUs;
+ if (mEncoder->getInputFormat(&inputFormat) == OK &&
+ inputFormat->findInt64("android._stop-time-offset-us", &stopTimeOffsetUs) &&
+ stopTimeOffsetUs > 0) {
+ if (stopTimeOffsetUs > kMaxStopTimeOffsetUs) {
+ ALOGW("Source stopTimeOffsetUs %lld too large, limit at %lld us",
+ (long long)stopTimeOffsetUs, (long long)kMaxStopTimeOffsetUs);
+ stopTimeOffsetUs = kMaxStopTimeOffsetUs;
+ }
+ timeoutUs += stopTimeOffsetUs;
+ } else {
+ // Use kMaxStopTimeOffsetUs if stop time offset is not provided by input source
+ timeoutUs = kMaxStopTimeOffsetUs;
+ }
} else {
mPuller->stop();
}
@@ -999,7 +1037,7 @@
// complete stop even if encoder/puller stalled
sp<AMessage> timeoutMsg = new AMessage(kWhatStopStalled, mReflector);
timeoutMsg->setInt32("generation", mGeneration);
- timeoutMsg->post(kStopTimeoutUs);
+ timeoutMsg->post(timeoutUs);
break;
}
@@ -1055,7 +1093,7 @@
response->postReply(replyID);
break;
}
- case kWhatSetStopTimeOffset:
+ case kWhatSetStopTimeUs:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
@@ -1063,11 +1101,13 @@
int64_t stopTimeUs;
CHECK(msg->findInt64("stop-time-us", &stopTimeUs));
- // Propagate the timestamp offset to GraphicBufferSource.
+ // Propagate the stop time to GraphicBufferSource.
if (mFlags & FLAG_USE_SURFACE_INPUT) {
sp<AMessage> params = new AMessage;
params->setInt64("stop-time-us", stopTimeUs);
err = mEncoder->setParameters(params);
+ } else {
+ err = mPuller->setStopTimeUs(stopTimeUs);
}
sp<AMessage> response = new AMessage;
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 0cf6fbf..9278381 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -32,6 +32,8 @@
#include <ui/GraphicBuffer.h>
+#include <system/window.h>
+
// Maximum late time allowed for a video frame to be rendered. When a video
// frame arrives later than this number, it will be discarded without rendering.
static const int64_t kMaxAllowedVideoLateTimeUs = 40000ll;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 442d0a4..766230a 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -578,6 +578,10 @@
}
// First two pages are header pages.
if (err == ERROR_END_OF_STREAM || mCurrentPage.mPageNo > 2) {
+ if (mBuf != NULL) {
+ mBuf->release();
+ mBuf = NULL;
+ }
break;
}
curGranulePosition = mCurrentPage.mGranulePosition;
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index ea7d5af..90b8603 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -36,6 +36,12 @@
//static
sp<SimpleDecodingSource> SimpleDecodingSource::Create(
+ const sp<IMediaSource> &source, uint32_t flags) {
+ return SimpleDecodingSource::Create(source, flags, nullptr, nullptr);
+}
+
+//static
+sp<SimpleDecodingSource> SimpleDecodingSource::Create(
const sp<IMediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec) {
sp<Surface> surface = static_cast<Surface*>(nativeWindow.get());
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 621c2ce..0aea8e1 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1069,6 +1069,16 @@
buffer->meta()->setInt32("csd", true);
buffer->meta()->setInt64("timeUs", 0);
msg->setBuffer("csd-2", buffer);
+ } else if (meta->findData(kKeyFlacMetadata, &type, &data, &size)) {
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ if (buffer.get() == NULL || buffer->base() == NULL) {
+ return NO_MEMORY;
+ }
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
} else if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) {
sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
if (buffer.get() == NULL || buffer->base() == NULL) {
@@ -1552,6 +1562,7 @@
{ MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
{ MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS},
{ MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3},
+ { MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC},
{ 0, AUDIO_FORMAT_INVALID }
};
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index ce6d50f..b75b468 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -80,7 +80,16 @@
for (size_t j = 0; j < sizeOfScalingList; ++j) {
if (nextScale != 0) {
signed delta_scale = parseSE(br);
- nextScale = (lastScale + delta_scale + 256) % 256;
+ // ISO_IEC_14496-10_201402-ITU, 7.4.2.1.1.1, The value of delta_scale
+ // shall be in the range of −128 to +127, inclusive.
+ if (delta_scale < -128) {
+ ALOGW("delta_scale (%d) is below range, capped to -128", delta_scale);
+ delta_scale = -128;
+ } else if (delta_scale > 127) {
+ ALOGW("delta_scale (%d) is above range, capped to 127", delta_scale);
+ delta_scale = 127;
+ }
+ nextScale = (lastScale + (delta_scale + 256)) % 256;
}
lastScale = (nextScale == 0) ? lastScale : nextScale;
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
new file mode 100644
index 0000000..6ac264d
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -0,0 +1,36 @@
+cc_library_shared {
+ name: "libstagefright_soft_flacdec",
+
+ srcs: [
+ "SoftFlacDecoder.cpp",
+ ],
+
+ include_dirs: [
+ "external/flac/include",
+ "frameworks/av/media/libstagefright/flac/dec",
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/native/include/media/openmax",
+ ],
+
+ cflags: ["-Werror"],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libstagefright_flacdec",
+ "libstagefright_omx",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+}
diff --git a/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2
diff --git a/media/libstagefright/codecs/flac/dec/NOTICE b/media/libstagefright/codecs/flac/dec/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
new file mode 100644
index 0000000..cff4a33
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2017 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 "SoftFlacDecoder"
+#include <utils/Log.h>
+
+#include "SoftFlacDecoder.h"
+#include <OMX_AudioExt.h>
+#include <OMX_IndexExt.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/misc.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+SoftFlacDecoder::SoftFlacDecoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mFLACDecoder(NULL),
+ mHasStreamInfo(false),
+ mInputBufferCount(0),
+ mSignalledError(false),
+ mOutputPortSettingsChange(NONE) {
+ ALOGV("ctor:");
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ initPorts();
+ initDecoder();
+}
+
+SoftFlacDecoder::~SoftFlacDecoder() {
+ ALOGV("dtor:");
+}
+
+void SoftFlacDecoder::initPorts() {
+ ALOGV("initPorts:");
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ def.nPortIndex = 0;
+ def.eDir = OMX_DirInput;
+ def.nBufferCountMin = kNumInputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 32768;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 1;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/flac");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC;
+
+ addPort(def);
+
+ def.nPortIndex = 1;
+ def.eDir = OMX_DirOutput;
+ def.nBufferCountMin = kNumOutputBuffers;
+ def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferSize = 4096 * FLACDecoder::kMaxChannels;
+ def.bEnabled = OMX_TRUE;
+ def.bPopulated = OMX_FALSE;
+ def.eDomain = OMX_PortDomainAudio;
+ def.bBuffersContiguous = OMX_FALSE;
+ def.nBufferAlignment = 2;
+
+ def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+ addPort(def);
+}
+
+void SoftFlacDecoder::initDecoder() {
+ ALOGV("initDecoder:");
+ mFLACDecoder = FLACDecoder::Create();
+ if (mFLACDecoder == NULL) {
+ ALOGE("initDecoder: failed to create FLACDecoder");
+ mSignalledError = true;
+ }
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::initCheck() const {
+ if (mSignalledError) {
+ if (mFLACDecoder == NULL) {
+ ALOGE("initCheck: failed due to NULL encoder");
+ return OMX_ErrorDynamicResourcesUnavailable;
+ }
+ return OMX_ErrorUndefined;
+ }
+
+ return SimpleSoftOMXComponent::initCheck();
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) {
+ ALOGV("internalGetParameter: index(%x)", index);
+ switch ((OMX_U32)index) {
+ case OMX_IndexParamAudioFlac:
+ {
+ OMX_AUDIO_PARAM_FLACTYPE *flacParams =
+ (OMX_AUDIO_PARAM_FLACTYPE *)params;
+
+ if (!isValidOMXParam(flacParams)) {
+ ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): invalid omx params");
+ return OMX_ErrorBadParameter;
+ }
+
+ if (flacParams->nPortIndex != 0) {
+ ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): bad port index");
+ return OMX_ErrorBadPortIndex;
+ }
+
+ flacParams->nCompressionLevel = 0;
+
+ if (isConfigured()) {
+ flacParams->nChannels = mStreamInfo.channels;
+ flacParams->nSampleRate = mStreamInfo.sample_rate;
+ } else {
+ flacParams->nChannels = 1;
+ flacParams->nSampleRate = 44100;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (!isValidOMXParam(pcmParams)) {
+ ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): invalid omx params");
+ return OMX_ErrorBadParameter;
+ }
+
+ if (pcmParams->nPortIndex != 1) {
+ ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): bad port index");
+ return OMX_ErrorBadPortIndex;
+ }
+
+ pcmParams->eNumData = OMX_NumericalDataSigned;
+ pcmParams->eEndian = OMX_EndianBig;
+ pcmParams->bInterleaved = OMX_TRUE;
+ pcmParams->nBitPerSample = 16;
+ pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+ pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF;
+ pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
+ pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS;
+ pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS;
+
+ if (isConfigured()) {
+ pcmParams->nChannels = mStreamInfo.channels;
+ pcmParams->nSamplingRate = mStreamInfo.sample_rate;
+ } else {
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = 44100;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftFlacDecoder::internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) {
+ ALOGV("internalSetParameter: index(%x)", (int)index);
+ switch ((int)index) {
+ case OMX_IndexParamStandardComponentRole:
+ {
+ const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+ (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+ if (!isValidOMXParam(roleParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (strncmp((const char *)roleParams->cRole,
+ "audio_decoder.flac",
+ OMX_MAX_STRINGNAME_SIZE - 1) != 0) {
+ return OMX_ErrorInvalidComponentName;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (!isValidOMXParam(pcmParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorBadPortIndex;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+bool SoftFlacDecoder::isConfigured() const {
+ return mHasStreamInfo;
+}
+
+void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
+ ALOGV("onQueueFilled:");
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!inQueue.empty() && !outQueue.empty()) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ uint8_t* inBuffer = inHeader->pBuffer + inHeader->nOffset;
+ uint32_t inBufferLength = inHeader->nFilledLen;
+ bool endOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+
+ if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ ALOGE("onQueueFilled: first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
+ inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+ }
+ if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
+ status_t decoderErr = mFLACDecoder->parseMetadata(inBuffer, inBufferLength);
+ mInputBufferCount++;
+
+ if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
+ ALOGE("onQueueFilled: FLACDecoder parseMetaData returns error %d", decoderErr);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
+ return;
+ }
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ notifyEmptyBufferDone(inHeader);
+
+ if (decoderErr == WOULD_BLOCK) {
+ continue;
+ }
+ mStreamInfo = mFLACDecoder->getStreamInfo();
+ mHasStreamInfo = true;
+
+ // Only send out port settings changed event if both sample rate
+ // and numChannels are valid.
+ if (mStreamInfo.sample_rate && mStreamInfo.channels) {
+ ALOGD("onQueueFilled: initially configuring decoder: %d Hz, %d channels",
+ mStreamInfo.sample_rate, mStreamInfo.channels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
+ return;
+ }
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+ short *outBuffer =
+ reinterpret_cast<short *>(outHeader->pBuffer + outHeader->nOffset);
+ size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset;
+
+ status_t decoderErr = mFLACDecoder->decodeOneFrame(
+ inBuffer, inBufferLength, outBuffer, &outBufferSize);
+ if (decoderErr != OK) {
+ ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
+ return;
+ }
+
+ mInputBufferCount++;
+ int64_t ts = inHeader->nTimeStamp;
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ notifyEmptyBufferDone(inHeader);
+
+ if (endOfInput) {
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ } else if (outBufferSize == 0) {
+ continue;
+ } else {
+ outHeader->nFlags = 0;
+ }
+
+ outHeader->nFilledLen = outBufferSize;
+ outHeader->nTimeStamp = ts;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ notifyFillBufferDone(outHeader);
+ }
+}
+
+void SoftFlacDecoder::onPortFlushCompleted(OMX_U32 portIndex) {
+ ALOGV("onPortFlushCompleted: portIndex(%u)", portIndex);
+ if (portIndex == 0) {
+ drainDecoder();
+ }
+}
+
+void SoftFlacDecoder::drainDecoder() {
+ mFLACDecoder->flush();
+}
+
+void SoftFlacDecoder::onReset() {
+ drainDecoder();
+
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ mHasStreamInfo = false;
+ mInputBufferCount = 0;
+ mSignalledError = false;
+ mOutputPortSettingsChange = NONE;
+}
+
+void SoftFlacDecoder::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+ ALOGV("onPortEnableCompleted: portIndex(%u), enabled(%d)", portIndex, enabled);
+ if (portIndex != 1) {
+ return;
+ }
+
+ switch (mOutputPortSettingsChange) {
+ case NONE:
+ break;
+
+ case AWAITING_DISABLED:
+ {
+ CHECK(!enabled);
+ mOutputPortSettingsChange = AWAITING_ENABLED;
+ PortInfo *info = editPortInfo(1 /* portIndex */);
+ if (!info->mDef.bEnabled) {
+ info->mDef.nBufferSize = mStreamInfo.max_blocksize * mStreamInfo.channels * 2;
+ }
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+ CHECK(enabled);
+ mOutputPortSettingsChange = NONE;
+ break;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ ALOGV("createSoftOMXComponent: flac decoder");
+ return new android::SoftFlacDecoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
new file mode 100644
index 0000000..c09081d
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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 SOFT_FLAC_DECODER_H
+#define SOFT_FLAC_DECODER_H
+
+#include "FLACDecoder.h"
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftFlacDecoder : public SimpleSoftOMXComponent {
+ SoftFlacDecoder(const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+ virtual OMX_ERRORTYPE initCheck() const override;
+
+protected:
+ virtual ~SoftFlacDecoder();
+
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params) override;
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params) override;
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex) override;
+ virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled) override;
+ virtual void onReset() override;
+
+private:
+ enum {
+ kNumInputBuffers = 4,
+ kNumOutputBuffers = 4,
+ };
+
+ sp<FLACDecoder> mFLACDecoder;
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+ bool mHasStreamInfo;
+ size_t mInputBufferCount;
+ bool mSignalledError;
+
+ enum {
+ NONE,
+ AWAITING_DISABLED,
+ AWAITING_ENABLED
+ } mOutputPortSettingsChange;
+
+ void initPorts();
+ void initDecoder();
+ bool isConfigured() const;
+ void drainDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftFlacDecoder);
+};
+
+} // namespace android
+
+#endif // SOFT_FLAC_DECODER_H
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 6e70ded..9a69226 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -89,10 +89,10 @@
}
-static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
+static ssize_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
OMX_S64 minTimeStamp = LLONG_MAX;
- int idx = -1;
- for (size_t i = 0; i < MAX_TIME_STAMPS; i++) {
+ ssize_t idx = -1;
+ for (ssize_t i = 0; i < MAX_TIME_STAMPS; i++) {
if (pIsTimeStampValid[i]) {
if (pNTimeStamp[i] < minTimeStamp) {
minTimeStamp = pNTimeStamp[i];
@@ -788,10 +788,15 @@
}
if (s_dec_op.u4_output_present) {
- size_t timeStampIdx;
+ ssize_t timeStampIdx;
outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid);
+ if (timeStampIdx < 0) {
+ ALOGE("b/62872863, Invalid timestamp index!");
+ android_errorWriteLog(0x534e4554, "62872863");
+ return;
+ }
outHeader->nTimeStamp = mTimeStamps[timeStampIdx];
mTimeStampsValid[timeStampIdx] = false;
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index be04e08..14dd250 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -23,6 +23,9 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaDefs.h>
+static int kDefaultChannelCount = 1;
+static int kDefaultSamplingRate = 48000;
+
extern "C" {
#include <Tremolo/codec_internal.h>
@@ -148,8 +151,8 @@
vorbisParams->bDownmix = OMX_FALSE;
if (!isConfigured()) {
- vorbisParams->nChannels = 1;
- vorbisParams->nSampleRate = 44100;
+ vorbisParams->nChannels = kDefaultChannelCount;
+ vorbisParams->nSampleRate = kDefaultSamplingRate;
} else {
vorbisParams->nChannels = mVi->channels;
vorbisParams->nSampleRate = mVi->rate;
@@ -157,7 +160,6 @@
vorbisParams->nMinBitRate = mVi->bitrate_lower;
vorbisParams->nMaxBitRate = mVi->bitrate_upper;
}
-
return OMX_ErrorNone;
}
@@ -183,8 +185,8 @@
pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
if (!isConfigured()) {
- pcmParams->nChannels = 1;
- pcmParams->nSamplingRate = 44100;
+ pcmParams->nChannels = kDefaultChannelCount;
+ pcmParams->nSamplingRate = kDefaultSamplingRate;
} else {
pcmParams->nChannels = mVi->channels;
pcmParams->nSamplingRate = mVi->rate;
@@ -313,8 +315,12 @@
mState = new vorbis_dsp_state;
CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
+ if (mVi->rate != kDefaultSamplingRate ||
+ mVi->channels != kDefaultChannelCount) {
+ ALOGV("vorbis: rate/channels changed: %ld/%d", mVi->rate, mVi->channels);
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
}
inQueue.erase(inQueue.begin());
diff --git a/media/libstagefright/colorconversion/Android.bp b/media/libstagefright/colorconversion/Android.bp
index 11fe5eb..16e9ded 100644
--- a/media/libstagefright/colorconversion/Android.bp
+++ b/media/libstagefright/colorconversion/Android.bp
@@ -10,7 +10,10 @@
"frameworks/native/include/media/openmax",
],
- shared_libs: ["libui"],
+ shared_libs: [
+ "libui",
+ "libnativewindow",
+ ],
static_libs: ["libyuv_static"],
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 536d40d..a07787a 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -30,10 +30,6 @@
namespace android {
-static bool runningInEmulator() {
- char prop[PROPERTY_VALUE_MAX];
- return (property_get("ro.kernel.qemu", prop, NULL) > 0);
-}
static int ALIGN(int x, int y) {
// y must be a power of 2.
@@ -108,7 +104,7 @@
size_t bufHeight = mCropHeight;
// hardware has YUV12 and RGBA8888 support, so convert known formats
- if (!runningInEmulator()) {
+ {
switch (mColorFormat) {
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
@@ -205,7 +201,7 @@
}
std::list<FrameRenderTracker::Info> SoftwareRenderer::render(
- const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs,
+ const void *data, size_t , int64_t mediaTimeUs, nsecs_t renderTimeNs,
void* /*platformPrivate*/, const sp<AMessage>& format) {
resetFormatIfChanged(format);
FrameRenderTracker::Info *info = NULL;
@@ -244,14 +240,15 @@
buf->stride, buf->height,
0, 0, mCropWidth - 1, mCropHeight - 1);
} else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) {
- if ((size_t)mWidth * mHeight * 3 / 2 > size) {
- goto skip_copying;
- }
const uint8_t *src_y = (const uint8_t *)data;
const uint8_t *src_u =
(const uint8_t *)data + mWidth * mHeight;
const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
+ src_y +=mCropLeft + mCropTop * mWidth;
+ src_u +=(mCropLeft + mCropTop * mWidth / 2)/2;
+ src_v +=(mCropLeft + mCropTop * mWidth / 2)/2;
+
uint8_t *dst_y = (uint8_t *)dst;
size_t dst_y_size = buf->stride * buf->height;
size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
@@ -259,6 +256,10 @@
uint8_t *dst_v = dst_y + dst_y_size;
uint8_t *dst_u = dst_v + dst_c_size;
+ dst_y += mCropTop * buf->stride + mCropLeft;
+ dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+ dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+
for (int y = 0; y < mCropHeight; ++y) {
memcpy(dst_y, src_y, mCropWidth);
@@ -277,12 +278,12 @@
}
} else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
|| mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
- if ((size_t)mWidth * mHeight * 3 / 2 > size) {
- goto skip_copying;
- }
const uint8_t *src_y = (const uint8_t *)data;
const uint8_t *src_uv = (const uint8_t *)data
- + mWidth * (mHeight - mCropTop / 2);
+ + mWidth * mHeight;
+
+ src_y += mCropLeft + mCropTop * mWidth;
+ src_uv += (mCropLeft + mCropTop * mWidth) / 2;
uint8_t *dst_y = (uint8_t *)dst;
@@ -292,6 +293,10 @@
uint8_t *dst_v = dst_y + dst_y_size;
uint8_t *dst_u = dst_v + dst_c_size;
+ dst_y += mCropTop * buf->stride + mCropLeft;
+ dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+ dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+
for (int y = 0; y < mCropHeight; ++y) {
memcpy(dst_y, src_y, mCropWidth);
@@ -311,11 +316,8 @@
dst_v += dst_c_stride;
}
} else if (mColorFormat == OMX_COLOR_Format24bitRGB888) {
- if ((size_t)mWidth * mHeight * 3 > size) {
- goto skip_copying;
- }
- uint8_t* srcPtr = (uint8_t*)data;
- uint8_t* dstPtr = (uint8_t*)dst;
+ uint8_t* srcPtr = (uint8_t*)data + mWidth * mCropTop * 3 + mCropLeft * 3;
+ uint8_t* dstPtr = (uint8_t*)dst + buf->stride * mCropTop * 3 + mCropLeft * 3;
for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
memcpy(dstPtr, srcPtr, mCropWidth * 3);
@@ -323,14 +325,11 @@
dstPtr += buf->stride * 3;
}
} else if (mColorFormat == OMX_COLOR_Format32bitARGB8888) {
- if ((size_t)mWidth * mHeight * 4 > size) {
- goto skip_copying;
- }
uint8_t *srcPtr, *dstPtr;
for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
- srcPtr = (uint8_t*)data + mWidth * 4 * y;
- dstPtr = (uint8_t*)dst + buf->stride * 4 * y;
+ srcPtr = (uint8_t*)data + mWidth * 4 * (y + mCropTop) + mCropLeft * 4;
+ dstPtr = (uint8_t*)dst + buf->stride * 4 * (y + mCropTop) + mCropLeft * 4;
for (size_t x = 0; x < (size_t)mCropWidth; ++x) {
uint8_t a = *srcPtr++;
for (size_t i = 0; i < 3; ++i) { // copy RGB
@@ -340,11 +339,8 @@
}
}
} else if (mColorFormat == OMX_COLOR_Format32BitRGBA8888) {
- if ((size_t)mWidth * mHeight * 4 > size) {
- goto skip_copying;
- }
- uint8_t* srcPtr = (uint8_t*)data;
- uint8_t* dstPtr = (uint8_t*)dst;
+ uint8_t* srcPtr = (uint8_t*)data + mWidth * mCropTop * 4 + mCropLeft * 4;
+ uint8_t* dstPtr = (uint8_t*)dst + buf->stride * mCropTop * 4 + mCropLeft * 4;
for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
memcpy(dstPtr, srcPtr, mCropWidth * 4);
diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml
index b957b0c..632088a 100644
--- a/media/libstagefright/data/media_codecs_google_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_audio.xml
@@ -61,6 +61,11 @@
<Limit name="sample-rate" ranges="8000-96000" />
<Limit name="bitrate" range="1-10000000" />
</MediaCodec>
+ <MediaCodec name="OMX.google.flac.decoder" type="audio/flac">
+ <Limit name="channel-count" max="8" />
+ <Limit name="sample-rate" ranges="1-655350" />
+ <Limit name="bitrate" range="1-21000000" />
+ </MediaCodec>
</Decoders>
<Encoders>
<MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm">
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
new file mode 100644
index 0000000..284c25f
--- /dev/null
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -0,0 +1,34 @@
+cc_library_shared {
+ name: "libstagefright_flacdec",
+
+ srcs: [
+ "FLACDecoder.cpp",
+ ],
+
+ include_dirs: [
+ "external/flac/include",
+ "frameworks/av/media/libstagefright/include",
+ ],
+
+ cflags: ["-Werror"],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: ["libFLAC"],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+}
diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp
new file mode 100644
index 0000000..8c7137c
--- /dev/null
+++ b/media/libstagefright/flac/dec/FLACDecoder.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2017 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 "FLACDecoder"
+#include <utils/Log.h>
+
+#include "FLACDecoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+// These are the corresponding callbacks with C++ calling conventions
+FLAC__StreamDecoderReadStatus FLACDecoder::readCallback(
+ FLAC__byte buffer[], size_t *bytes) {
+ if (mBuffer == nullptr || mBufferLen == 0) {
+ *bytes = 0;
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ size_t actual = *bytes;
+ if (actual > mBufferDataSize - mBufferPos) {
+ actual = mBufferDataSize - mBufferPos;
+ }
+ memcpy(buffer, mBuffer + mBufferPos, actual);
+ mBufferPos += actual;
+ *bytes = actual;
+ return (actual == 0 ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
+ : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE);
+}
+
+FLAC__StreamDecoderWriteStatus FLACDecoder::writeCallback(
+ const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+ if (!mWriteRequested) {
+ ALOGE("writeCallback: unexpected");
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+
+ mWriteRequested = false;
+ // FLAC decoder doesn't free or realloc buffer until next frame or finish
+ mWriteHeader = frame->header;
+ memmove(mWriteBuffer, buffer, sizeof(const FLAC__int32 * const) * getChannels());
+ mWriteCompleted = true;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void FLACDecoder::metadataCallback(const FLAC__StreamMetadata *metadata)
+{
+ switch (metadata->type) {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ {
+ if (mStreamInfoValid) {
+ ALOGE("metadataCallback: unexpected STREAMINFO");
+ } else {
+ mStreamInfo = metadata->data.stream_info;
+ mStreamInfoValid = true;
+ }
+ break;
+ }
+
+ /* TODO: enable metadata parsing below.
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ {
+ const FLAC__StreamMetadata_VorbisComment *vc;
+ vc = &metadata->data.vorbis_comment;
+ for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) {
+ FLAC__StreamMetadata_VorbisComment_Entry *vce;
+ vce = &vc->comments[i];
+ if (mFileMetadata != 0 && vce->entry != NULL) {
+ parseVorbisComment(mFileMetadata, (const char *) vce->entry,
+ vce->length);
+ }
+ }
+ break;
+ }
+
+ case FLAC__METADATA_TYPE_PICTURE:
+ {
+ if (mFileMetadata != 0) {
+ const FLAC__StreamMetadata_Picture *p = &metadata->data.picture;
+ mFileMetadata->setData(kKeyAlbumArt,
+ MetaData::TYPE_NONE, p->data, p->data_length);
+ mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type);
+ }
+ break;
+ }
+ */
+
+ default:
+ ALOGW("metadataCallback: unexpected type %u", metadata->type);
+ break;
+ }
+}
+
+void FLACDecoder::errorCallback(FLAC__StreamDecoderErrorStatus status)
+{
+ ALOGE("errorCallback: status=%d", status);
+ mErrorStatus = status;
+}
+
+// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved.
+// These are candidates for optimization if needed.
+static void copyMono8(
+ short *dst,
+ const int * src[FLACDecoder::kMaxChannels],
+ unsigned nSamples,
+ unsigned /* nChannels */) {
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] << 8;
+ }
+}
+
+static void copyStereo8(
+ short *dst,
+ const int * src[FLACDecoder::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(
+ short *dst,
+ const int * src[FLACDecoder::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 copyMono16(
+ short *dst,
+ const int * src[FLACDecoder::kMaxChannels],
+ unsigned nSamples,
+ unsigned /* nChannels */) {
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i];
+ }
+}
+
+static void copyStereo16(
+ short *dst,
+ const int * src[FLACDecoder::kMaxChannels],
+ unsigned nSamples,
+ unsigned /* nChannels */) {
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i];
+ *dst++ = src[1][i];
+ }
+}
+
+static void copyMultiCh16(
+ short *dst,
+ const int * src[FLACDecoder::kMaxChannels],
+ unsigned nSamples,
+ unsigned nChannels) {
+ for (unsigned i = 0; i < nSamples; ++i) {
+ for (unsigned c = 0; c < nChannels; ++c) {
+ *dst++ = src[c][i];
+ }
+ }
+}
+
+// TODO: 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
+static void copyMono24(
+ short *dst,
+ const int * src[FLACDecoder::kMaxChannels],
+ unsigned nSamples,
+ unsigned /* nChannels */) {
+ for (unsigned i = 0; i < nSamples; ++i) {
+ *dst++ = src[0][i] >> 8;
+ }
+}
+
+static void copyStereo24(
+ short *dst,
+ const int * src[FLACDecoder::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(
+ short *dst,
+ const int * src[FLACDecoder::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
+sp<FLACDecoder> FLACDecoder::Create() {
+ sp<FLACDecoder> decoder = new FLACDecoder();
+ if (decoder->init() != OK) {
+ return NULL;
+ }
+ return decoder;
+}
+
+FLACDecoder::FLACDecoder()
+ : mDecoder(NULL),
+ mBuffer(NULL),
+ mBufferLen(0),
+ mBufferPos(0),
+ mBufferDataSize(0),
+ mStreamInfoValid(false),
+ mWriteRequested(false),
+ mWriteCompleted(false),
+ mErrorStatus((FLAC__StreamDecoderErrorStatus) -1),
+ mCopy(nullptr) {
+ ALOGV("ctor:");
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ memset(&mWriteHeader, 0, sizeof(mWriteHeader));
+ memset(&mWriteBuffer, 0, sizeof(mWriteBuffer));
+}
+
+FLACDecoder::~FLACDecoder() {
+ ALOGV("dtor:");
+ if (mDecoder != NULL) {
+ FLAC__stream_decoder_delete(mDecoder);
+ mDecoder = NULL;
+ }
+ if (mBuffer != NULL) {
+ free(mBuffer);
+ }
+}
+
+status_t FLACDecoder::init() {
+ ALOGV("init:");
+ // setup libFLAC stream decoder
+ mDecoder = FLAC__stream_decoder_new();
+ if (mDecoder == NULL) {
+ ALOGE("init: failed to create libFLAC stream decoder");
+ return NO_INIT;
+ }
+ FLAC__stream_decoder_set_md5_checking(mDecoder, false);
+ FLAC__stream_decoder_set_metadata_ignore_all(mDecoder);
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_STREAMINFO);
+ /*
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_PICTURE);
+ FLAC__stream_decoder_set_metadata_respond(
+ mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ */
+ static auto read_callback =
+ [] (const FLAC__StreamDecoder * /* decoder */,
+ FLAC__byte buffer[],
+ size_t *bytes,
+ void *client_data) -> FLAC__StreamDecoderReadStatus {
+ return ((FLACDecoder *) client_data)->readCallback(buffer, bytes); };
+
+ static auto write_callback =
+ [] (const FLAC__StreamDecoder * /* decoder */,
+ const FLAC__Frame *frame,
+ const FLAC__int32 * const buffer[],
+ void *client_data) -> FLAC__StreamDecoderWriteStatus {
+ return ((FLACDecoder *) client_data)->writeCallback(frame, buffer); };
+
+ static auto metadata_callback =
+ [] (const FLAC__StreamDecoder * /* decoder */,
+ const FLAC__StreamMetadata *metadata,
+ void *client_data) {
+ ((FLACDecoder *) client_data)->metadataCallback(metadata); };
+
+ static auto error_callback =
+ [] (const FLAC__StreamDecoder * /* decoder */,
+ FLAC__StreamDecoderErrorStatus status,
+ void *client_data) {
+ ((FLACDecoder *) client_data)->errorCallback(status); };
+
+ FLAC__StreamDecoderInitStatus initStatus =
+ FLAC__stream_decoder_init_stream(
+ mDecoder,
+ read_callback,
+ NULL /* seek_callback */,
+ NULL /* tell_callback */,
+ NULL /* length_callback */,
+ NULL /* eof_callback */,
+ write_callback,
+ metadata_callback,
+ error_callback,
+ (void *)this);
+ if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ ALOGE("init: init_stream failed, returned %d", initStatus);
+ return NO_INIT;
+ }
+ return OK;
+}
+
+void FLACDecoder::flush() {
+ ALOGV("flush:");
+ mBufferPos = 0;
+ mBufferDataSize = 0;
+ mStreamInfoValid = false;
+ if (!FLAC__stream_decoder_reset(mDecoder)) {
+ ALOGE("flush: failed to reset FLAC stream decoder");
+ }
+}
+
+status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) {
+ ALOGV("parseMetadata: input size(%zu)", inBufferLen);
+ //hexdump(inBuffer, inBufferLen);
+
+ if (mStreamInfoValid) {
+ ALOGE("parseMetadata: already have full metadata blocks");
+ return ERROR_MALFORMED;
+ }
+
+ status_t err = addDataToBuffer(inBuffer, inBufferLen);
+ if (err != OK) {
+ ALOGE("parseMetadata: addDataToBuffer returns error %d", err);
+ return err;
+ }
+
+ if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) {
+ if (!FLAC__stream_decoder_reset(mDecoder)) {
+ ALOGE("parseMetadata: failed to reset FLAC stream decoder");
+ return FAILED_TRANSACTION;
+ }
+ mBufferPos = 0;
+ ALOGV("parseMetadata: do not have full metadata blocks yet");
+ return WOULD_BLOCK;
+ }
+
+ if (!mStreamInfoValid) {
+ ALOGE("parseMetadata: missing STREAMINFO");
+ return ERROR_MALFORMED;
+ }
+
+ // check block size
+ if (getMaxBlockSize() == 0) {
+ ALOGE("wrong max blocksize %u", getMaxBlockSize());
+ mStreamInfoValid = false;
+ return ERROR_MALFORMED;
+ }
+
+ // check channel count
+ if (getChannels() == 0 || getChannels() > kMaxChannels) {
+ ALOGE("unsupported channel count %u", getChannels());
+ mStreamInfoValid = false;
+ return ERROR_MALFORMED;
+ }
+
+ // check bit depth
+ switch (getBitsPerSample()) {
+ case 8:
+ case 16:
+ case 24:
+ break;
+
+ default:
+ ALOGE("parseMetadata: unsupported bits per sample %u", getBitsPerSample());
+ mStreamInfoValid = false;
+ return ERROR_MALFORMED;
+ }
+
+ // configure the appropriate copy function, defaulting to trespass
+ static const struct {
+ unsigned mChannels;
+ unsigned mBitsPerSample;
+ void (*mCopy)(short *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 (const auto &entry : table) {
+ if (entry.mChannels >= getChannels() &&
+ entry.mBitsPerSample == getBitsPerSample()) {
+ mCopy = entry.mCopy;
+ break;
+ }
+ }
+
+ // Now we have all metadata blocks.
+ mBufferPos = 0;
+ mBufferDataSize = 0;
+
+ return OK;
+}
+
+status_t FLACDecoder::decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
+ short *outBuffer, size_t *outBufferLen) {
+ ALOGV("decodeOneFrame: input size(%zu)", inBufferLen);
+
+ if (inBufferLen == 0) {
+ ALOGV("decodeOneFrame: no input data");
+ if (outBufferLen) {
+ *outBufferLen = 0;
+ }
+ return OK;
+ }
+
+ if (!mStreamInfoValid) {
+ ALOGW("decodeOneFrame: no streaminfo metadata block");
+ }
+
+ status_t err = addDataToBuffer(inBuffer, inBufferLen);
+ if (err != OK) {
+ ALOGW("decodeOneFrame: addDataToBuffer returns error %d", err);
+ return err;
+ }
+
+ mWriteRequested = true;
+ mWriteCompleted = false;
+ if (!FLAC__stream_decoder_process_single(mDecoder)) {
+ ALOGE("decodeOneFrame: process_single failed");
+ return ERROR_MALFORMED;
+ }
+ if (!mWriteCompleted) {
+ ALOGV("decodeOneFrame: write did not complete");
+ if (outBufferLen) {
+ *outBufferLen = 0;
+ }
+ return OK;
+ }
+
+ // frame header should be consistent with STREAMINFO
+ unsigned blocksize = mWriteHeader.blocksize;
+ if (blocksize == 0 || blocksize > getMaxBlockSize()) {
+ ALOGE("decodeOneFrame: write invalid blocksize %u", blocksize);
+ return ERROR_MALFORMED;
+ }
+ if (mWriteHeader.sample_rate != getSampleRate() ||
+ mWriteHeader.channels != getChannels() ||
+ mWriteHeader.bits_per_sample != getBitsPerSample()) {
+ ALOGE("decodeOneFrame: parameters are changed mid-stream: %d/%d/%d -> %d/%d/%d",
+ getSampleRate(), getChannels(), getBitsPerSample(),
+ mWriteHeader.sample_rate, mWriteHeader.channels, mWriteHeader.bits_per_sample);
+ return ERROR_MALFORMED;
+ }
+ if (mWriteHeader.number_type != FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) {
+ ALOGE("decodeOneFrame: number type is %d, expected %d",
+ mWriteHeader.number_type, FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
+ return ERROR_MALFORMED;
+ }
+
+ size_t bufferSize = blocksize * getChannels() * sizeof(short);
+ if (bufferSize > *outBufferLen) {
+ ALOGW("decodeOneFrame: output buffer holds only partial frame %zu:%zu",
+ *outBufferLen, bufferSize);
+ blocksize = *outBufferLen / (getChannels() * sizeof(short));
+ bufferSize = blocksize * getChannels() * sizeof(short);
+ }
+
+ if (mCopy == nullptr) {
+ ALOGE("decodeOneFrame: format is not supported: channels(%d), BitsPerSample(%d)",
+ getChannels(), getBitsPerSample());
+ return ERROR_UNSUPPORTED;
+ }
+ // copy PCM from FLAC write buffer to output buffer, with interleaving
+ (*mCopy)(outBuffer, mWriteBuffer, blocksize, getChannels());
+ *outBufferLen = bufferSize;
+ return OK;
+}
+
+status_t FLACDecoder::addDataToBuffer(const uint8_t *inBuffer, size_t inBufferLen) {
+ // mBufferPos should be no larger than mBufferDataSize
+ if (inBufferLen > SIZE_MAX - (mBufferDataSize - mBufferPos)) {
+ ALOGE("addDataToBuffer: input buffer is too large");
+ return ERROR_MALFORMED;
+ }
+
+ if (inBufferLen > mBufferLen - mBufferDataSize) {
+ if (mBufferPos > 0) {
+ memmove(mBuffer, mBuffer + mBufferPos, mBufferDataSize - mBufferPos);
+ mBufferDataSize -= mBufferPos;
+ mBufferPos = 0;
+ }
+ if (inBufferLen > mBufferLen - mBufferDataSize) {
+ mBuffer = (uint8_t*)realloc(mBuffer, mBufferDataSize + inBufferLen);
+ if (mBuffer == nullptr) {
+ mBufferDataSize = 0;
+ mBufferLen = 0;
+ ALOGE("decodeOneFrame: failed to allocate memory for input buffer");
+ return NO_MEMORY;
+ }
+ mBufferLen = mBufferDataSize + inBufferLen;
+ }
+ }
+
+ memcpy(mBuffer + mBufferDataSize, inBuffer, inBufferLen);
+ mBufferDataSize += inBufferLen;
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h
new file mode 100644
index 0000000..36282a8
--- /dev/null
+++ b/media/libstagefright/flac/dec/FLACDecoder.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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 FLAC_DECODER_H_
+#define FLAC_DECODER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+#include "FLAC/stream_decoder.h"
+
+namespace android {
+
+// packet based FLAC decoder, wrapps libFLAC stream decoder.
+class FLACDecoder : public RefBase {
+
+public:
+ enum {
+ kMaxChannels = 8,
+ };
+
+ static sp<FLACDecoder> Create();
+
+ FLAC__StreamMetadata_StreamInfo getStreamInfo() const {
+ return mStreamInfo;
+ }
+
+ status_t parseMetadata(const uint8_t *inBuffer, size_t inBufferLen);
+ status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
+ short *outBuffer, size_t *outBufferLen);
+ void flush();
+
+protected:
+ FLACDecoder();
+ virtual ~FLACDecoder() override;
+
+private:
+ // stream properties
+ unsigned getMaxBlockSize() const {
+ return mStreamInfo.max_blocksize;
+ }
+ unsigned getSampleRate() const {
+ return mStreamInfo.sample_rate;
+ }
+ unsigned getChannels() const {
+ return mStreamInfo.channels;
+ }
+ unsigned getBitsPerSample() const {
+ return mStreamInfo.bits_per_sample;
+ }
+ FLAC__uint64 getTotalSamples() const {
+ return mStreamInfo.total_samples;
+ }
+
+ status_t addDataToBuffer(const uint8_t *inBuffer, size_t inBufferLen);
+
+ FLAC__StreamDecoder *mDecoder;
+
+ uint8_t *mBuffer; // cache input bit stream data
+ size_t mBufferLen; // the memory size of |mBuffer|
+ size_t mBufferPos; // next byte to read in |mBuffer|
+ // size of input data stored in |mBuffer|, always started at offset 0
+ size_t mBufferDataSize;
+
+ // cached when the STREAMINFO metadata is parsed by libFLAC
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+ bool mStreamInfoValid;
+
+ // cached when a decoded PCM block is "written" by libFLAC decoder
+ bool mWriteRequested;
+ bool mWriteCompleted;
+ FLAC__FrameHeader mWriteHeader;
+ FLAC__int32 const * mWriteBuffer[kMaxChannels];
+
+ // most recent error reported by libFLAC decoder
+ FLAC__StreamDecoderErrorStatus mErrorStatus;
+
+ void (*mCopy)(short *dst, const int *src[kMaxChannels], unsigned nSamples, unsigned nChannels);
+
+ status_t init();
+
+ // FLAC stream decoder callbacks as C++ instance methods
+ FLAC__StreamDecoderReadStatus readCallback(FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderWriteStatus writeCallback(
+ const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+ void metadataCallback(const FLAC__StreamMetadata *metadata);
+ void errorCallback(FLAC__StreamDecoderErrorStatus status);
+
+ DISALLOW_EVIL_CONSTRUCTORS(FLACDecoder);
+};
+
+} // namespace android
+
+#endif // FLAC_DECODER_H_
diff --git a/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/libstagefright/flac/dec/MODULE_LICENSE_APACHE2
diff --git a/media/libstagefright/flac/dec/NOTICE b/media/libstagefright/flac/dec/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/media/libstagefright/flac/dec/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index 7da7db9..cc89064 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -78,8 +78,7 @@
accum = (accum << 6) | value;
if (((i + 1) % 4) == 0) {
- out[j++] = (accum >> 16);
-
+ if (j < outLen) { out[j++] = (accum >> 16); }
if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
if (j < outLen) { out[j++] = accum & 0xff; }
diff --git a/media/libstagefright/include/SoftVideoEncoderOMXComponent.h b/media/libstagefright/include/SoftVideoEncoderOMXComponent.h
index b43635d..db5496a 100644
--- a/media/libstagefright/include/SoftVideoEncoderOMXComponent.h
+++ b/media/libstagefright/include/SoftVideoEncoderOMXComponent.h
@@ -21,7 +21,6 @@
#include <media/IOMX.h>
#include "SimpleSoftOMXComponent.h"
-#include <system/window.h>
struct hw_module_t;
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 22b8657..d049df5 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -15,7 +15,6 @@
*/
#ifndef A_CODEC_H_
-
#define A_CODEC_H_
#include <stdint.h>
@@ -30,6 +29,7 @@
#include <utils/NativeHandle.h>
#include <OMX_Audio.h>
#include <hardware/gralloc.h>
+#include <nativebase/nativebase.h>
#define TRACK_BUFFER_TIMING 0
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index f20c2cd..1595be4 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -53,6 +53,7 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t setStopTimeUs(int64_t stopTimeUs);
status_t dataCallback(const AudioRecord::Buffer& buffer);
virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -85,6 +86,8 @@
bool mTrackMaxAmplitude;
int64_t mStartTimeUs;
+ int64_t mStopSystemTimeUs;
+ int64_t mLastFrameTimestampUs;
int16_t mMaxAmplitude;
int64_t mPrevSampleTimeUs;
int64_t mInitialReadTimeUs;
@@ -92,6 +95,7 @@
int64_t mNumFramesSkipped;
int64_t mNumFramesLost;
int64_t mNumClientOwnedBuffers;
+ bool mNoMoreFramesToRead;
List<MediaBuffer * > mBuffersReceived;
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index aa56d27..2aaa884 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -98,6 +98,7 @@
virtual status_t stop() { return reset(); }
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t setStopTimeUs(int64_t stopTimeUs);
/**
* Check whether a CameraSource object is properly initialized.
@@ -253,6 +254,7 @@
List<int64_t> mFrameTimes;
int64_t mFirstFrameTimeUs;
+ int64_t mStopSystemTimeUs;
int32_t mNumFramesDropped;
int32_t mNumGlitches;
int64_t mGlitchDurationThresholdUs;
diff --git a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
index 6cbf85d..044699c 100644
--- a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
+++ b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
@@ -20,13 +20,14 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <system/window.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
#include <list>
+struct ANativeWindowBuffer;
+
namespace android {
class Fence;
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 1c4827f..dd357cc 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -83,6 +83,10 @@
kWhatSwitch = 'swch',
};
+ enum {
+ kMaxCttsOffsetTimeUs = 1000000LL, // 1 second
+ };
+
int mFd;
int mNextFd;
sp<MetaData> mStartMeta;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecSource.h b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
index 02ba227..3ac539e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
@@ -60,6 +60,8 @@
virtual status_t read(
MediaBuffer **buffer,
const ReadOptions *options = NULL);
+ virtual status_t setStopTimeUs(int64_t stopTimeUs);
+
// MediaBufferObserver
virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -67,11 +69,7 @@
// for AHandlerReflector
void onMessageReceived(const sp<AMessage> &msg);
- // Set GraphicBufferSource stop time. GraphicBufferSource will stop
- // after receiving a buffer with timestamp larger or equal than stopTimeUs.
- // All the buffers with timestamp larger or equal to stopTimeUs will be
- // discarded. stopTimeUs uses SYSTEM_TIME_MONOTONIC time base.
- status_t setStopStimeUs(int64_t stopTimeUs);
+
protected:
virtual ~MediaCodecSource();
@@ -86,7 +84,7 @@
kWhatStop,
kWhatPause,
kWhatSetInputBufferTimeOffset,
- kWhatSetStopTimeOffset,
+ kWhatSetStopTimeUs,
kWhatGetFirstSampleSystemTimeUs,
kWhatStopStalled,
};
diff --git a/media/libstagefright/include/media/stagefright/MediaSource.h b/media/libstagefright/include/media/stagefright/MediaSource.h
index 1bd3ed0..14adb05 100644
--- a/media/libstagefright/include/media/stagefright/MediaSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaSource.h
@@ -75,6 +75,23 @@
return ERROR_UNSUPPORTED;
}
+ // The consumer of this media source requests the source stops sending
+ // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
+ // must be in the same time base as the startTime passed in start(). If
+ // source does not support this request, ERROR_UNSUPPORTED will be returned.
+ // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
+ // called at any time even before source starts and it could be called
+ // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
+ // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
+ // source will start to drop buffer when it gets a buffer with timestamp larger
+ // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
+ // buffer's timestamp, source will drop all the incoming buffers immediately.
+ // After setting stopTimeUs, source may still stop sending buffers with timestamp
+ // less than stopTimeUs if it is stopped by the consumer.
+ virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
+ return ERROR_UNSUPPORTED;
+ }
+
protected:
virtual ~MediaSource();
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 7afd22d..9676b97 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -64,6 +64,7 @@
kKeyOpusHeader = 'ohdr', // raw data
kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns)
kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns)
+ kKeyFlacMetadata = 'flMd', // raw data
kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information)
kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync', // int32_t (bool)
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index e6aee6a..a000fde 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -17,8 +17,6 @@
#ifndef SIMPLE_DECODING_SOURCE_H_
#define SIMPLE_DECODING_SOURCE_H_
-#include <system/window.h>
-
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/Mutexed.h>
@@ -26,6 +24,8 @@
#include <utils/Condition.h>
#include <utils/StrongPointer.h>
+struct ANativeWindow;
+
namespace android {
struct ALooper;
@@ -45,10 +45,13 @@
// does not support secure input or pausing.
// if |desiredCodec| is given, use this specific codec.
static sp<SimpleDecodingSource> Create(
- const sp<IMediaSource> &source, uint32_t flags = 0,
- const sp<ANativeWindow> &nativeWindow = NULL,
+ const sp<IMediaSource> &source, uint32_t flags,
+ const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec = NULL);
+ static sp<SimpleDecodingSource> Create(
+ const sp<IMediaSource> &source, uint32_t flags = 0);
+
virtual ~SimpleDecodingSource();
// starts this source (and it's underlying source). |params| is ignored.
diff --git a/media/libstagefright/matroska/Android.bp b/media/libstagefright/matroska/Android.bp
index a5891c3..ec2fb4b 100644
--- a/media/libstagefright/matroska/Android.bp
+++ b/media/libstagefright/matroska/Android.bp
@@ -4,8 +4,10 @@
srcs: ["MatroskaExtractor.cpp"],
include_dirs: [
+ "external/flac/include",
"external/libvpx/libwebm",
"frameworks/native/include/media/openmax",
+ "frameworks/av/media/libstagefright/flac/dec",
"frameworks/av/media/libstagefright/include",
],
@@ -26,5 +28,8 @@
},
},
- shared_libs: ["libmedia"],
+ shared_libs: [
+ "libmedia",
+ "libstagefright_flacdec"
+ ],
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 6135b59..462eff6 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "MatroskaExtractor"
#include <utils/Log.h>
+#include "FLACDecoder.h"
#include "MatroskaExtractor.h"
#include "avc_utils.h"
@@ -1065,6 +1066,37 @@
return OK;
}
+static status_t addFlacMetadata(
+ const sp<MetaData> &meta,
+ const void *codecPrivate, size_t codecPrivateSize) {
+ // hexdump(codecPrivate, codecPrivateSize);
+
+ meta->setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
+
+ int32_t maxInputSize = 64 << 10;
+ sp<FLACDecoder> flacDecoder = FLACDecoder::Create();
+ if (flacDecoder != NULL
+ && flacDecoder->parseMetadata((const uint8_t*)codecPrivate, codecPrivateSize) == OK) {
+ FLAC__StreamMetadata_StreamInfo streamInfo = flacDecoder->getStreamInfo();
+ maxInputSize = streamInfo.max_framesize;
+ if (maxInputSize == 0) {
+ // In case max framesize is not available, use raw data size as max framesize,
+ // assuming there is no expansion.
+ if (streamInfo.max_blocksize != 0
+ && streamInfo.channels != 0
+ && ((streamInfo.bits_per_sample + 7) / 8) >
+ INT32_MAX / streamInfo.max_blocksize / streamInfo.channels) {
+ return ERROR_MALFORMED;
+ }
+ maxInputSize = ((streamInfo.bits_per_sample + 7) / 8)
+ * streamInfo.max_blocksize * streamInfo.channels;
+ }
+ }
+ meta->setInt32(kKeyMaxInputSize, maxInputSize);
+
+ return OK;
+}
+
status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) {
BlockIterator iter(this, trackInfo->mTrackNum, index);
if (iter.eos()) {
@@ -1385,6 +1417,9 @@
mSeekPreRollNs = track->GetSeekPreRoll();
} else if (!strcmp("A_MPEG/L3", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ } else if (!strcmp("A_FLAC", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
+ err = addFlacMetadata(meta, codecPrivate, codecPrivateSize);
} else {
ALOGW("%s is not supported.", codecID);
continue;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f1b44ae..1cf9744 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -892,6 +892,11 @@
bits.skipBits(2);
unsigned aac_frame_length = bits.getBits(13);
+ if (aac_frame_length == 0){
+ ALOGE("b/62673179, Invalid AAC frame length!");
+ android_errorWriteLog(0x534e4554, "62673179");
+ return NULL;
+ }
bits.skipBits(11); // adts_buffer_fullness
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index 64b2c08..789379a 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -90,46 +90,49 @@
using ::android::IOMXNode;
using ::android::IOMXObserver;
- Mutex::Autolock autoLock(mLock);
- if (mLiveNodes.size() == kMaxNodeInstances) {
- _hidl_cb(toStatus(NO_MEMORY), nullptr);
- return Void();
- }
-
- sp<OMXNodeInstance> instance = new OMXNodeInstance(
- this, new LWOmxObserver(observer), name.c_str());
-
- OMX_COMPONENTTYPE *handle;
- OMX_ERRORTYPE err = mMaster->makeComponentInstance(
- name.c_str(), &OMXNodeInstance::kCallbacks,
- instance.get(), &handle);
-
- if (err != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to allocate omx component "
- "'" << name.c_str() << "' "
- " err=" << asString(err) <<
- "(0x" << std::hex << unsigned(err) << ")";
- _hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);
- return Void();
- }
- instance->setHandle(handle);
- std::vector<AString> quirkVector;
- if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
- uint32_t quirks = 0;
- for (const AString quirk : quirkVector) {
- if (quirk == "requires-allocate-on-input-ports") {
- quirks |= kRequiresAllocateBufferOnInputPorts;
- }
- if (quirk == "requires-allocate-on-output-ports") {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- }
+ sp<OMXNodeInstance> instance;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mLiveNodes.size() == kMaxNodeInstances) {
+ _hidl_cb(toStatus(NO_MEMORY), nullptr);
+ return Void();
}
- instance->setQuirks(quirks);
- }
- mLiveNodes.add(observer.get(), instance);
+ instance = new OMXNodeInstance(
+ this, new LWOmxObserver(observer), name.c_str());
+
+ OMX_COMPONENTTYPE *handle;
+ OMX_ERRORTYPE err = mMaster->makeComponentInstance(
+ name.c_str(), &OMXNodeInstance::kCallbacks,
+ instance.get(), &handle);
+
+ if (err != OMX_ErrorNone) {
+ LOG(ERROR) << "Failed to allocate omx component "
+ "'" << name.c_str() << "' "
+ " err=" << asString(err) <<
+ "(0x" << std::hex << unsigned(err) << ")";
+ _hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);
+ return Void();
+ }
+ instance->setHandle(handle);
+ std::vector<AString> quirkVector;
+ if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
+ uint32_t quirks = 0;
+ for (const AString quirk : quirkVector) {
+ if (quirk == "requires-allocate-on-input-ports") {
+ quirks |= kRequiresAllocateBufferOnInputPorts;
+ }
+ if (quirk == "requires-allocate-on-output-ports") {
+ quirks |= kRequiresAllocateBufferOnOutputPorts;
+ }
+ }
+ instance->setQuirks(quirks);
+ }
+
+ mLiveNodes.add(observer.get(), instance);
+ mNode2Observer.add(instance.get(), observer.get());
+ }
observer->linkToDeath(this, 0);
- mNode2Observer.add(instance.get(), observer.get());
_hidl_cb(toStatus(OK), new TWOmxNode(instance));
return Void();
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index 650db8e..acda060 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -21,6 +21,7 @@
#include "WGraphicBufferProducer.h"
#include "WProducerListener.h"
#include "Conversion.h"
+#include <system/window.h>
namespace android {
namespace hardware {
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index e876306..d8540f8 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -206,8 +206,10 @@
Return<void> TWGraphicBufferSource::getStopTimeOffsetUs(
getStopTimeOffsetUs_cb _hidl_cb) {
- // TODO: Implement this when needed.
- _hidl_cb(Status::OK, 0);
+ status_t status;
+ int64_t stopTimeOffsetUs;
+ status = mBase->getStopTimeOffsetUs(&stopTimeOffsetUs);
+ _hidl_cb(toStatus(status), stopTimeOffsetUs);
return Void();
}
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index 2e2c461..79f6d93 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -160,6 +160,11 @@
return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
}
+::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs(
+ int64_t *stopTimeOffsetUs) {
+ return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
+}
+
::android::binder::Status BWGraphicBufferSource::setColorAspects(
int32_t aspects) {
return Status::fromStatusT(mBase->setColorAspects(aspects));
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/BWGraphicBufferSource.h
index 6f69d39..0f78eb6 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/BWGraphicBufferSource.h
@@ -53,6 +53,7 @@
double fps, double captureFps) override;
Status setStartTimeUs(int64_t startTimeUs) override;
Status setStopTimeUs(int64_t stopTimeUs) override;
+ Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override;
Status setColorAspects(int32_t aspects) override;
Status setTimeOffsetUs(int64_t timeOffsetsUs) override;
Status signalEndOfInputStream() override;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 49e7e33..d85ef89 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -264,6 +264,7 @@
mLastDataspace(HAL_DATASPACE_UNKNOWN),
mExecuting(false),
mSuspended(false),
+ mLastFrameTimestampUs(-1),
mStopTimeUs(-1),
mLastActionTimeUs(-1ll),
mSkipFramesBeforeNs(-1ll),
@@ -649,6 +650,7 @@
}
ALOGV("buffer submitted [slot=%d, useCount=%ld] acquired=%d",
item.mBuffer->getSlot(), item.mBuffer.use_count(), mNumOutstandingAcquires);
+ mLastFrameTimestampUs = itemTimeUs;
}
return true;
@@ -1232,10 +1234,21 @@
return OK;
}
+status_t GraphicBufferSource::getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) {
+ ALOGV("getStopTimeOffsetUs");
+ Mutex::Autolock autoLock(mMutex);
+ if (mStopTimeUs == -1) {
+ ALOGW("Fail to return stopTimeOffsetUs as stop time is not set");
+ return INVALID_OPERATION;
+ }
+ *stopTimeOffsetUs =
+ mLastFrameTimestampUs == -1 ? 0 : mStopTimeUs - mLastFrameTimestampUs;
+ return OK;
+}
+
status_t GraphicBufferSource::setTimeLapseConfig(double fps, double captureFps) {
ALOGV("setTimeLapseConfig: fps=%lg, captureFps=%lg",
fps, captureFps);
-
Mutex::Autolock autoLock(mMutex);
if (mExecuting || !(fps > 0) || !(captureFps > 0)) {
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 96be0ca..009691d 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -172,6 +172,13 @@
// and not submitted to encoder. timeUs uses SYSTEM_TIME_MONOTONIC time base.
status_t setStopTimeUs(int64_t stopTimeUs);
+ // Gets the stop time offset in us. This is the time offset between latest buffer
+ // time and the stopTimeUs. If stop time is not set, INVALID_OPERATION will be returned.
+ // If return is OK, *stopTimeOffsetUs will contain the valid offset. Otherwise,
+ // *stopTimeOffsetUs will not be modified. Positive stopTimeOffsetUs means buffer time
+ // larger than stopTimeUs.
+ status_t getStopTimeOffsetUs(int64_t *stopTimeOffsetUs);
+
// Sets the desired color aspects, e.g. to be used when producer does not specify a dataspace.
status_t setColorAspects(int32_t aspectsPacked);
@@ -340,6 +347,8 @@
// regardless of the metadata of those buffers
bool areWeDiscardingAvailableBuffers_l();
+ int64_t mLastFrameTimestampUs;
+
// Our BufferQueue interfaces. mProducer is passed to the producer through
// getIGraphicBufferProducer, and mConsumer is used internally to retrieve
// the buffers queued by the producer.
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index db99ef2..22d7d27 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1043,6 +1043,10 @@
case OMXBuffer::kBufferTypeHidlMemory: {
sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
+ if (hidlMemory == nullptr) {
+ ALOGE("OMXNodeInstance useBuffer() failed to map memory");
+ return NO_MEMORY;
+ }
return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
}
default:
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index ee6d1d5..a66d565 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -24,6 +24,7 @@
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/MediaErrors.h>
#include <media/MediaDefs.h>
+#include <system/graphics-base.h>
#include "OMXUtils.h"
namespace android {
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index a0b940d..5bae7e8 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -214,6 +214,13 @@
Mutex::Autolock autoLock(mLock);
CHECK_LT(portIndex, mPorts.size());
+ PortInfo *port = &mPorts.editItemAt(portIndex);
+ if (size < port->mDef.nBufferSize) {
+ ALOGE("b/63522430, Buffer size is too small.");
+ android_errorWriteLog(0x534e4554, "63522430");
+ return OMX_ErrorBadParameter;
+ }
+
*header = new OMX_BUFFERHEADERTYPE;
(*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE);
(*header)->nVersion.s.nVersionMajor = 1;
@@ -236,8 +243,6 @@
(*header)->nOutputPortIndex = portIndex;
(*header)->nInputPortIndex = portIndex;
- PortInfo *port = &mPorts.editItemAt(portIndex);
-
CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE);
CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual);
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index a773ca2..fccb12b 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -58,6 +58,7 @@
{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
{ "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },
{ "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
+ { "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },
{ "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
{ "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
};
diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
index 2aa88af..7ecfbbb 100644
--- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
@@ -36,6 +36,8 @@
#include <hardware/gralloc.h>
+#include <nativebase/nativebase.h>
+
#include <OMX_IndexExt.h>
namespace android {
diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp
index 46428e3..8bcb99e 100644
--- a/media/libstagefright/omx/tests/Android.bp
+++ b/media/libstagefright/omx/tests/Android.bp
@@ -14,6 +14,7 @@
"libcutils",
"libhidlbase",
"libhidlmemory",
+ "libnativewindow",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"android.hardware.media.omx@1.0",
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index fcc44d8..3266439 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -41,6 +41,7 @@
#include <media/OMXBuffer.h>
#include <android/hardware/media/omx/1.0/IOmx.h>
#include <media/omx/1.0/WOmx.h>
+#include <system/window.h>
#define DEFAULT_TIMEOUT 500000
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 5505aa4..c6c0245 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -33,6 +33,7 @@
#include "ASessionDescription.h"
#include <ctype.h>
+#include <cutils/properties.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -135,7 +136,7 @@
mCheckPending(false),
mCheckGeneration(0),
mCheckTimeoutGeneration(0),
- mTryTCPInterleaving(false),
+ mTryTCPInterleaving(property_get_bool("rtp.transport.TCP", false)),
mTryFakeRTCP(false),
mReceivedFirstRTCPPacket(false),
mReceivedFirstRTPPacket(false),
diff --git a/media/libstagefright/wifi-display/Android.bp b/media/libstagefright/wifi-display/Android.bp
index be23359..fb08c5b 100644
--- a/media/libstagefright/wifi-display/Android.bp
+++ b/media/libstagefright/wifi-display/Android.bp
@@ -29,6 +29,7 @@
"libstagefright",
"libstagefright_foundation",
"libui",
+ "libgui",
"libutils",
],
diff --git a/media/mtp/tests/Android.bp b/media/mtp/tests/Android.bp
index a0480b6..0750208 100644
--- a/media/mtp/tests/Android.bp
+++ b/media/mtp/tests/Android.bp
@@ -16,6 +16,7 @@
cc_test {
name: "mtp_ffs_handle_test",
+ test_suites: ["device-tests"],
srcs: ["MtpFfsHandle_test.cpp"],
shared_libs: [
"libbase",
diff --git a/media/mtp/tests/AndroidTest.xml b/media/mtp/tests/AndroidTest.xml
new file mode 100644
index 0000000..c1f4753
--- /dev/null
+++ b/media/mtp/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for mtp_ffs_handle_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="mtp_ffs_handle_test->/data/local/tmp/mtp_ffs_handle_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="mtp_ffs_handle_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2c33fc2..0df9a39 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -260,10 +260,11 @@
status_t MmapStreamInterface::openMmapStream(MmapStreamInterface::stream_direction_t direction,
const audio_attributes_t *attr,
audio_config_base_t *config,
- const MmapStreamInterface::Client& client,
+ const AudioClient& client,
audio_port_handle_t *deviceId,
const sp<MmapStreamCallback>& callback,
- sp<MmapStreamInterface>& interface)
+ sp<MmapStreamInterface>& interface,
+ audio_port_handle_t *handle)
{
sp<AudioFlinger> af;
{
@@ -273,7 +274,7 @@
status_t ret = NO_INIT;
if (af != 0) {
ret = af->openMmapStream(
- direction, attr, config, client, deviceId, callback, interface);
+ direction, attr, config, client, deviceId, callback, interface, handle);
}
return ret;
}
@@ -281,10 +282,11 @@
status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t direction,
const audio_attributes_t *attr,
audio_config_base_t *config,
- const MmapStreamInterface::Client& client,
+ const AudioClient& client,
audio_port_handle_t *deviceId,
const sp<MmapStreamCallback>& callback,
- sp<MmapStreamInterface>& interface)
+ sp<MmapStreamInterface>& interface,
+ audio_port_handle_t *handle)
{
status_t ret = initCheck();
if (ret != NO_ERROR) {
@@ -293,7 +295,7 @@
audio_session_t sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
- audio_io_handle_t io;
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER;
@@ -306,14 +308,14 @@
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
- *deviceId, &portId);
+ deviceId, &portId);
} else {
ret = AudioSystem::getInputForAttr(attr, &io,
sessionId,
client.clientPid,
client.clientUid,
config,
- AUDIO_INPUT_FLAG_MMAP_NOIRQ, *deviceId, &portId);
+ AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
}
if (ret != NO_ERROR) {
return ret;
@@ -325,6 +327,7 @@
if (thread != 0) {
interface = new MmapThreadHandle(thread);
thread->configure(attr, streamType, sessionId, callback, portId);
+ *handle = portId;
} else {
ret = NO_INIT;
}
@@ -339,7 +342,6 @@
AUDIO_HARDWARE_MODULE_ID_A2DP,
AUDIO_HARDWARE_MODULE_ID_USB,
};
-#define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0])))
AudioHwDevice* AudioFlinger::findSuitableHwDev_l(
audio_module_handle_t module,
@@ -349,7 +351,7 @@
// well known modules
if (module == 0) {
ALOGW("findSuitableHwDev_l() loading well know audio hw modules");
- for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
+ for (size_t i = 0; i < arraysize(audio_interfaces); i++) {
loadHwModule_l(audio_interfaces[i]);
}
// then try to find a module supporting the requested device.
@@ -519,7 +521,7 @@
#ifdef TEE_SINK
// dump the serially shared record tee sink
if (mRecordTeeSource != 0) {
- dumpTee(fd, mRecordTeeSource);
+ dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C');
}
#endif
@@ -1191,25 +1193,10 @@
String8 value;
if (param.get(String8(AudioParameter::keyBtNrec), value) == NO_ERROR) {
bool btNrecIsOff = (value == AudioParameter::valueOff);
- if (mBtNrecIsOff != btNrecIsOff) {
+ if (mBtNrecIsOff.exchange(btNrecIsOff) != btNrecIsOff) {
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- sp<RecordThread> thread = mRecordThreads.valueAt(i);
- audio_devices_t device = thread->inDevice();
- bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
- // collect all of the thread's session IDs
- KeyedVector<audio_session_t, bool> ids = thread->sessionIds();
- // suspend effects associated with those session IDs
- for (size_t j = 0; j < ids.size(); ++j) {
- audio_session_t sessionId = ids.keyAt(j);
- thread->setEffectSuspended(FX_IID_AEC,
- suspend,
- sessionId);
- thread->setEffectSuspended(FX_IID_NS,
- suspend,
- sessionId);
- }
+ mRecordThreads.valueAt(i)->checkBtNrec();
}
- mBtNrecIsOff = btNrecIsOff;
}
}
String8 screenState;
@@ -1280,7 +1267,7 @@
if (thread == NULL) {
thread = (ThreadBase *)checkMmapThread_l(ioHandle);
if (thread == NULL) {
- String8("");
+ return String8("");
}
}
}
@@ -3212,6 +3199,11 @@
status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
{
+ // clear possible suspended state before parking the chain so that it starts in default state
+ // when attached to a new record thread
+ chain->setEffectSuspended_l(FX_IID_AEC, false);
+ chain->setEffectSuspended_l(FX_IID_NS, false);
+
audio_session_t session = chain->sessionId();
ssize_t index = mOrphanEffectChains.indexOfKey(session);
ALOGV("putOrphanEffectChain_l session %d index %zd", session, index);
@@ -3264,7 +3256,7 @@
}
#ifdef TEE_SINK
-void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id)
+void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix)
{
NBAIO_Source *teeSource = source.get();
if (teeSource != NULL) {
@@ -3326,7 +3318,8 @@
struct tm tm;
localtime_r(&tv.tv_sec, &tm);
strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
- snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id);
+ snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id,
+ suffix);
// if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
if (teeFd >= 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 75b4e4c..9023b2d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -25,6 +25,8 @@
#include <sys/types.h>
#include <limits.h>
+#include <android-base/macros.h>
+
#include <cutils/compiler.h>
#include <cutils/properties.h>
@@ -71,12 +73,11 @@
#include <powermanager/IPowerManager.h>
#include <media/nbaio/NBLog.h>
+#include <private/media/AudioEffectShared.h>
#include <private/media/AudioTrackShared.h>
namespace android {
-struct audio_track_cblk_t;
-struct effect_param_cblk_t;
class AudioMixer;
class AudioBuffer;
class AudioResampler;
@@ -293,12 +294,14 @@
status_t openMmapStream(MmapStreamInterface::stream_direction_t direction,
const audio_attributes_t *attr,
audio_config_base_t *config,
- const MmapStreamInterface::Client& client,
+ const AudioClient& client,
audio_port_handle_t *deviceId,
const sp<MmapStreamCallback>& callback,
- sp<MmapStreamInterface>& interface);
+ sp<MmapStreamInterface>& interface,
+ audio_port_handle_t *handle);
private:
- static const size_t kLogMemorySize = 40 * 1024;
+ // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
+ static const size_t kLogMemorySize = 400 * 1024;
sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled
// When a log writer is unregistered, it is done lazily so that media.log can continue to see it
// for as long as possible. The memory is only freed when it is needed for another log writer.
@@ -347,12 +350,13 @@
sync_event_callback_t callBack,
const wp<RefBase>& cookie);
+ bool btNrecIsOff() const { return mBtNrecIsOff.load(); }
+
+
private:
audio_mode_t getMode() const { return mMode; }
- bool btNrecIsOff() const { return mBtNrecIsOff; }
-
AudioFlinger() ANDROID_API;
virtual ~AudioFlinger();
@@ -445,8 +449,8 @@
sp<AudioFlinger> audioFlinger() const { return mAudioFlinger; }
private:
- Client(const Client&);
- Client& operator = (const Client&);
+ DISALLOW_COPY_AND_ASSIGN(Client);
+
const sp<AudioFlinger> mAudioFlinger;
sp<MemoryDealer> mMemoryDealer;
const pid_t mPid;
@@ -466,8 +470,7 @@
virtual void binderDied(const wp<IBinder>& who);
private:
- NotificationClient(const NotificationClient&);
- NotificationClient& operator = (const NotificationClient&);
+ DISALLOW_COPY_AND_ASSIGN(NotificationClient);
const sp<AudioFlinger> mAudioFlinger;
const pid_t mPid;
@@ -595,7 +598,7 @@
virtual status_t createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info);
virtual status_t getMmapPosition(struct audio_mmap_position *position);
- virtual status_t start(const MmapStreamInterface::Client& client,
+ virtual status_t start(const AudioClient& client,
audio_port_handle_t *handle);
virtual status_t stop(audio_port_handle_t handle);
virtual status_t standby();
@@ -779,7 +782,7 @@
volatile atomic_uint_fast32_t mNextUniqueIds[AUDIO_UNIQUE_ID_USE_MAX];
audio_mode_t mMode;
- bool mBtNrecIsOff;
+ std::atomic_bool mBtNrecIsOff;
// protected by mLock
Vector<AudioSessionRef*> mAudioSessionRefs;
@@ -824,7 +827,7 @@
#ifdef TEE_SINK
// tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
- static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0);
+ static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix);
// whether tee sink is enabled by property
static bool mTeeSinkInputEnabled;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b4029c7..f2c1c4f 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -21,11 +21,12 @@
#include "Configuration.h"
#include <utils/Log.h>
+#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_ns.h>
+#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
-#include <private/media/AudioEffectShared.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
-#include <system/audio_effects/effect_visualizer.h>
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
@@ -1334,6 +1335,24 @@
ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
cmdCode, mHasControl, mEffect.unsafe_get());
+ // reject commands reserved for internal use by audio framework if coming from outside
+ // of audioserver
+ switch(cmdCode) {
+ case EFFECT_CMD_ENABLE:
+ case EFFECT_CMD_DISABLE:
+ case EFFECT_CMD_SET_PARAM:
+ case EFFECT_CMD_SET_PARAM_DEFERRED:
+ case EFFECT_CMD_SET_PARAM_COMMIT:
+ case EFFECT_CMD_GET_PARAM:
+ break;
+ default:
+ if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY) {
+ break;
+ }
+ android_errorWriteLog(0x534e4554, "62019992");
+ return BAD_VALUE;
+ }
+
if (cmdCode == EFFECT_CMD_ENABLE) {
if (*replySize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
@@ -1792,6 +1811,7 @@
idx_insert);
}
effect->configure();
+
return NO_ERROR;
}
@@ -2013,6 +2033,7 @@
mSuspendedEffects.add(type->timeLow, desc);
ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
}
+
if (desc->mRefCount++ == 0) {
sp<EffectModule> effect = getEffectIfEnabled(type);
if (effect != 0) {
@@ -2028,7 +2049,8 @@
desc = mSuspendedEffects.valueAt(index);
if (desc->mRefCount <= 0) {
ALOGW("setEffectSuspended_l() restore refcount should not be 0 %d", desc->mRefCount);
- desc->mRefCount = 1;
+ desc->mRefCount = 0;
+ return;
}
if (--desc->mRefCount == 0) {
ALOGV("setEffectSuspended_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
@@ -2106,6 +2128,17 @@
const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
#endif //OPENSL_ES_H_
+/* static */
+bool AudioFlinger::EffectChain::isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type)
+{
+ // Only NS and AEC are suspended when BtNRec is off
+ if ((memcmp(type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) ||
+ (memcmp(type, FX_IID_NS, sizeof(effect_uuid_t)) == 0)) {
+ return true;
+ }
+ return false;
+}
+
bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
{
// auxiliary effects and visualizer are never suspended on output mix
@@ -2160,7 +2193,7 @@
ALOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
effect->desc().type.timeLow);
sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
- // if effect is requested to suspended but was not yet enabled, supend it now.
+ // if effect is requested to suspended but was not yet enabled, suspend it now.
if (desc->mEffect == 0) {
desc->mEffect = effect;
effect->setEnabled(false);
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 0755c52..e29798b 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -135,15 +135,14 @@
void dump(int fd, const Vector<String16>& args);
-protected:
+private:
friend class AudioFlinger; // for mHandles
bool mPinned;
// Maximum time allocated to effect engines to complete the turn off sequence
static const uint32_t MAX_DISABLE_TIME_MS = 10000;
- EffectModule(const EffectModule&);
- EffectModule& operator = (const EffectModule&);
+ DISALLOW_COPY_AND_ASSIGN(EffectModule);
status_t start_l();
status_t stop_l();
@@ -232,10 +231,9 @@
void dumpToBuffer(char* buffer, size_t size);
-protected:
+private:
friend class AudioFlinger; // for mEffect, mHasControl, mEnabled
- EffectHandle(const EffectHandle&);
- EffectHandle& operator =(const EffectHandle&);
+ DISALLOW_COPY_AND_ASSIGN(EffectHandle);
Mutex mLock; // protects IEffect method calls
wp<EffectModule> mEffect; // pointer to controlled EffectModule
@@ -333,7 +331,8 @@
void setStrategy(uint32_t strategy)
{ mStrategy = strategy; }
- // suspend effect of the given type
+ // suspend or restore effects of the specified type. The number of suspend requests is counted
+ // and restore occurs once all suspend requests are cancelled.
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend);
// suspend all eligible effects
@@ -366,16 +365,15 @@
void dump(int fd, const Vector<String16>& args);
-protected:
+private:
friend class AudioFlinger; // for mThread, mEffects
- EffectChain(const EffectChain&);
- EffectChain& operator =(const EffectChain&);
+ DISALLOW_COPY_AND_ASSIGN(EffectChain);
class SuspendedEffectDesc : public RefBase {
public:
SuspendedEffectDesc() : mRefCount(0) {}
- int mRefCount;
+ int mRefCount; // > 0 when suspended
effect_uuid_t mType;
wp<EffectModule> mEffect;
};
@@ -391,6 +389,8 @@
// types or implementations from the suspend/restore mechanism.
bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
+ static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
+
void clearInputBuffer_l(const sp<ThreadBase>& thread);
void setThread(const sp<ThreadBase>& thread);
@@ -417,6 +417,6 @@
// mSuspendedEffects lists all effects currently suspended in the chain.
// Use effect type UUID timelow field as key. There is no real risk of identical
// timeLow fields among effect type UUIDs.
- // Updated by updateSuspendedSessions_l() only.
+ // Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
};
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 873a9ad..d063772 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -57,7 +57,7 @@
return mSQ.poll();
}
-void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+void FastCapture::setNBLogWriter(NBLog::Writer *logWriter __unused)
{
}
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
index e258a4d..c3817c0 100644
--- a/services/audioflinger/FastCapture.h
+++ b/services/audioflinger/FastCapture.h
@@ -39,7 +39,7 @@
// callouts
virtual const FastThreadState *poll();
- virtual void setLog(NBLog::Writer *logWriter);
+ virtual void setNBLogWriter(NBLog::Writer *logWriter);
virtual void onIdle();
virtual void onExit();
virtual bool isSubClassCommand(FastThreadState::Command command);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 103e7f8..c4f1af3 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -41,6 +41,7 @@
#include <audio_utils/format.h>
#include <media/AudioMixer.h>
#include "FastMixer.h"
+#include "TypedLogger.h"
namespace android {
@@ -101,10 +102,12 @@
return mSQ.poll();
}
-void FastMixer::setLog(NBLog::Writer *logWriter)
+void FastMixer::setNBLogWriter(NBLog::Writer *logWriter)
{
+ // FIXME If mMixer is set or changed prior to this, we don't inform correctly.
+ // Should cache logWriter and re-apply it at the assignment to mMixer.
if (mMixer != NULL) {
- mMixer->setLog(logWriter);
+ mMixer->setNBLogWriter(logWriter);
}
}
@@ -135,6 +138,7 @@
void FastMixer::onStateChange()
{
+ LOG_HIST_FLUSH();
const FastMixerState * const current = (const FastMixerState *) mCurrent;
const FastMixerState * const previous = (const FastMixerState *) mPrevious;
FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
@@ -188,6 +192,7 @@
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
+ // FIXME See the other FIXME at FastMixer::setNBLogWriter()
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
mMixerBufferSize = mixerFrameSize * frameCount;
@@ -330,6 +335,7 @@
void FastMixer::onWork()
{
+ LOG_HIST_TS();
const FastMixerState * const current = (const FastMixerState *) mCurrent;
FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
const FastMixerState::Command command = mCommand;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index bdfd8a0..930fa8d 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -46,7 +46,7 @@
// callouts
virtual const FastThreadState *poll();
- virtual void setLog(NBLog::Writer *logWriter);
+ virtual void setNBLogWriter(NBLog::Writer *logWriter);
virtual void onIdle();
virtual void onExit();
virtual bool isSubClassCommand(FastThreadState::Command command);
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index cf9fce3..85865b7 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -27,6 +27,7 @@
#include <utils/Trace.h>
#include "FastThread.h"
#include "FastThreadDumpState.h"
+#include "TypedLogger.h"
#define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep
#define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling
@@ -64,8 +65,8 @@
/* mMeasuredWarmupTs({0, 0}), */
mWarmupCycles(0),
mWarmupConsecutiveInRangeCycles(0),
- // mDummyLogWriter
- mLogWriter(&mDummyLogWriter),
+ // mDummyNBLogWriter
+ mNBLogWriter(&mDummyNBLogWriter),
mTimestampStatus(INVALID_OPERATION),
mCommand(FastThreadState::INITIAL),
@@ -90,6 +91,9 @@
bool FastThread::threadLoop()
{
+ // LOGT now works even if tlNBLogWriter is nullptr, but we're considering changing that,
+ // so this initialization permits a future change to remove the check for nullptr.
+ tlNBLogWriter = &mDummyNBLogWriter;
for (;;) {
// either nanosleep, sched_yield, or busy wait
@@ -119,8 +123,9 @@
// As soon as possible of learning of a new dump area, start using it
mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
- mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter;
- setLog(mLogWriter);
+ mNBLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyNBLogWriter;
+ setNBLogWriter(mNBLogWriter); // FastMixer informs its AudioMixer, FastCapture ignores
+ tlNBLogWriter = mNBLogWriter;
// We want to always have a valid reference to the previous (non-idle) state.
// However, the state queue only guarantees access to current and previous states.
@@ -218,7 +223,6 @@
struct timespec newTs;
int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
if (rc == 0) {
- //mLogWriter->logTimestamp(newTs);
if (mOldTsValid) {
time_t sec = newTs.tv_sec - mOldTs.tv_sec;
long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h
index 816b666..2a71414 100644
--- a/services/audioflinger/FastThread.h
+++ b/services/audioflinger/FastThread.h
@@ -41,7 +41,7 @@
// callouts to subclass in same lexical order as they were in original FastMixer.cpp
// FIXME need comments
virtual const FastThreadState *poll() = 0;
- virtual void setLog(NBLog::Writer *logWriter __unused) { }
+ virtual void setNBLogWriter(NBLog::Writer *logWriter __unused) { }
virtual void onIdle() = 0;
virtual void onExit() = 0;
virtual bool isSubClassCommand(FastThreadState::Command command) = 0;
@@ -81,8 +81,8 @@
struct timespec mMeasuredWarmupTs; // how long did it take for warmup to complete
uint32_t mWarmupCycles; // counter of number of loop cycles during warmup phase
uint32_t mWarmupConsecutiveInRangeCycles; // number of consecutive cycles in range
- NBLog::Writer mDummyLogWriter;
- NBLog::Writer* mLogWriter;
+ NBLog::Writer mDummyNBLogWriter;
+ NBLog::Writer* mNBLogWriter; // always non-nullptr: real NBLog::Writer* or &mDummyNBLogWriter
status_t mTimestampStatus;
FastThreadState::Command mCommand;
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index e4fe8ac..2a27dfd 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -41,11 +41,10 @@
static void appendDumpHeader(String8& result);
void dump(char* buffer, size_t size);
-protected:
+private:
friend class MmapThread;
- MmapTrack(const MmapTrack&);
- MmapTrack& operator = (const MmapTrack&);
+ DISALLOW_COPY_AND_ASSIGN(MmapTrack);
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index f84ba08..3f1a0c0 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -94,8 +94,7 @@
friend class DirectOutputThread;
friend class OffloadThread;
- Track(const Track&);
- Track& operator = (const Track&);
+ DISALLOW_COPY_AND_ASSIGN(Track);
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 72ebc93..3f83ca8 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -65,8 +65,7 @@
private:
friend class AudioFlinger; // for mState
- RecordTrack(const RecordTrack&);
- RecordTrack& operator = (const RecordTrack&);
+ DISALLOW_COPY_AND_ASSIGN(RecordTrack);
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index a6857fe..65eccb6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -100,10 +100,6 @@
return a < b ? a : b;
}
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
namespace android {
// retry counts for buffer fill timeout
@@ -995,13 +991,6 @@
ALOGW("power manager service died !!!");
}
-void AudioFlinger::ThreadBase::setEffectSuspended(
- const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
-{
- Mutex::Autolock _l(mLock);
- setEffectSuspended_l(type, suspend, sessionId);
-}
-
void AudioFlinger::ThreadBase::setEffectSuspended_l(
const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
{
@@ -1180,6 +1169,11 @@
return BAD_VALUE;
}
+ // always allow effects without processing load or latency
+ if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
+ return NO_ERROR;
+ }
+
switch (mType) {
case MIXER: {
// Reject any effect on mixer multichannel sinks.
@@ -1210,10 +1204,6 @@
}
}
- // always allow effects without processing load or latency
- if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
- break;
- }
if (flags & AUDIO_OUTPUT_FLAG_RAW) {
ALOGW("checkEffectCompatibility_l(): effect %s on playback thread in raw mode",
desc->name);
@@ -1455,6 +1445,7 @@
effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
+
return NO_ERROR;
}
@@ -2045,7 +2036,7 @@
pid_t callingPid = IPCThreadState::self()->getCallingPid();
// we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
// so ask activity manager to do this on our behalf
- sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp, true /*isForApp*/);
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp, true /*forApp*/);
}
}
@@ -2182,7 +2173,7 @@
}
char buffer[256];
- track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ track->dump(buffer, arraysize(buffer), false /* active */);
mLocalLog.log("addTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
status = NO_ERROR;
@@ -2212,7 +2203,7 @@
track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
char buffer[256];
- track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ track->dump(buffer, arraysize(buffer), false /* active */);
mLocalLog.log("removeTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
mTracks.remove(track);
@@ -2930,7 +2921,7 @@
bool AudioFlinger::PlaybackThread::threadLoop()
{
- logWriterTLS = mNBLogWriter.get();
+ tlNBLogWriter = mNBLogWriter.get();
Vector< sp<Track> > tracksToRemove;
@@ -2957,9 +2948,13 @@
acquireWakeLock();
- // mNBLogWriter->log can only be called while thread mutex mLock is held.
+ // mNBLogWriter logging APIs can only be called by a single thread, typically the
+ // thread associated with this PlaybackThread.
+ // If you want to share the mNBLogWriter with other threads (for example, binder threads)
+ // then all such threads must agree to hold a common mutex before logging.
// So if you need to log when mutex is unlocked, set logString to a non-NULL string,
// and then that string will be logged at the next convenient opportunity.
+ // See reference to logString below.
const char *logString = NULL;
// Estimated time for next buffer to be written to hal. This is used only on
@@ -2967,9 +2962,7 @@
nsecs_t timeLoopNextNs = 0;
checkSilentMode_l();
-#if 0
- int z = 0; // used in logFormat example
-#endif
+
while (!exitPending())
{
// Log merge requests are performed during AudioFlinger binder transactions, but
@@ -2986,6 +2979,7 @@
processConfigEvents_l();
+ // See comment at declaration of logString for why this is done under mLock
if (logString != NULL) {
mNBLogWriter->logTimestamp();
mNBLogWriter->log(logString);
@@ -3412,7 +3406,7 @@
removeTrack_l(track);
} else { // inactive but not terminated
char buffer[256];
- track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ track->dump(buffer, arraysize(buffer), false /* active */);
mLocalLog.log("removeTracks_l(%p) %s", track.get(), buffer + 4);
}
}
@@ -3738,7 +3732,7 @@
// start the fast mixer
mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
pid_t tid = mFastMixer->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false);
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
stream()->setHalThreadPriority(kPriorityFastMixer);
#ifdef AUDIO_WATCHDOG
@@ -3747,7 +3741,7 @@
mAudioWatchdog->setDump(&mAudioWatchdogDump);
mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
tid = mAudioWatchdog->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer);
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
#endif
}
@@ -4775,7 +4769,7 @@
#ifdef TEE_SINK
// Write the tee output to a .wav file
- dumpTee(fd, mTeeSource, mId);
+ dumpTee(fd, mTeeSource, mId, 'M');
#endif
}
@@ -5948,6 +5942,7 @@
// mPipeMemory
// mFastCaptureNBLogWriter
, mFastTrackAvail(false)
+ , mBtNrecSuspended(false)
{
snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -5971,12 +5966,17 @@
switch (kUseFastCapture) {
case FastCapture_Never:
initFastCapture = false;
+ ALOGV("%p kUseFastCapture = Never, initFastCapture = false", this);
break;
case FastCapture_Always:
initFastCapture = true;
+ ALOGV("%p kUseFastCapture = Always, initFastCapture = true", this);
break;
case FastCapture_Static:
initFastCapture = (mFrameCount * 1000) / mSampleRate < kMinNormalCaptureBufferSizeMs;
+ ALOGV("%p kUseFastCapture = Static, (%lld * 1000) / %u vs %u, initFastCapture = %d",
+ this, (long long)mFrameCount, mSampleRate, kMinNormalCaptureBufferSizeMs,
+ initFastCapture);
break;
// case FastCapture_Dynamic:
}
@@ -5987,13 +5987,16 @@
// quadruple-buffering of 20 ms each; this ensures we can sleep for 20ms in RecordThread
size_t pipeFramesP2 = roundup(4 * FMS_20 * mSampleRate / 1000);
size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
- void *pipeBuffer;
+ void *pipeBuffer = nullptr;
const sp<MemoryDealer> roHeap(readOnlyHeap());
sp<IMemory> pipeMemory;
if ((roHeap == 0) ||
(pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
- (pipeBuffer = pipeMemory->pointer()) == NULL) {
- ALOGE("not enough memory for pipe buffer size=%zu", pipeSize);
+ (pipeBuffer = pipeMemory->pointer()) == nullptr) {
+ ALOGE("not enough memory for pipe buffer size=%zu; "
+ "roHeap=%p, pipeMemory=%p, pipeBuffer=%p; roHeapSize: %lld",
+ pipeSize, roHeap.get(), pipeMemory.get(), pipeBuffer,
+ (long long)kRecordThreadReadOnlyHeapSize);
goto failed;
}
// pipe will be shared directly with fast clients, so clear to avoid leaking old information
@@ -6042,7 +6045,7 @@
// start the fast capture
mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
pid_t tid = mFastCapture->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastCapture, false);
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastCapture, false /*forApp*/);
stream()->setHalThreadPriority(kPriorityFastCapture);
#ifdef AUDIO_WATCHDOG
// FIXME
@@ -6635,19 +6638,19 @@
audio_input_flags_t old = *flags;
chain->checkInputFlagCompatibility(flags);
if (old != *flags) {
- ALOGV("AUDIO_INPUT_FLAGS denied by effect old=%#x new=%#x",
- (int)old, (int)*flags);
+ ALOGV("%p AUDIO_INPUT_FLAGS denied by effect old=%#x new=%#x",
+ this, (int)old, (int)*flags);
}
}
ALOGV_IF((*flags & AUDIO_INPUT_FLAG_FAST) != 0,
- "AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
- frameCount, mFrameCount);
+ "%p AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+ this, frameCount, mFrameCount);
} else {
- ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
- "format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+ ALOGV("%p AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
+ "format=%#x isLinear=%d mFormat=%#x channelMask=%#x sampleRate=%u mSampleRate=%u "
"hasFastCapture=%d tid=%d mFastTrackAvail=%d",
- frameCount, mFrameCount, mPipeFramesP2,
- format, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate,
+ this, frameCount, mFrameCount, mPipeFramesP2,
+ format, audio_is_linear_pcm(format), mFormat, channelMask, sampleRate, mSampleRate,
hasFastCapture(), tid, mFastTrackAvail);
*flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_FAST);
}
@@ -6704,17 +6707,11 @@
}
mTracks.add(track);
- // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
- setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
-
if ((*flags & AUDIO_INPUT_FLAG_FAST) && (tid != -1)) {
pid_t callingPid = IPCThreadState::self()->getCallingPid();
// we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
// so ask activity manager to do this on our behalf
- sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp, true);
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp, true /*forApp*/);
}
}
@@ -7072,6 +7069,26 @@
buffer->frameCount = 0;
}
+void AudioFlinger::RecordThread::checkBtNrec()
+{
+ Mutex::Autolock _l(mLock);
+ checkBtNrec_l();
+}
+
+void AudioFlinger::RecordThread::checkBtNrec_l()
+{
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ if (mBtNrecSuspended.exchange(suspend) != suspend) {
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ setEffectSuspended_l(FX_IID_AEC, suspend, mEffectChains[i]->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, mEffectChains[i]->sessionId());
+ }
+ }
+}
+
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
@@ -7144,17 +7161,7 @@
if (value != AUDIO_DEVICE_NONE) {
mPrevInDevice = value;
}
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
+ checkBtNrec_l();
}
}
if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
@@ -7244,6 +7251,10 @@
result = mInput->stream->getBufferSize(&mBufferSize);
LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
mFrameCount = mBufferSize / mFrameSize;
+ ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%lld, "
+ "mBufferSize=%lld, mFrameCount=%lld",
+ this, mChannelCount, mFormat, (long long)mFrameSize, (long long)mBufferSize,
+ (long long)mFrameCount);
// This is the formula for calculating the temporary buffer size.
// With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
// 1 full output buffer, regardless of the alignment of the available input.
@@ -7383,17 +7394,7 @@
mEffectChains[i]->setDevice_l(mInDevice);
}
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
+ checkBtNrec_l();
// store new source and send to effects
if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
@@ -7512,7 +7513,7 @@
return mThread->getMmapPosition(position);
}
-status_t AudioFlinger::MmapThreadHandle::start(const MmapStreamInterface::Client& client,
+status_t AudioFlinger::MmapThreadHandle::start(const AudioClient& client,
audio_port_handle_t *handle)
{
@@ -7605,75 +7606,75 @@
return mHalStream->getMmapPosition(position);
}
-status_t AudioFlinger::MmapThread::start(const MmapStreamInterface::Client& client,
+status_t AudioFlinger::MmapThread::start(const AudioClient& client,
audio_port_handle_t *handle)
{
- ALOGV("%s clientUid %d mStandby %d", __FUNCTION__, client.clientUid, mStandby);
+ ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
+ client.clientUid, mStandby, mPortId, *handle);
if (mHalStream == 0) {
return NO_INIT;
}
status_t ret;
- audio_session_t sessionId;
- audio_port_handle_t portId;
- if (mActiveTracks.size() == 0) {
+ if (*handle == mPortId) {
// for the first track, reuse portId and session allocated when the stream was opened
ret = mHalStream->start();
if (ret != NO_ERROR) {
ALOGE("%s: error mHalStream->start() = %d for first track", __FUNCTION__, ret);
return ret;
}
- portId = mPortId;
- sessionId = mSessionId;
mStandby = false;
+ return NO_ERROR;
+ }
+
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+
+ audio_io_handle_t io = mId;
+ if (isOutput()) {
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.sample_rate = mSampleRate;
+ config.channel_mask = mChannelMask;
+ config.format = mFormat;
+ audio_stream_type_t stream = streamType();
+ audio_output_flags_t flags =
+ (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
+ audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+ ret = AudioSystem::getOutputForAttr(&mAttr, &io,
+ mSessionId,
+ &stream,
+ client.clientUid,
+ &config,
+ flags,
+ &deviceId,
+ &portId);
} else {
- // for other tracks than first one, get a new port ID from APM.
- sessionId = (audio_session_t)mAudioFlinger->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
- audio_io_handle_t io;
- if (isOutput()) {
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = mSampleRate;
- config.channel_mask = mChannelMask;
- config.format = mFormat;
- audio_stream_type_t stream = streamType();
- audio_output_flags_t flags =
- (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
- ret = AudioSystem::getOutputForAttr(&mAttr, &io,
- sessionId,
- &stream,
- client.clientUid,
- &config,
- flags,
- AUDIO_PORT_HANDLE_NONE,
- &portId);
- } else {
- audio_config_base_t config;
- config.sample_rate = mSampleRate;
- config.channel_mask = mChannelMask;
- config.format = mFormat;
- ret = AudioSystem::getInputForAttr(&mAttr, &io,
- sessionId,
- client.clientPid,
- client.clientUid,
- &config,
- AUDIO_INPUT_FLAG_MMAP_NOIRQ,
- AUDIO_PORT_HANDLE_NONE,
- &portId);
- }
- // APM should not chose a different input or output stream for the same set of attributes
- // and audo configuration
- if (ret != NO_ERROR || io != mId) {
- ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
- __FUNCTION__, ret, io, mId);
- return BAD_VALUE;
- }
+ audio_config_base_t config;
+ config.sample_rate = mSampleRate;
+ config.channel_mask = mChannelMask;
+ config.format = mFormat;
+ audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+ ret = AudioSystem::getInputForAttr(&mAttr, &io,
+ mSessionId,
+ client.clientPid,
+ client.clientUid,
+ &config,
+ AUDIO_INPUT_FLAG_MMAP_NOIRQ,
+ &deviceId,
+ &portId);
+ }
+ // APM should not chose a different input or output stream for the same set of attributes
+ // and audo configuration
+ if (ret != NO_ERROR || io != mId) {
+ ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
+ __FUNCTION__, ret, io, mId);
+ return BAD_VALUE;
}
if (isOutput()) {
- ret = AudioSystem::startOutput(mId, streamType(), sessionId);
+ ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
} else {
- ret = AudioSystem::startInput(mId, sessionId);
+ ret = AudioSystem::startInput(mId, mSessionId);
}
// abort if start is rejected by audio policy manager
@@ -7681,9 +7682,9 @@
ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
if (mActiveTracks.size() != 0) {
if (isOutput()) {
- AudioSystem::releaseOutput(mId, streamType(), sessionId);
+ AudioSystem::releaseOutput(mId, streamType(), mSessionId);
} else {
- AudioSystem::releaseInput(mId, sessionId);
+ AudioSystem::releaseInput(mId, mSessionId);
}
} else {
mHalStream->stop();
@@ -7691,11 +7692,11 @@
return PERMISSION_DENIED;
}
- sp<MmapTrack> track = new MmapTrack(this, mSampleRate, mFormat, mChannelMask, sessionId,
+ sp<MmapTrack> track = new MmapTrack(this, mSampleRate, mFormat, mChannelMask, mSessionId,
client.clientUid, portId);
mActiveTracks.add(track);
- sp<EffectChain> chain = getEffectChain_l(sessionId);
+ sp<EffectChain> chain = getEffectChain_l(mSessionId);
if (chain != 0) {
chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
chain->incTrackCnt();
@@ -7703,10 +7704,9 @@
}
*handle = portId;
-
broadcast_l();
- ALOGV("%s DONE handle %d stream %p", __FUNCTION__, portId, mHalStream.get());
+ ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());
return NO_ERROR;
}
@@ -7719,6 +7719,11 @@
return NO_INIT;
}
+ if (handle == mPortId) {
+ mHalStream->stop();
+ return NO_ERROR;
+ }
+
sp<MmapTrack> track;
for (const sp<MmapTrack> &t : mActiveTracks) {
if (handle == t->portId()) {
@@ -7734,14 +7739,10 @@
if (isOutput()) {
AudioSystem::stopOutput(mId, streamType(), track->sessionId());
- if (mActiveTracks.size() != 0) {
- AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
- }
+ AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
} else {
AudioSystem::stopInput(mId, track->sessionId());
- if (mActiveTracks.size() != 0) {
- AudioSystem::releaseInput(mId, track->sessionId());
- }
+ AudioSystem::releaseInput(mId, track->sessionId());
}
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
@@ -7752,9 +7753,6 @@
broadcast_l();
- if (mActiveTracks.size() == 0) {
- mHalStream->stop();
- }
return NO_ERROR;
}
@@ -7857,17 +7855,34 @@
{
AudioParameter param = AudioParameter(keyValuePair);
int value;
+ bool sendToHal = true;
if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ audio_devices_t device = (audio_devices_t)value;
// forward device change to effects that have requested to be
// aware of attached audio device.
- if (value != AUDIO_DEVICE_NONE) {
- mOutDevice = value;
+ if (device != AUDIO_DEVICE_NONE) {
for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mOutDevice);
+ mEffectChains[i]->setDevice_l(device);
}
}
+ if (audio_is_output_devices(device)) {
+ mOutDevice = device;
+ if (!isOutput()) {
+ sendToHal = false;
+ }
+ } else {
+ mInDevice = device;
+ if (device != AUDIO_DEVICE_NONE) {
+ mPrevInDevice = value;
+ }
+ // TODO: implement and call checkBtNrec_l();
+ }
}
- status = mHalStream->setParameters(keyValuePair);
+ if (sendToHal) {
+ status = mHalStream->setParameters(keyValuePair);
+ } else {
+ status = NO_ERROR;
+ }
return false;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 80b368e..062bad6 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -229,8 +229,7 @@
virtual void binderDied(const wp<IBinder>& who);
private:
- PMDeathRecipient(const PMDeathRecipient&);
- PMDeathRecipient& operator = (const PMDeathRecipient&);
+ DISALLOW_COPY_AND_ASSIGN(PMDeathRecipient);
wp<ThreadBase> mThread;
};
@@ -362,11 +361,6 @@
virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
{ return 0; }
- // suspend or restore effect according to the type of effect passed. a NULL
- // type pointer means suspend all effects in the session
- void setEffectSuspended(const effect_uuid_t *type,
- bool suspend,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX);
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
@@ -418,10 +412,13 @@
void releaseWakeLock_l();
void updateWakeLockUids_l(const SortedVector<uid_t> &uids);
void getPowerManager_l();
+ // suspend or restore effects of the specified type (or all if type is NULL)
+ // on a given session. The number of suspend requests is counted and restore
+ // occurs when all suspend requests are cancelled.
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
audio_session_t sessionId);
- // updated mSuspendedSessions when an effect suspended or restored
+ // updated mSuspendedSessions when an effect is suspended or restored
void updateSuspendedSessions_l(const effect_uuid_t *type,
bool suspend,
audio_session_t sessionId);
@@ -485,6 +482,7 @@
const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first (outer) vector is
// keyed by session ID, the second (inner) by type UUID timeLow field
+ // Updated by updateSuspendedSessions_l() only.
KeyedVector< audio_session_t, KeyedVector< int, sp<SuspendedSessionDesc> > >
mSuspendedSessions;
static const size_t kLogSize = 4 * 1024;
@@ -901,7 +899,7 @@
friend class AudioFlinger; // for numerous
- PlaybackThread& operator = (const PlaybackThread&);
+ DISALLOW_COPY_AND_ASSIGN(PlaybackThread);
status_t addTrack_l(const sp<Track>& track);
bool destroyTrack_l(const sp<Track>& track);
@@ -982,7 +980,7 @@
sp<NBAIO_Source> mTeeSource;
#endif
uint32_t mScreenState; // cached copy of gScreenState
- static const size_t kFastMixerLogSize = 4 * 1024;
+ static const size_t kFastMixerLogSize = 8 * 1024;
sp<NBLog::Writer> mFastMixerNBLogWriter;
@@ -1388,6 +1386,8 @@
}
virtual bool isOutput() const override { return false; }
+ void checkBtNrec();
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
@@ -1395,6 +1395,8 @@
// Call the HAL standby method unconditionally, and don't change mStandby flag
void inputStandBy();
+ void checkBtNrec_l();
+
AudioStreamIn *mInput;
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
@@ -1454,6 +1456,8 @@
sp<NBLog::Writer> mFastCaptureNBLogWriter;
bool mFastTrackAvail; // true if fast track available
+ // common state to all record threads
+ std::atomic_bool mBtNrecSuspended;
};
class MmapThread : public ThreadBase
@@ -1479,7 +1483,7 @@
status_t createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info);
status_t getMmapPosition(struct audio_mmap_position *position);
- status_t start(const MmapStreamInterface::Client& client, audio_port_handle_t *handle);
+ status_t start(const AudioClient& client, audio_port_handle_t *handle);
status_t stop(audio_port_handle_t handle);
status_t standby();
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index e0c09f7..cb540ca 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -92,8 +92,7 @@
protected:
- TrackBase(const TrackBase&);
- TrackBase& operator = (const TrackBase&);
+ DISALLOW_COPY_AND_ASSIGN(TrackBase);
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 301510c..9763bf2 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -234,7 +234,7 @@
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
#ifdef TEE_SINK
- dumpTee(-1, mTeeSource, mId);
+ dumpTee(-1, mTeeSource, mId, 'T');
#endif
// delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
mServerProxy.clear();
diff --git a/services/audioflinger/TypedLogger.cpp b/services/audioflinger/TypedLogger.cpp
index e08f6f6..57c206b 100644
--- a/services/audioflinger/TypedLogger.cpp
+++ b/services/audioflinger/TypedLogger.cpp
@@ -23,5 +23,5 @@
#include "TypedLogger.h"
namespace android {
-thread_local NBLog::Writer *logWriterTLS;
+thread_local NBLog::Writer *tlNBLogWriter;
}
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 0b23c7c..2d84028 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -19,11 +19,84 @@
#define ANDROID_TYPED_LOGGER_H
#include <media/nbaio/NBLog.h>
-#define LOGT(fmt, ...) logWriterTLS->logFormat(fmt, ##__VA_ARGS__) // TODO: check null pointer
+#include <algorithm>
+
+/*
+Fowler-Noll-Vo (FNV-1a) hash function for the file name.
+Hashes at compile time. FNV-1a iterative function:
+
+hash = offset_basis
+for each byte to be hashed
+ hash = hash xor byte
+ hash = hash * FNV_prime
+return hash
+
+offset_basis and FNV_prime values depend on the size of the hash output
+Following values are defined by FNV and should not be changed arbitrarily
+*/
+
+template<typename T>
+constexpr T offset_basis();
+
+template<typename T>
+constexpr T FNV_prime();
+
+template<>
+constexpr uint32_t offset_basis<uint32_t>() {
+ return 2166136261u;
+}
+
+template<>
+constexpr uint32_t FNV_prime<uint32_t>() {
+ return 16777619u;
+}
+
+template<>
+constexpr uint64_t offset_basis<uint64_t>() {
+ return 14695981039346656037ull;
+}
+
+template<>
+constexpr uint64_t FNV_prime<uint64_t>() {
+ return 1099511628211ull;
+}
+
+template <typename T, size_t n>
+constexpr T fnv1a(const char (&file)[n], int i = n - 1) {
+ return i == -1 ? offset_basis<T>() : (fnv1a<T>(file, i - 1) ^ file[i]) * FNV_prime<T>();
+}
+
+template <size_t n>
+constexpr uint64_t hash(const char (&file)[n], uint32_t line) {
+ // Line numbers over or equal to 2^16 are clamped to 2^16 - 1. This way increases collisions
+ // compared to wrapping around, but is easy to identify because it doesn't produce aliasing.
+ // It's a very unlikely case anyways.
+ return ((fnv1a<uint64_t>(file) << 16) ^ ((fnv1a<uint64_t>(file) >> 32) & 0xFFFF0000)) |
+ std::min(line, 0xFFFFu);
+}
+
+// TODO Permit disabling of logging at compile-time.
+
+// TODO A non-nullptr dummy implementation that is a nop would be faster than checking for nullptr
+// in the case when logging is enabled at compile-time and enabled at runtime, but it might be
+// slower than nullptr check when logging is enabled at compile-time and disabled at runtime.
+
+// Write formatted entry to log
+#define LOGT(fmt, ...) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->logFormat((fmt), hash(__FILE__, __LINE__), ##__VA_ARGS__); } \
+ while (0)
+
+// Write histogram timestamp entry
+#define LOG_HIST_TS() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->logHistTS(hash(__FILE__, __LINE__)); } while(0)
+
+// flush all histogram
+#define LOG_HIST_FLUSH() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->logHistFlush(hash(__FILE__, __LINE__)); } while(0)
namespace android {
extern "C" {
-extern thread_local NBLog::Writer *logWriterTLS;
+extern thread_local NBLog::Writer *tlNBLogWriter;
}
} // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 60ed1d6..c868206 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -122,7 +122,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- int selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_io_handle_t output,
@@ -144,7 +144,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
audio_port_handle_t *portId) = 0;
// indicates to the audio policy manager that the input starts being used.
@@ -241,6 +241,9 @@
virtual status_t setMasterMono(bool mono) = 0;
virtual status_t getMasterMono(bool *mono) = 0;
+
+ virtual float getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device) = 0;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
index 18fba25..ca070cf 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -51,6 +51,7 @@
audio_channel_mask_t channelMask() const { return mConfig.channel_mask; }
audio_input_flags_t flags() const { return mFlags; }
uid_t uid() const { return mUid; }
+ void setUid(uid_t uid) { mUid = uid; }
bool matches(const sp<AudioSession> &other) const;
bool isSoundTrigger() const { return mIsSoundTrigger; }
uint32_t openCount() const { return mOpenCount; } ;
@@ -68,7 +69,7 @@
const audio_source_t mInputSource;
const struct audio_config_base mConfig;
const audio_input_flags_t mFlags;
- const uid_t mUid;
+ uid_t mUid;
bool mIsSoundTrigger;
uint32_t mOpenCount;
uint32_t mActiveCount;
diff --git a/services/audiopolicy/config/audio_policy_configuration_stub.xml b/services/audiopolicy/config/audio_policy_configuration_stub.xml
index a7747f8..26c381f 100644
--- a/services/audiopolicy/config/audio_policy_configuration_stub.xml
+++ b/services/audiopolicy/config/audio_policy_configuration_stub.xml
@@ -15,38 +15,9 @@
-->
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
- <modules>
- <module name="stub" halVersion="2.0">
- <attachedDevices>
- <item>Default Out</item>
- <item>Default In</item>
- </attachedDevices>
- <defaultOutputDevice>Default Out</defaultOutputDevice>
- <mixPorts>
- <mixPort name="stub output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
- <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
- </mixPort>
-
- <mixPort name="stub input" role="sink">
- <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
- </mixPort>
- </mixPorts>
- <devicePorts>
- <devicePort tagName="Default Out" type="AUDIO_DEVICE_OUT_STUB" role="sink">
- </devicePort>
-
- <devicePort tagName="Default In" type="AUDIO_DEVICE_IN_STUB" role="source">
- </devicePort>
- </devicePorts>
- <routes>
- <route type="mix" sink="Default Out" sources="stub output"/>
-
- <route type="mix" sink="stub input" sources="Default In"/>
- </routes>
-
- </module>
+ <modules>
+ <!-- Stub Audio HAL -->
+ <xi:include href="stub_audio_policy_configuration.xml"/>
<!-- Remote Submix Audio HAL -->
<xi:include href="r_submix_audio_policy_configuration.xml"/>
diff --git a/services/audiopolicy/config/stub_audio_policy_configuration.xml b/services/audiopolicy/config/stub_audio_policy_configuration.xml
new file mode 100644
index 0000000..17005d2
--- /dev/null
+++ b/services/audiopolicy/config/stub_audio_policy_configuration.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<module name="stub" halVersion="2.0">
+ <attachedDevices>
+ <item>Default Out</item>
+ <item>Default In</item>
+ </attachedDevices>
+ <defaultOutputDevice>Default Out</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="stub output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+
+ <mixPort name="stub input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Default Out" type="AUDIO_DEVICE_OUT_STUB" role="sink">
+ </devicePort>
+
+ <devicePort tagName="Default In" type="AUDIO_DEVICE_IN_STUB" role="source">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Default Out" sources="stub output"/>
+
+ <route type="mix" sink="stub input" sources="Default In"/>
+ </routes>
+</module>
diff --git a/services/audiopolicy/config/usb_audio_policy_configuration.xml b/services/audiopolicy/config/usb_audio_policy_configuration.xml
index 1630a94..a487ecb 100644
--- a/services/audiopolicy/config/usb_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/usb_audio_policy_configuration.xml
@@ -30,14 +30,18 @@
samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="USB Device Out" type="AUDIO_DEVICE_OUT_USB_DEVICE" role="sink"/>
+ <devicePort tagName="USB Headset Out" type="AUDIO_DEVICE_OUT_USB_HEADSET" role="sink"/>
<devicePort tagName="USB Device In" type="AUDIO_DEVICE_IN_USB_DEVICE" role="source"/>
+ <devicePort tagName="USB Headset In" type="AUDIO_DEVICE_IN_USB_HEADSET" role="source"/>
</devicePorts>
<routes>
<route type="mix" sink="USB Host Out"
sources="usb_accessory output"/>
<route type="mix" sink="USB Device Out"
sources="usb_device output"/>
+ <route type="mix" sink="USB Headset Out"
+ sources="usb_device output"/>
<route type="mix" sink="usb_device input"
- sources="USB Device In"/>
+ sources="USB Device In,USB Headset In"/>
</routes>
</module>
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 096ffd1..9bdb98c 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -277,8 +277,11 @@
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
} else if (outputs.isStreamActive(
- AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
- // while media is playing (or has recently played), use the same device
+ AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+ || outputs.isStreamActive(
+ AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY))
+ {
+ // while media/a11y is playing (or has recently played), use the same device
device = getDeviceForStrategyInt(
STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs);
} else {
@@ -552,6 +555,15 @@
AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) {
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
+
+ // for STRATEGY_SONIFICATION:
+ // if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead
+ if ((strategy == STRATEGY_SONIFICATION) &&
+ (device & AUDIO_DEVICE_OUT_SPEAKER) &&
+ (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+ device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+ device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+ }
} break;
default:
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d9859ea..78f195d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -54,6 +54,11 @@
//FIXME: workaround for truncated touch sounds
// to be removed when the problem is handled by system UI
#define TOUCH_SOUND_FIXED_DELAY_MS 100
+
+// Largest difference in dB on earpiece in call between the voice volume and another
+// media / notification / system volume.
+constexpr float IN_CALL_EARPIECE_HEADROOM_DB = 3.f;
+
// ----------------------------------------------------------------------------
// AudioPolicyInterface implementation
// ----------------------------------------------------------------------------
@@ -709,7 +714,8 @@
// only retain flags that will drive the direct output profile selection
// if explicitly requested
static const uint32_t kRelevantFlags =
- (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
+ (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
+ AUDIO_OUTPUT_FLAG_VOIP_RX);
flags =
(audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
@@ -768,7 +774,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
audio_attributes_t attributes;
@@ -812,16 +818,18 @@
ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
" session %d selectedDeviceId %d",
attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
- session, selectedDeviceId);
+ session, *selectedDeviceId);
*stream = streamTypefromAttributesInt(&attributes);
// Explicit routing?
sp<DeviceDescriptor> deviceDesc;
- for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
- if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {
- deviceDesc = mAvailableOutputDevices[i];
- break;
+ if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+ if (mAvailableOutputDevices[i]->getId() == *selectedDeviceId) {
+ deviceDesc = mAvailableOutputDevices[i];
+ break;
+ }
}
}
mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
@@ -844,6 +852,12 @@
return INVALID_OPERATION;
}
+ DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device);
+ *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId()
+ : AUDIO_PORT_HANDLE_NONE;
+
+ ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d", *output, *selectedDeviceId);
+
return NO_ERROR;
}
@@ -923,6 +937,11 @@
}
if (stream == AUDIO_STREAM_TTS) {
flags = AUDIO_OUTPUT_FLAG_TTS;
+ } else if (stream == AUDIO_STREAM_VOICE_CALL &&
+ audio_is_linear_pcm(format)) {
+ flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_VOIP_RX |
+ AUDIO_OUTPUT_FLAG_DIRECT);
+ ALOGV("Set VoIP and Direct output flags for PCM format");
}
sp<IOProfile> profile;
@@ -1078,8 +1097,6 @@
ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
"format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
- ALOGV(" getOutputForDevice() returns output %d", output);
-
return output;
}
@@ -1474,7 +1491,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
audio_port_handle_t *portId)
{
@@ -1482,6 +1499,43 @@
"session %d, flags %#x",
attr->source, config->sample_rate, config->format, config->channel_mask, session, flags);
+ // special case for mmap capture: if an input IO handle is specified, we reuse this input if
+ // possible
+ if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) == AUDIO_INPUT_FLAG_MMAP_NOIRQ &&
+ *input != AUDIO_IO_HANDLE_NONE) {
+ ssize_t index = mInputs.indexOfKey(*input);
+ if (index < 0) {
+ ALOGW("getInputForAttr() unknown MMAP input %d", *input);
+ return BAD_VALUE;
+ }
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
+ sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
+ if (audioSession == 0) {
+ ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
+ return BAD_VALUE;
+ }
+ // For MMAP mode, the first call to getInputForAttr() is made on behalf of audioflinger.
+ // The second call is for the first active client and sets the UID. Any further call
+ // corresponds to a new client and is only permitted from the same UId.
+ if (audioSession->openCount() == 1) {
+ audioSession->setUid(uid);
+ } else if (audioSession->uid() != uid) {
+ ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
+ uid, session, audioSession->uid());
+ return INVALID_OPERATION;
+ }
+ audioSession->changeOpenCount(1);
+ *inputType = API_INPUT_LEGACY;
+ if (*portId == AUDIO_PORT_HANDLE_NONE) {
+ *portId = AudioPort::getNextUniqueId();
+ }
+ DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice);
+ *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
+ : AUDIO_PORT_HANDLE_NONE;
+ ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+ return NO_ERROR;
+ }
+
*input = AUDIO_IO_HANDLE_NONE;
*inputType = API_INPUT_INVALID;
@@ -1504,10 +1558,12 @@
// Explicit routing?
sp<DeviceDescriptor> deviceDesc;
- for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
- if (mAvailableInputDevices[i]->getId() == selectedDeviceId) {
- deviceDesc = mAvailableInputDevices[i];
- break;
+ if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
+ if (mAvailableInputDevices[i]->getId() == *selectedDeviceId) {
+ deviceDesc = mAvailableInputDevices[i];
+ break;
+ }
}
}
mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
@@ -1558,7 +1614,13 @@
return INVALID_OPERATION;
}
- ALOGV("getInputForAttr() returns input type = %d", *inputType);
+ DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+ *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
+ : AUDIO_PORT_HANDLE_NONE;
+
+ ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d",
+ *input, *inputType, *selectedDeviceId);
+
return NO_ERROR;
}
@@ -1588,6 +1650,9 @@
} else {
halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
}
+ } else if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION &&
+ audio_is_linear_pcm(format)) {
+ flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_VOIP_TX);
}
// find a compatible input profile (not necessarily identical in parameters)
@@ -1790,6 +1855,40 @@
return true;
}
+// FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537.
+bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() {
+ if (!mHasComputedSoundTriggerSupportsConcurrentCapture) {
+ bool soundTriggerSupportsConcurrentCapture = false;
+ unsigned int numModules = 0;
+ struct sound_trigger_module_descriptor* nModules = NULL;
+
+ status_t status = SoundTrigger::listModules(nModules, &numModules);
+ if (status == NO_ERROR && numModules != 0) {
+ nModules = (struct sound_trigger_module_descriptor*) calloc(
+ numModules, sizeof(struct sound_trigger_module_descriptor));
+ if (nModules == NULL) {
+ // We failed to malloc the buffer, so just say no for now, and hope that we have more
+ // ram the next time this function is called.
+ ALOGE("Failed to allocate buffer for module descriptors");
+ return false;
+ }
+
+ status = SoundTrigger::listModules(nModules, &numModules);
+ if (status == NO_ERROR) {
+ soundTriggerSupportsConcurrentCapture = true;
+ for (size_t i = 0; i < numModules; ++i) {
+ soundTriggerSupportsConcurrentCapture &=
+ nModules[i].properties.concurrent_capture;
+ }
+ }
+ free(nModules);
+ }
+ mSoundTriggerSupportsConcurrentCapture = soundTriggerSupportsConcurrentCapture;
+ mHasComputedSoundTriggerSupportsConcurrentCapture = true;
+ }
+ return mSoundTriggerSupportsConcurrentCapture;
+}
+
status_t AudioPolicyManager::startInput(audio_io_handle_t input,
audio_session_t session,
@@ -1839,6 +1938,11 @@
continue;
}
+ if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+ activeDesc->getId() == inputDesc->getId()) {
+ continue;
+ }
+
audio_source_t activeSource = activeDesc->inputSource(true);
if (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) {
if (activeSource == AUDIO_SOURCE_HOTWORD) {
@@ -1862,6 +1966,12 @@
}
}
+ // We only need to check if the sound trigger session supports concurrent capture if the
+ // input is also a sound trigger input. Otherwise, we should preempt any hotword stream
+ // that's running.
+ const bool allowConcurrentWithSoundTrigger =
+ inputDesc->isSoundTrigger() ? soundTriggerSupportsConcurrentCapture() : false;
+
// if capture is allowed, preempt currently active HOTWORD captures
for (size_t i = 0; i < activeInputs.size(); i++) {
sp<AudioInputDescriptor> activeDesc = activeInputs[i];
@@ -1870,6 +1980,10 @@
continue;
}
+ if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) {
+ continue;
+ }
+
audio_source_t activeSource = activeDesc->inputSource(true);
if (activeSource == AUDIO_SOURCE_HOTWORD) {
AudioSessionCollection activeSessions =
@@ -2140,7 +2254,8 @@
continue;
}
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
- audio_devices_t curStreamDevice = getDeviceForStrategy(curStrategy, false /*fromCache*/);
+ audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(
+ curStrategy, false /*fromCache*/));
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
((curStreamDevice & device) == 0)) {
continue;
@@ -2151,7 +2266,7 @@
applyVolume = (curDevice & curStreamDevice) != 0;
} else {
applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
- stream, Volume::getDeviceForVolume(curStreamDevice));
+ stream, curStreamDevice);
}
if (applyVolume) {
@@ -3365,6 +3480,12 @@
return NO_ERROR;
}
+float AudioPolicyManager::getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device)
+{
+ return computeVolume(stream, index, device);
+}
+
status_t AudioPolicyManager::disconnectAudioSource(const sp<AudioSourceDescriptor>& sourceDesc)
{
ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle());
@@ -3459,7 +3580,8 @@
mBeaconMuted(false),
mTtsOutputAvailable(false),
mMasterMono(false),
- mMusicEffectOutput(AUDIO_IO_HANDLE_NONE)
+ mMusicEffectOutput(AUDIO_IO_HANDLE_NONE),
+ mHasComputedSoundTriggerSupportsConcurrentCapture(false)
{
mUidCached = getuid();
mpClientInterface = clientInterface;
@@ -5231,6 +5353,30 @@
return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB;
}
+ // in-call: always cap earpiece volume by voice volume + some low headroom
+ if ((stream != AUDIO_STREAM_VOICE_CALL) && (device & AUDIO_DEVICE_OUT_EARPIECE) && isInCall()) {
+ switch (stream) {
+ case AUDIO_STREAM_SYSTEM:
+ case AUDIO_STREAM_RING:
+ case AUDIO_STREAM_MUSIC:
+ case AUDIO_STREAM_ALARM:
+ case AUDIO_STREAM_NOTIFICATION:
+ case AUDIO_STREAM_ENFORCED_AUDIBLE:
+ case AUDIO_STREAM_DTMF:
+ case AUDIO_STREAM_ACCESSIBILITY: {
+ const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, index, device)
+ + IN_CALL_EARPIECE_HEADROOM_DB;
+ if (volumeDB > maxVoiceVolDb) {
+ ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f",
+ stream, volumeDB, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb);
+ volumeDB = maxVoiceVolDb;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
// if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
// - always attenuate notifications volume by 6dB
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index c831d46..82c4c35 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -116,7 +116,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -133,7 +133,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
audio_port_handle_t *portId);
@@ -234,6 +234,8 @@
virtual status_t setMasterMono(bool mono);
virtual status_t getMasterMono(bool *mono);
+ virtual float getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device);
// return the strategy corresponding to a given stream type
routing_strategy getStrategy(audio_stream_type_t stream) const;
@@ -669,6 +671,10 @@
param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono);
mpClientInterface->setParameters(output, param.toString());
}
+
+ bool soundTriggerSupportsConcurrentCapture();
+ bool mSoundTriggerSupportsConcurrentCapture;
+ bool mHasComputedSoundTriggerSupportsConcurrentCapture;
};
};
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1e63a05..7d7cd93 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -167,7 +167,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
if (mAudioPolicyManager == NULL) {
@@ -277,7 +277,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
if (mAudioPolicyManager == NULL) {
@@ -769,4 +769,16 @@
return mAudioPolicyManager->getMasterMono(mono);
}
+
+float AudioPolicyService::getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device)
+{
+ if (mAudioPolicyManager == NULL) {
+ return NAN;
+ }
+ Mutex::Autolock _l(mLock);
+ return mAudioPolicyManager->getStreamVolumeDB(stream, index, device);
+}
+
+
}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 9a083f4..35542f1 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -82,7 +82,7 @@
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId,
+ audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -100,7 +100,7 @@
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ audio_port_handle_t *selectedDeviceId = NULL,
audio_port_handle_t *portId = NULL);
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session);
@@ -202,6 +202,9 @@
virtual status_t setMasterMono(bool mono);
virtual status_t getMasterMono(bool *mono);
+ virtual float getStreamVolumeDB(
+ audio_stream_type_t stream, int index, audio_devices_t device);
+
status_t doStopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t session);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c175259..20bd5e4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -73,6 +73,7 @@
using binder::Status;
using hardware::ICamera;
using hardware::ICameraClient;
+using hardware::ICameraServiceProxy;
using hardware::ICameraServiceListener;
using hardware::camera::common::V1_0::CameraDeviceStatus;
using hardware::camera::common::V1_0::TorchModeStatus;
@@ -2213,7 +2214,7 @@
// Transition device state to OPEN
sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_OPEN,
- mCameraIdStr);
+ mCameraIdStr, mCameraFacing, mClientPackageName);
return OK;
}
@@ -2237,7 +2238,7 @@
// Transition device state to CLOSED
sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_CLOSED,
- mCameraIdStr);
+ mCameraIdStr, mCameraFacing, mClientPackageName);
}
// Always stop watching, even if no camera op is active
if (mOpsCallback != NULL) {
@@ -2741,12 +2742,12 @@
onStatusUpdatedLocked(cameraId, status);
}
-void CameraService::updateProxyDeviceState(ICameraServiceProxy::CameraState newState,
- const String8& cameraId) {
+void CameraService::updateProxyDeviceState(int newState,
+ const String8& cameraId, int facing, const String16& clientName) {
sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
if (proxyBinder == nullptr) return;
String16 id(cameraId);
- proxyBinder->notifyCameraState(id, newState);
+ proxyBinder->notifyCameraState(id, newState, facing, clientName);
}
status_t CameraService::getTorchStatusLocked(
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 87603a3..6d5dde8 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -19,6 +19,7 @@
#include <android/hardware/BnCameraService.h>
#include <android/hardware/ICameraServiceListener.h>
+#include <android/hardware/ICameraServiceProxy.h>
#include <cutils/multiuser.h>
#include <utils/Vector.h>
@@ -26,7 +27,6 @@
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
-#include <camera/ICameraServiceProxy.h>
#include <hardware/camera.h>
#include <android/hardware/camera/common/1.0/types.h>
@@ -182,8 +182,10 @@
* the camera proxy service in the system service
*/
static void updateProxyDeviceState(
- ICameraServiceProxy::CameraState newState,
- const String8& cameraId);
+ int newState,
+ const String8& cameraId,
+ int facing,
+ const String16& clientName);
/////////////////////////////////////////////////////////////////////
// CameraDeviceFactory functionality
@@ -772,7 +774,7 @@
static StatusInternal mapToInternal(hardware::camera::common::V1_0::CameraDeviceStatus status);
static int32_t mapToInterface(StatusInternal status);
- static sp<ICameraServiceProxy> getCameraServiceProxy();
+ static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
static void pingCameraServiceProxy();
};
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 075c2e3..a407d0b 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -256,8 +256,8 @@
disableMsgType(CAMERA_MSG_ALL_MSGS);
mHardware->stopPreview();
sCameraService->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_IDLE,
- String8::format("%d", mCameraId));
+ hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
+ mCameraIdStr, mCameraFacing, mClientPackageName);
mHardware->cancelPicture();
// Release the hardware resources.
mHardware->release();
@@ -418,8 +418,8 @@
result = mHardware->startPreview();
if (result == NO_ERROR) {
sCameraService->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_ACTIVE,
- String8::format("%d", mCameraId));
+ hardware::ICameraServiceProxy::CAMERA_STATE_ACTIVE,
+ mCameraIdStr, mCameraFacing, mClientPackageName);
}
return result;
}
@@ -461,8 +461,8 @@
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
mHardware->stopPreview();
sCameraService->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_IDLE,
- String8::format("%d", mCameraId));
+ hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
+ mCameraIdStr, mCameraFacing, mClientPackageName);
mPreviewBuffer.clear();
}
@@ -960,8 +960,8 @@
// Shutters only happen in response to takePicture, so mark device as
// idle now, until preview is restarted
sCameraService->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_IDLE,
- String8::format("%d", mCameraId));
+ hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
+ mCameraIdStr, mCameraFacing, mClientPackageName);
mLock.unlock();
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 2bf73a0..e8fc080 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -85,7 +85,7 @@
virtual binder::Status endConfigure(int operatingMode) override;
- // Returns -EBUSY if device is not idle
+ // Returns -EBUSY if device is not idle or in error state
virtual binder::Status deleteStream(int streamId) override;
virtual binder::Status createStream(
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 32ee273..51ef160 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -248,7 +248,8 @@
void Camera2ClientBase<TClientBase>::notifyIdle() {
if (mDeviceActive) {
getCameraService()->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_IDLE, TClientBase::mCameraIdStr);
+ hardware::ICameraServiceProxy::CAMERA_STATE_IDLE, TClientBase::mCameraIdStr,
+ TClientBase::mCameraFacing, TClientBase::mClientPackageName);
}
mDeviceActive = false;
@@ -263,7 +264,8 @@
if (!mDeviceActive) {
getCameraService()->updateProxyDeviceState(
- ICameraServiceProxy::CAMERA_STATE_ACTIVE, TClientBase::mCameraIdStr);
+ hardware::ICameraServiceProxy::CAMERA_STATE_ACTIVE, TClientBase::mCameraIdStr,
+ TClientBase::mCameraFacing, TClientBase::mClientPackageName);
}
mDeviceActive = true;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 0c83ed0..69b1d7d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -138,13 +138,15 @@
requestQueueRet.description().c_str());
return DEAD_OBJECT;
}
+
+ std::unique_ptr<ResultMetadataQueue>& resQueue = mResultMetadataQueue;
auto resultQueueRet = session->getCaptureResultMetadataQueue(
- [&queue = mResultMetadataQueue](const auto& descriptor) {
- queue = std::make_unique<ResultMetadataQueue>(descriptor);
- if (!queue->isValid() || queue->availableToWrite() <= 0) {
+ [&resQueue](const auto& descriptor) {
+ resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
+ if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
ALOGE("HAL returns empty result metadata fmq, not use it");
- queue = nullptr;
- // Don't use the queue onwards.
+ resQueue = nullptr;
+ // Don't use the resQueue onwards.
}
});
if (!resultQueueRet.isOk()) {
@@ -153,7 +155,7 @@
return DEAD_OBJECT;
}
- mInterface = std::make_unique<HalInterface>(session, queue);
+ mInterface = new HalInterface(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
@@ -182,7 +184,7 @@
mTagMonitor.initialize(mVendorTagId);
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get());
+ mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -237,7 +239,8 @@
ALOGI("%s: E", __FUNCTION__);
status_t res = OK;
-
+ std::vector<wp<Camera3StreamInterface>> streams;
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
{
Mutex::Autolock l(mLock);
if (mStatus == STATUS_UNINITIALIZED) return res;
@@ -249,9 +252,10 @@
SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
} else {
- res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain");
+ SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
+ maxExpectedDuration);
// Continue to close device even in case of error
}
}
@@ -269,8 +273,13 @@
mRequestThread->requestExit();
}
- mOutputStreams.clear();
- mInputStream.clear();
+ streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ streams.push_back(mOutputStreams[i]);
+ }
+ if (mInputStream != nullptr) {
+ streams.push_back(mInputStream);
+ }
}
// Joining done without holding mLock, otherwise deadlocks may ensue
@@ -289,11 +298,8 @@
HalInterface* interface;
{
Mutex::Autolock l(mLock);
-
mRequestThread.clear();
mStatusTracker.clear();
- mBufferManager.clear();
-
interface = mInterface.get();
}
@@ -301,12 +307,26 @@
// wait on assorted callbacks,etc, to complete before it can return.
interface->close();
+ flushInflightRequests();
+
{
Mutex::Autolock l(mLock);
mInterface->clear();
+ mOutputStreams.clear();
+ mInputStream.clear();
+ mDeletedStreams.clear();
+ mBufferManager.clear();
internalUpdateStatusLocked(STATUS_UNINITIALIZED);
}
+ for (auto& weakStream : streams) {
+ sp<Camera3StreamInterface> stream = weakStream.promote();
+ if (stream != nullptr) {
+ ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+ __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ }
+ }
+
ALOGI("%s: X", __FUNCTION__);
return res;
}
@@ -834,6 +854,15 @@
hardware::Return<void> Camera3Device::processCaptureResult(
const hardware::hidl_vec<
hardware::camera::device::V3_2::CaptureResult>& results) {
+ // Ideally we should grab mLock, but that can lead to deadlock, and
+ // it's not super important to get up to date value of mStatus for this
+ // warning print, hence skipping the lock here
+ if (mStatus == STATUS_ERROR) {
+ // Per API contract, HAL should act as closed after device error
+ // But mStatus can be set to error by framework as well, so just log
+ // a warning here.
+ ALOGW("%s: received capture result in error state.", __FUNCTION__);
+ }
if (mProcessCaptureResultLock.tryLock() != OK) {
// This should never happen; it indicates a wrong client implementation
@@ -965,6 +994,16 @@
hardware::Return<void> Camera3Device::notify(
const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
+ // Ideally we should grab mLock, but that can lead to deadlock, and
+ // it's not super important to get up to date value of mStatus for this
+ // warning print, hence skipping the lock here
+ if (mStatus == STATUS_ERROR) {
+ // Per API contract, HAL should act as closed after device error
+ // But mStatus can be set to error by framework as well, so just log
+ // a warning here.
+ ALOGW("%s: received notify message in error state.", __FUNCTION__);
+ }
+
for (const auto& msg : msgs) {
notify(msg);
}
@@ -1102,6 +1141,7 @@
uint32_t width, uint32_t height, int format, int *id) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
ALOGV("Camera %s: Creating new input stream %d: %d x %d, format %d",
mId.string(), mNextStreamId, width, height, format);
@@ -1122,7 +1162,7 @@
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- res = internalPauseAndWaitLocked();
+ res = internalPauseAndWaitLocked(maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
@@ -1189,6 +1229,7 @@
int streamSetId, bool isShared, uint32_t consumerUsage) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
ALOGV("Camera %s: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
" consumer usage 0x%x, isShared %d", mId.string(), mNextStreamId, width, height, format,
@@ -1210,7 +1251,7 @@
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- res = internalPauseAndWaitLocked();
+ res = internalPauseAndWaitLocked(maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
@@ -1389,6 +1430,12 @@
return -EBUSY;
}
+ if (mStatus == STATUS_ERROR) {
+ ALOGW("%s: Camera %s: deleteStream not allowed in ERROR state",
+ __FUNCTION__, mId.string());
+ return -EBUSY;
+ }
+
sp<Camera3StreamInterface> deletedStream;
ssize_t outputStreamIdx = mOutputStreams.indexOfKey(id);
if (mInputStream != NULL && id == mInputStream->getId()) {
@@ -1455,59 +1502,66 @@
}
Mutex::Autolock il(mInterfaceLock);
- Mutex::Autolock l(mLock);
- switch (mStatus) {
- case STATUS_ERROR:
- CLOGE("Device has encountered a serious error");
- return INVALID_OPERATION;
- case STATUS_UNINITIALIZED:
- CLOGE("Device is not initialized!");
- return INVALID_OPERATION;
- case STATUS_UNCONFIGURED:
- case STATUS_CONFIGURED:
- case STATUS_ACTIVE:
- // OK
- break;
- default:
- SET_ERR_L("Unexpected status: %d", mStatus);
- return INVALID_OPERATION;
- }
+ {
+ Mutex::Autolock l(mLock);
+ switch (mStatus) {
+ case STATUS_ERROR:
+ CLOGE("Device has encountered a serious error");
+ return INVALID_OPERATION;
+ case STATUS_UNINITIALIZED:
+ CLOGE("Device is not initialized!");
+ return INVALID_OPERATION;
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
+ case STATUS_ACTIVE:
+ // OK
+ break;
+ default:
+ SET_ERR_L("Unexpected status: %d", mStatus);
+ return INVALID_OPERATION;
+ }
- if (!mRequestTemplateCache[templateId].isEmpty()) {
- *request = mRequestTemplateCache[templateId];
- return OK;
+ if (!mRequestTemplateCache[templateId].isEmpty()) {
+ *request = mRequestTemplateCache[templateId];
+ return OK;
+ }
}
camera_metadata_t *rawRequest;
status_t res = mInterface->constructDefaultRequestSettings(
(camera3_request_template_t) templateId, &rawRequest);
- if (res == BAD_VALUE) {
- ALOGI("%s: template %d is not supported on this camera device",
- __FUNCTION__, templateId);
- return res;
- } else if (res != OK) {
- CLOGE("Unable to construct request template %d: %s (%d)",
- templateId, strerror(-res), res);
- return res;
+
+ {
+ Mutex::Autolock l(mLock);
+ if (res == BAD_VALUE) {
+ ALOGI("%s: template %d is not supported on this camera device",
+ __FUNCTION__, templateId);
+ return res;
+ } else if (res != OK) {
+ CLOGE("Unable to construct request template %d: %s (%d)",
+ templateId, strerror(-res), res);
+ return res;
+ }
+
+ set_camera_metadata_vendor_id(rawRequest, mVendorTagId);
+ mRequestTemplateCache[templateId].acquire(rawRequest);
+
+ *request = mRequestTemplateCache[templateId];
}
-
- set_camera_metadata_vendor_id(rawRequest, mVendorTagId);
- mRequestTemplateCache[templateId].acquire(rawRequest);
-
- *request = mRequestTemplateCache[templateId];
return OK;
}
status_t Camera3Device::waitUntilDrained() {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
- return waitUntilDrainedLocked();
+ return waitUntilDrainedLocked(maxExpectedDuration);
}
-status_t Camera3Device::waitUntilDrainedLocked() {
+status_t Camera3Device::waitUntilDrainedLocked(nsecs_t maxExpectedDuration) {
switch (mStatus) {
case STATUS_UNINITIALIZED:
case STATUS_UNCONFIGURED:
@@ -1523,9 +1577,9 @@
SET_ERR_L("Unexpected status: %d",mStatus);
return INVALID_OPERATION;
}
-
- ALOGV("%s: Camera %s: Waiting until idle", __FUNCTION__, mId.string());
- status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ ALOGV("%s: Camera %s: Waiting until idle (%" PRIi64 "ns)", __FUNCTION__, mId.string(),
+ maxExpectedDuration);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
res);
@@ -1541,15 +1595,16 @@
}
// Pause to reconfigure
-status_t Camera3Device::internalPauseAndWaitLocked() {
+status_t Camera3Device::internalPauseAndWaitLocked(nsecs_t maxExpectedDuration) {
mRequestThread->setPaused(true);
mPauseStateNotify = true;
- ALOGV("%s: Camera %s: Internal wait until idle", __FUNCTION__, mId.string());
- status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ ALOGV("%s: Camera %s: Internal wait until idle (% " PRIi64 " ns)", __FUNCTION__, mId.string(),
+ maxExpectedDuration);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Can't idle device in %f seconds!",
- kShutdownTimeout/1e9);
+ maxExpectedDuration/1e9);
}
return res;
@@ -2295,7 +2350,9 @@
mErrorCause = errorCause;
- mRequestThread->setPaused(true);
+ if (mRequestThread != nullptr) {
+ mRequestThread->setPaused(true);
+ }
internalUpdateStatusLocked(STATUS_ERROR);
// Notify upstream about a device error
@@ -2316,13 +2373,13 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool hasAppCallback) {
+ bool hasAppCallback, nsecs_t maxExpectedDuration) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- hasAppCallback));
+ hasAppCallback, maxExpectedDuration));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2333,6 +2390,7 @@
}
}
+ mExpectedInflightDuration += maxExpectedDuration;
return OK;
}
@@ -2353,6 +2411,7 @@
}
void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+ nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
mInFlightMap.removeItemsAt(idx, 1);
// Indicate idle inFlightMap to the status tracker
@@ -2363,6 +2422,7 @@
mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
}
}
+ mExpectedInflightDuration -= duration;
}
void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) {
@@ -2373,6 +2433,25 @@
nsecs_t sensorTimestamp = request.sensorTimestamp;
nsecs_t shutterTimestamp = request.shutterTimestamp;
+ bool skipResultMetadata = false;
+ if (request.requestStatus != OK) {
+ switch (request.requestStatus) {
+ case CAMERA3_MSG_ERROR_DEVICE:
+ case CAMERA3_MSG_ERROR_REQUEST:
+ case CAMERA3_MSG_ERROR_RESULT:
+ skipResultMetadata = true;
+ break;
+ case CAMERA3_MSG_ERROR_BUFFER:
+ //Result metadata should return in this case.
+ skipResultMetadata = false;
+ break;
+ default:
+ SET_ERR("Unknown error message: %d", request.requestStatus);
+ skipResultMetadata = false;
+ break;
+ }
+ }
+
// Check if it's okay to remove the request from InFlightMap:
// In the case of a successful request:
// all input and output buffers, all result metadata, shutter callback
@@ -2380,7 +2459,7 @@
// In the case of a unsuccessful request:
// all input and output buffers arrived.
if (request.numBuffersLeft == 0 &&
- (request.requestStatus != OK ||
+ (skipResultMetadata ||
(request.haveResultMetadata && shutterTimestamp != 0))) {
ATRACE_ASYNC_END("frame capture", frameNumber);
@@ -2415,6 +2494,98 @@
}
}
+void Camera3Device::flushInflightRequests() {
+ { // First return buffers cached in mInFlightMap
+ Mutex::Autolock l(mInFlightLock);
+ for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
+ const InFlightRequest &request = mInFlightMap.valueAt(idx);
+ returnOutputBuffers(request.pendingOutputBuffers.array(),
+ request.pendingOutputBuffers.size(), 0);
+ }
+ mInFlightMap.clear();
+ mExpectedInflightDuration = 0;
+ }
+
+ // Then return all inflight buffers not returned by HAL
+ std::vector<std::pair<int32_t, int32_t>> inflightKeys;
+ mInterface->getInflightBufferKeys(&inflightKeys);
+
+ int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1;
+ for (auto& pair : inflightKeys) {
+ int32_t frameNumber = pair.first;
+ int32_t streamId = pair.second;
+ buffer_handle_t* buffer;
+ status_t res = mInterface->popInflightBuffer(frameNumber, streamId, &buffer);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: No in-flight buffer for stream %d",
+ __FUNCTION__, frameNumber, streamId);
+ continue;
+ }
+
+ camera3_stream_buffer_t streamBuffer;
+ streamBuffer.buffer = buffer;
+ streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.acquire_fence = -1;
+ streamBuffer.release_fence = -1;
+
+ // First check if the buffer belongs to deleted stream
+ bool streamDeleted = false;
+ for (auto& stream : mDeletedStreams) {
+ if (streamId == stream->getId()) {
+ streamDeleted = true;
+ // Return buffer to deleted stream
+ camera3_stream* halStream = stream->asHalStream();
+ streamBuffer.stream = halStream;
+ switch (halStream->stream_type) {
+ case CAMERA3_STREAM_OUTPUT:
+ res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0);
+ if (res != OK) {
+ ALOGE("%s: Can't return output buffer for frame %d to"
+ " stream %d: %s (%d)", __FUNCTION__,
+ frameNumber, streamId, strerror(-res), res);
+ }
+ break;
+ case CAMERA3_STREAM_INPUT:
+ res = stream->returnInputBuffer(streamBuffer);
+ if (res != OK) {
+ ALOGE("%s: Can't return input buffer for frame %d to"
+ " stream %d: %s (%d)", __FUNCTION__,
+ frameNumber, streamId, strerror(-res), res);
+ }
+ break;
+ default: // Bi-direcitonal stream is deprecated
+ ALOGE("%s: stream %d has unknown stream type %d",
+ __FUNCTION__, streamId, halStream->stream_type);
+ break;
+ }
+ break;
+ }
+ }
+ if (streamDeleted) {
+ continue;
+ }
+
+ // Then check against configured streams
+ if (streamId == inputStreamId) {
+ streamBuffer.stream = mInputStream->asHalStream();
+ res = mInputStream->returnInputBuffer(streamBuffer);
+ if (res != OK) {
+ ALOGE("%s: Can't return input buffer for frame %d to"
+ " stream %d: %s (%d)", __FUNCTION__,
+ frameNumber, streamId, strerror(-res), res);
+ }
+ } else {
+ ssize_t idx = mOutputStreams.indexOfKey(streamId);
+ if (idx == NAME_NOT_FOUND) {
+ ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
+ continue;
+ }
+ streamBuffer.stream = mOutputStreams.valueAt(idx)->asHalStream();
+ returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
+ }
+ }
+}
+
void Camera3Device::insertResultLocked(CaptureResult *result,
uint32_t frameNumber) {
if (result == nullptr) return;
@@ -3330,6 +3501,20 @@
return res;
}
+void Camera3Device::HalInterface::getInflightBufferKeys(
+ std::vector<std::pair<int32_t, int32_t>>* out) {
+ std::lock_guard<std::mutex> lock(mInflightLock);
+ out->clear();
+ out->reserve(mInflightBufferMap.size());
+ for (auto& pair : mInflightBufferMap) {
+ uint64_t key = pair.first;
+ int32_t streamId = key & 0xFFFFFFFF;
+ int32_t frameNumber = (key >> 32) & 0xFFFFFFFF;
+ out->push_back(std::make_pair(frameNumber, streamId));
+ }
+ return;
+}
+
status_t Camera3Device::HalInterface::pushInflightBufferLocked(
int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer, int acquireFence) {
uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
@@ -3404,7 +3589,7 @@
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
- HalInterface* interface) :
+ sp<HalInterface> interface) :
Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
@@ -3579,7 +3764,8 @@
// Abort the input buffers for reprocess requests.
if ((*it)->mInputStream != NULL) {
camera3_stream_buffer_t inputBuffer;
- status_t res = (*it)->mInputStream->getInputBuffer(&inputBuffer);
+ status_t res = (*it)->mInputStream->getInputBuffer(&inputBuffer,
+ /*respectHalLimit*/ false);
if (res != OK) {
ALOGW("%s: %d: couldn't get input buffer while clearing the request "
"list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
@@ -3801,6 +3987,42 @@
return true;
}
+nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_metadata_t *request) {
+ nsecs_t maxExpectedDuration = kDefaultExpectedDuration;
+ camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+ find_camera_metadata_ro_entry(request,
+ ANDROID_CONTROL_AE_MODE,
+ &e);
+ if (e.count == 0) return maxExpectedDuration;
+
+ switch (e.data.u8[0]) {
+ case ANDROID_CONTROL_AE_MODE_OFF:
+ find_camera_metadata_ro_entry(request,
+ ANDROID_SENSOR_EXPOSURE_TIME,
+ &e);
+ if (e.count > 0) {
+ maxExpectedDuration = e.data.i64[0];
+ }
+ find_camera_metadata_ro_entry(request,
+ ANDROID_SENSOR_FRAME_DURATION,
+ &e);
+ if (e.count > 0) {
+ maxExpectedDuration = std::max(e.data.i64[0], maxExpectedDuration);
+ }
+ break;
+ default:
+ find_camera_metadata_ro_entry(request,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ &e);
+ if (e.count > 1) {
+ maxExpectedDuration = 1e9 / e.data.u8[0];
+ }
+ break;
+ }
+
+ return maxExpectedDuration;
+}
+
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
@@ -4021,7 +4243,8 @@
res = parent->registerInFlight(halRequest->frame_number,
totalNumBuffers, captureRequest->mResultExtras,
/*hasInput*/halRequest->input_buffer != NULL,
- hasCallback);
+ hasCallback,
+ calculateMaxExpectedDuration(halRequest->settings));
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
__FUNCTION__,
@@ -4075,6 +4298,12 @@
return false;
}
+nsecs_t Camera3Device::getExpectedInFlightDuration() {
+ Mutex::Autolock al(mInFlightLock);
+ return mExpectedInflightDuration > kMinInflightDuration ?
+ mExpectedInflightDuration : kMinInflightDuration;
+}
+
void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
if (mNextRequests.empty()) {
return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index bfb58c6..d700e03 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -187,10 +187,11 @@
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
- static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
static const nsecs_t kActiveTimeout = 500000000; // 500 ms
- static const size_t kInFlightWarnLimit = 20;
+ static const size_t kInFlightWarnLimit = 30;
static const size_t kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
+ static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
+ static const nsecs_t kMinInflightDuration = 5000000000; // 5 s
// SCHED_FIFO priority for request submission thread in HFR mode
static const int kRequestThreadPriority = 1;
@@ -265,6 +266,10 @@
status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
/*out*/ buffer_handle_t **buffer);
+ // Get a vector of (frameNumber, streamId) pair of currently inflight
+ // buffers
+ void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
+
private:
camera3_device_t *mHal3Device;
sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -333,7 +338,7 @@
std::vector<std::pair<int, uint64_t>> mFreedBuffers;
};
- std::unique_ptr<HalInterface> mInterface;
+ sp<HalInterface> mInterface;
CameraMetadata mDeviceInfo;
@@ -481,7 +486,7 @@
* CameraDeviceBase interface we shouldn't need to.
* Must be called with mLock and mInterfaceLock both held.
*/
- status_t internalPauseAndWaitLocked();
+ status_t internalPauseAndWaitLocked(nsecs_t maxExpectedDuration);
/**
* Resume work after internalPauseAndWaitLocked()
@@ -507,7 +512,7 @@
*
* Need to be called with mLock and mInterfaceLock held.
*/
- status_t waitUntilDrainedLocked();
+ status_t waitUntilDrainedLocked(nsecs_t maxExpectedDuration);
/**
* Do common work for setting up a streaming or single capture request.
@@ -624,7 +629,7 @@
RequestThread(wp<Camera3Device> parent,
sp<camera3::StatusTracker> statusTracker,
- HalInterface* interface);
+ sp<HalInterface> interface);
~RequestThread();
void setNotificationListener(wp<NotificationListener> listener);
@@ -772,9 +777,12 @@
// send request in mNextRequests to HAL in a batch. Return true = sucssess
bool sendRequestsBatch();
+ // Calculate the expected maximum duration for a request
+ nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
+
wp<Camera3Device> mParent;
wp<camera3::StatusTracker> mStatusTracker;
- HalInterface* mInterface;
+ sp<HalInterface> mInterface;
wp<NotificationListener> mListener;
@@ -873,6 +881,11 @@
// is not for constrained high speed recording, this flag will also be true.
bool hasCallback;
+ // Maximum expected frame duration for this request.
+ // For manual captures, equal to the max of requested exposure time and frame duration
+ // For auto-exposure modes, equal to 1/(lower end of target FPS range)
+ nsecs_t maxExpectedDuration;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
shutterTimestamp(0),
@@ -881,11 +894,12 @@
haveResultMetadata(false),
numBuffersLeft(0),
hasInputBuffer(false),
- hasCallback(true) {
+ hasCallback(true),
+ maxExpectedDuration(kDefaultExpectedDuration) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
- bool hasAppCallback) :
+ bool hasAppCallback, nsecs_t maxDuration) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -893,20 +907,31 @@
numBuffersLeft(numBuffers),
resultExtras(extras),
hasInputBuffer(hasInput),
- hasCallback(hasAppCallback) {
+ hasCallback(hasAppCallback),
+ maxExpectedDuration(maxDuration) {
}
};
// Map from frame number to the in-flight request state
typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
- Mutex mInFlightLock; // Protects mInFlightMap
+
+ Mutex mInFlightLock; // Protects mInFlightMap and
+ // mExpectedInflightDuration
InFlightMap mInFlightMap;
+ nsecs_t mExpectedInflightDuration = 0;
int mInFlightStatusId;
+
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool callback);
+ bool callback, nsecs_t maxExpectedDuration);
+
+ /**
+ * Returns the maximum expected time it'll take for all currently in-flight
+ * requests to complete, based on their settings
+ */
+ nsecs_t getExpectedInFlightDuration();
/**
* Tracking for idle detection
@@ -1023,6 +1048,10 @@
// Remove the in-flight request of the given index from mInFlightMap
// if it's no longer needed. It must only be called with mInFlightLock held.
void removeInFlightRequestIfReadyLocked(int idx);
+ // Remove all in-flight requests and return all buffers.
+ // This is used after HAL interface is closed to cleanup any request/buffers
+ // not returned by HAL.
+ void flushInflightRequests();
/**** End scope for mInFlightLock ****/
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 4eb15ad..ff2dcef 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -293,8 +293,18 @@
void Camera3InputStream::onBufferFreed(const wp<GraphicBuffer>& gb) {
const sp<GraphicBuffer> buffer = gb.promote();
if (buffer != nullptr) {
- if (mBufferFreedListener != nullptr) {
- mBufferFreedListener->onBufferFreed(mId, buffer->handle);
+ camera3_stream_buffer streamBuffer =
+ {nullptr, &buffer->handle, 0, -1, -1};
+ // Check if this buffer is outstanding.
+ if (isOutstandingBuffer(streamBuffer)) {
+ ALOGV("%s: Stream %d: Trying to free a buffer that is still being "
+ "processed.", __FUNCTION__, mId);
+ return;
+ }
+
+ sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
+ if (callback != nullptr) {
+ callback->onBufferFreed(mId, buffer->handle);
}
} else {
ALOGE("%s: GraphicBuffer is freed before onBufferFreed callback finishes!", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e15aa43..b02cd6a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -727,7 +727,7 @@
void Camera3OutputStream::onBuffersRemovedLocked(
const std::vector<sp<GraphicBuffer>>& removedBuffers) {
- Camera3StreamBufferFreedListener* callback = mBufferFreedListener;
+ sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
if (callback != nullptr) {
for (auto gb : removedBuffers) {
callback->onBufferFreed(mId, gb->handle);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index ba352c4..9e6ac79 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -479,6 +479,7 @@
if (res == OK) {
fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
if (buffer->buffer) {
+ Mutex::Autolock l(mOutstandingBuffersLock);
mOutstandingBuffers.push_back(*buffer->buffer);
}
}
@@ -486,11 +487,13 @@
return res;
}
-bool Camera3Stream::isOutstandingBuffer(const camera3_stream_buffer &buffer) {
+bool Camera3Stream::isOutstandingBuffer(const camera3_stream_buffer &buffer) const{
if (buffer.buffer == nullptr) {
return false;
}
+ Mutex::Autolock l(mOutstandingBuffersLock);
+
for (auto b : mOutstandingBuffers) {
if (b == *buffer.buffer) {
return true;
@@ -504,6 +507,8 @@
return;
}
+ Mutex::Autolock l(mOutstandingBuffersLock);
+
for (auto b = mOutstandingBuffers.begin(); b != mOutstandingBuffers.end(); b++) {
if (*b == *buffer.buffer) {
mOutstandingBuffers.erase(b);
@@ -523,6 +528,8 @@
return BAD_VALUE;
}
+ removeOutstandingBuffer(buffer);
+
/**
* TODO: Check that the state is valid first.
*
@@ -540,11 +547,10 @@
// buffer to be returned.
mOutputBufferReturnedSignal.signal();
- removeOutstandingBuffer(buffer);
return res;
}
-status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer) {
+status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res = OK;
@@ -557,7 +563,7 @@
}
// Wait for new buffer returned back if we are running into the limit.
- if (getHandoutInputBufferCountLocked() == camera3_stream::max_buffers) {
+ if (getHandoutInputBufferCountLocked() == camera3_stream::max_buffers && respectHalLimit) {
ALOGV("%s: Already dequeued max input buffers (%d), wait for next returned one.",
__FUNCTION__, camera3_stream::max_buffers);
res = mInputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration);
@@ -574,6 +580,7 @@
if (res == OK) {
fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/false);
if (buffer->buffer) {
+ Mutex::Autolock l(mOutstandingBuffersLock);
mOutstandingBuffers.push_back(*buffer->buffer);
}
}
@@ -591,13 +598,14 @@
return BAD_VALUE;
}
+ removeOutstandingBuffer(buffer);
+
status_t res = returnInputBufferLocked(buffer);
if (res == OK) {
fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/false);
mInputBufferReturnedSignal.signal();
}
- removeOutstandingBuffer(buffer);
return res;
}
@@ -744,7 +752,7 @@
}
void Camera3Stream::setBufferFreedListener(
- Camera3StreamBufferFreedListener* listener) {
+ wp<Camera3StreamBufferFreedListener> listener) {
Mutex::Autolock l(mLock);
// Only allow set listener during stream configuration because stream is guaranteed to be IDLE
// at this state, so setBufferFreedListener won't collide with onBufferFreed callbacks
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index b5a9c5d..44fe6b6 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -308,8 +308,10 @@
* For bidirectional streams, this method applies to the input-side
* buffers.
*
+ * Normally this call will block until the handed out buffer count is less than the stream
+ * max buffer count; if respectHalLimit is set to false, this is ignored.
*/
- status_t getInputBuffer(camera3_stream_buffer *buffer);
+ status_t getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit = true);
/**
* Return a buffer to the stream after use by the HAL.
@@ -369,7 +371,7 @@
// Setting listener will remove previous listener (if exists)
virtual void setBufferFreedListener(
- Camera3StreamBufferFreedListener* listener) override;
+ wp<Camera3StreamBufferFreedListener> listener) override;
/**
* Return if the buffer queue of the stream is abandoned.
@@ -414,7 +416,7 @@
android_dataspace dataSpace, camera3_stream_rotation_t rotation,
int setId);
- Camera3StreamBufferFreedListener* mBufferFreedListener;
+ wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
/**
* Interface to be implemented by derived classes
@@ -459,6 +461,9 @@
// INVALID_OPERATION if they cannot be obtained.
virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
+ // Return whether the buffer is in the list of outstanding buffers.
+ bool isOutstandingBuffer(const camera3_stream_buffer& buffer) const;
+
// Tracking for idle state
wp<StatusTracker> mStatusTracker;
// Status tracker component ID
@@ -481,9 +486,6 @@
status_t cancelPrepareLocked();
- // Return whether the buffer is in the list of outstanding buffers.
- bool isOutstandingBuffer(const camera3_stream_buffer& buffer);
-
// Remove the buffer from the list of outstanding buffers.
void removeOutstandingBuffer(const camera3_stream_buffer& buffer);
@@ -500,6 +502,7 @@
// Number of buffers allocated on last prepare call.
size_t mLastMaxCount;
+ mutable Mutex mOutstandingBuffersLock;
// Outstanding buffers dequeued from the stream's buffer queue.
List<buffer_handle_t> mOutstandingBuffers;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
index 478a752..104cd22 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
@@ -24,7 +24,7 @@
namespace camera3 {
-class Camera3StreamBufferFreedListener {
+class Camera3StreamBufferFreedListener : public virtual RefBase {
public:
// onBufferFreed is called when a buffer is no longer being managed
// by this stream. This will not be called in events when all
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 37b7c36..0544a1b 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -232,8 +232,10 @@
* For bidirectional streams, this method applies to the input-side
* buffers.
*
+ * Normally this call will block until the handed out buffer count is less than the stream
+ * max buffer count; if respectHalLimit is set to false, this is ignored.
*/
- virtual status_t getInputBuffer(camera3_stream_buffer *buffer) = 0;
+ virtual status_t getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit = true) = 0;
/**
* Return a buffer to the stream after use by the HAL.
@@ -296,7 +298,7 @@
* Client is responsible to keep the listener object alive throughout the lifecycle of this
* Camera3Stream.
*/
- virtual void setBufferFreedListener(Camera3StreamBufferFreedListener* listener) = 0;
+ virtual void setBufferFreedListener(wp<Camera3StreamBufferFreedListener> listener) = 0;
};
} // namespace camera3
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
index 6d5787e..3477f1f 100644
--- a/services/mediaanalytics/MetricsSummarizer.cpp
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -153,11 +153,11 @@
ALOGE("unable to save MediaMetrics record");
}
sortProps(item);
- item->setInt32("aggregated",1);
+ item->setInt32("count",1);
mSummaries->push_back(item);
} else {
ALOGV("increment existing record");
- (*it)->addInt32("aggregated",1);
+ (*it)->addInt32("count",1);
mergeRecord(*(*it), *item);
}
}
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 8e1fc77..a751b4c 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -12,6 +12,7 @@
dup: 1
ppoll: 1
mmap2: 1
+getrandom: 1
# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
# parser support for '<' is in this needs to be modified to also prevent
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index aaf1018..a5512e1 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -26,7 +26,7 @@
namespace android {
-// static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
+ static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
MediaLogService::MediaLogService() :
BnMediaLogService(),
mMergerShared((NBLog::Shared*) malloc(NBLog::Timeline::sharedSize(kMergeBufferSize))),
@@ -99,35 +99,38 @@
return NO_ERROR;
}
-#if 0
- Vector<NBLog::NamedReader> namedReaders;
- {
- bool locked = dumpTryLock(mLock);
+ if (args.size() > 0) {
+ const String8 arg0(args[0]);
+ if (!strcmp(arg0.string(), "-r")) {
+ // needed because mNamedReaders is protected by mLock
+ bool locked = dumpTryLock(mLock);
- // failed to lock - MediaLogService is probably deadlocked
- if (!locked) {
- String8 result(kDeadlockedString);
- if (fd >= 0) {
- write(fd, result.string(), result.size());
- } else {
- ALOGW("%s:", result.string());
+ // failed to lock - MediaLogService is probably deadlocked
+ if (!locked) {
+ String8 result(kDeadlockedString);
+ if (fd >= 0) {
+ write(fd, result.string(), result.size());
+ } else {
+ ALOGW("%s:", result.string());
+ }
+ // TODO should we instead proceed to mMergeReader.dump? does it need lock?
+ return NO_ERROR;
}
- return NO_ERROR;
- }
- // namedReaders = mNamedReaders;
- // for (size_t i = 0; i < namedReaders.size(); i++) {
- // const NBLog::NamedReader& namedReader = namedReaders[i];
- // if (fd >= 0) {
- // dprintf(fd, "\n%s:\n", namedReader.name());
- // } else {
- // ALOGI("%s:", namedReader.name());
- // }
- // namedReader.reader()->dump(fd, 0 /*indent*/);
- // }
- mLock.unlock();
+ for (const auto& namedReader : mNamedReaders) {
+ if (fd >= 0) {
+ dprintf(fd, "\n%s:\n", namedReader.name());
+ } else {
+ ALOGI("%s:", namedReader.name());
+ }
+ // TODO This code is for testing, remove it when done
+ // namedReader.reader()->dump(fd, 0 /*indent*/);
+ }
+
+ mLock.unlock();
+ }
}
-#endif
+
// FIXME request merge to make sure log is up to date
mMergeReader.dump(fd);
return NO_ERROR;
diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h
index c6b99f1..39d9cc0 100644
--- a/services/medialog/MediaLogService.h
+++ b/services/medialog/MediaLogService.h
@@ -49,12 +49,15 @@
// Internal dump
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
- static const size_t kMergeBufferSize = 16 * 1024; // TODO determine good value for this
+ // Size of merge buffer, in bytes
+ static const size_t kMergeBufferSize = 64 * 1024; // TODO determine good value for this
static bool dumpTryLock(Mutex& mutex);
Mutex mLock;
- Vector<NBLog::NamedReader> mNamedReaders;
+ Vector<NBLog::NamedReader> mNamedReaders; // protected by mLock
+
+ // FIXME Need comments on all of these, especially about locking
NBLog::Shared *mMergerShared;
NBLog::Merger mMerger;
NBLog::MergeReader mMergeReader;
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
new file mode 100644
index 0000000..75392bd
--- /dev/null
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2017 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 "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <binder/IPCThreadState.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "utility/AAudioUtilities.h"
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioClientTracker.h"
+
+using namespace android;
+using namespace aaudio;
+
+ANDROID_SINGLETON_STATIC_INSTANCE(AAudioClientTracker);
+
+AAudioClientTracker::AAudioClientTracker()
+ : Singleton<AAudioClientTracker>() {
+}
+
+
+std::string AAudioClientTracker::dump() const {
+ std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "AAudioClientTracker may be deadlocked\n";
+ }
+
+ result << "AAudioClientTracker:\n";
+ for (const auto& it : mNotificationClients) {
+ result << it.second->dump();
+ }
+
+ if (isLocked) {
+ mLock.unlock();
+ }
+ return result.str();
+}
+
+// Create a tracker for the client.
+aaudio_result_t AAudioClientTracker::registerClient(pid_t pid,
+ const sp<IAAudioClient>& client) {
+ ALOGV("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
+ pid, getpid());
+
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mNotificationClients.count(pid) == 0) {
+ sp<NotificationClient> notificationClient = new NotificationClient(pid);
+ mNotificationClients[pid] = notificationClient;
+
+ sp<IBinder> binder = IInterface::asBinder(client);
+ status_t status = binder->linkToDeath(notificationClient);
+ ALOGW_IF(status != NO_ERROR,
+ "AAudioClientTracker::registerClient() linkToDeath = %d\n", status);
+ return AAudioConvert_androidToAAudioResult(status);
+ } else {
+ ALOGW("AAudioClientTracker::registerClient(%d) already registered!", pid);
+ return AAUDIO_OK; // TODO should this be considered an error
+ }
+}
+
+void AAudioClientTracker::unregisterClient(pid_t pid) {
+ ALOGV("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
+ pid, getpid());
+ std::lock_guard<std::mutex> lock(mLock);
+ mNotificationClients.erase(pid);
+}
+
+int32_t AAudioClientTracker::getStreamCount(pid_t pid) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto it = mNotificationClients.find(pid);
+ if (it != mNotificationClients.end()) {
+ return it->second->getStreamCount();
+ } else {
+ return 0; // no existing client
+ }
+}
+
+aaudio_result_t
+AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
+ aaudio_result_t result = AAUDIO_OK;
+ ALOGV("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
+ std::lock_guard<std::mutex> lock(mLock);
+ sp<NotificationClient> notificationClient = mNotificationClients[pid];
+ if (notificationClient == 0) {
+ // This will get called the first time the audio server registers an internal stream.
+ ALOGV("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
+ notificationClient = new NotificationClient(pid);
+ mNotificationClients[pid] = notificationClient;
+ }
+ notificationClient->registerClientStream(serviceStream);
+ return result;
+}
+
+// Find the tracker for this process and remove it.
+aaudio_result_t
+AAudioClientTracker::unregisterClientStream(pid_t pid,
+ sp<AAudioServiceStreamBase> serviceStream) {
+ ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
+ std::lock_guard<std::mutex> lock(mLock);
+ auto it = mNotificationClients.find(pid);
+ if (it != mNotificationClients.end()) {
+ ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
+ pid, serviceStream.get());
+ it->second->unregisterClientStream(serviceStream);
+ } else {
+ ALOGE("AAudioClientTracker::unregisterClientStream(%d, %p) missing NotificationClient\n",
+ pid, serviceStream.get());
+ }
+ return AAUDIO_OK;
+}
+
+AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid)
+ : mProcessId(pid) {
+ //ALOGD("AAudioClientTracker::NotificationClient(%d) created %p\n", pid, this);
+}
+
+AAudioClientTracker::NotificationClient::~NotificationClient() {
+ //ALOGD("AAudioClientTracker::~NotificationClient() destroyed %p\n", this);
+}
+
+int32_t AAudioClientTracker::NotificationClient::getStreamCount() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mStreams.size();
+}
+
+aaudio_result_t AAudioClientTracker::NotificationClient::registerClientStream(
+ sp<AAudioServiceStreamBase> serviceStream) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mStreams.insert(serviceStream);
+ return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioClientTracker::NotificationClient::unregisterClientStream(
+ sp<AAudioServiceStreamBase> serviceStream) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mStreams.erase(serviceStream);
+ return AAUDIO_OK;
+}
+
+// Close any open streams for the client.
+void AAudioClientTracker::NotificationClient::binderDied(const wp<IBinder>& who __unused) {
+ AAudioService *aaudioService = AAudioClientTracker::getInstance().getAAudioService();
+ if (aaudioService != nullptr) {
+ // Copy the current list of streams to another vector because closing them below
+ // will cause unregisterClientStream() calls back to this object.
+ std::set<sp<AAudioServiceStreamBase>> streamsToClose;
+
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (auto serviceStream : mStreams) {
+ streamsToClose.insert(serviceStream);
+ }
+ }
+
+ for (auto serviceStream : streamsToClose) {
+ aaudio_handle_t handle = serviceStream->getHandle();
+ ALOGW("AAudioClientTracker::binderDied() close abandoned stream 0x%08X\n", handle);
+ aaudioService->closeStream(handle);
+ }
+ // mStreams should be empty now
+ }
+ sp<NotificationClient> keep(this);
+ AAudioClientTracker::getInstance().unregisterClient(mProcessId);
+}
+
+
+std::string AAudioClientTracker::NotificationClient::dump() const {
+ std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "AAudioClientTracker::NotificationClient may be deadlocked\n";
+ }
+
+ result << " client: pid = " << mProcessId << " has " << mStreams.size() << " streams\n";
+ for (auto serviceStream : mStreams) {
+ result << " stream: 0x" << std::hex << serviceStream->getHandle() << std::dec << "\n";
+ }
+
+ if (isLocked) {
+ mLock.unlock();
+ }
+ return result.str();
+}
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
new file mode 100644
index 0000000..accf1a7
--- /dev/null
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_AAUDIO_AAUDIO_CLIENT_TRACKER_H
+#define ANDROID_AAUDIO_AAUDIO_CLIENT_TRACKER_H
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include <utils/Singleton.h>
+
+#include <aaudio/AAudio.h>
+#include "binding/IAAudioClient.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+class AAudioClientTracker : public android::Singleton<AAudioClientTracker>{
+public:
+ AAudioClientTracker();
+ ~AAudioClientTracker() = default;
+
+ /**
+ * Returns information about the state of the this class.
+ *
+ * Will attempt to get the object lock, but will proceed
+ * even if it cannot.
+ *
+ * Each line of information ends with a newline.
+ *
+ * @return a string with useful information
+ */
+ std::string dump() const;
+
+ aaudio_result_t registerClient(pid_t pid, const android::sp<android::IAAudioClient>& client);
+
+ void unregisterClient(pid_t pid);
+
+ int32_t getStreamCount(pid_t pid);
+
+ aaudio_result_t registerClientStream(pid_t pid,
+ android::sp<AAudioServiceStreamBase> serviceStream);
+
+ aaudio_result_t unregisterClientStream(pid_t pid,
+ android::sp<AAudioServiceStreamBase> serviceStream);
+
+ android::AAudioService *getAAudioService() const {
+ return mAAudioService;
+ }
+
+ void setAAudioService(android::AAudioService *aaudioService) {
+ mAAudioService = aaudioService;
+ }
+
+private:
+
+ /**
+ * One per process.
+ */
+ class NotificationClient : public IBinder::DeathRecipient {
+ public:
+ NotificationClient(pid_t pid);
+ virtual ~NotificationClient();
+
+ int32_t getStreamCount();
+
+ std::string dump() const;
+
+ aaudio_result_t registerClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+
+ aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const android::wp<IBinder>& who);
+
+ protected:
+ mutable std::mutex mLock;
+ const pid_t mProcessId;
+ std::set<android::sp<AAudioServiceStreamBase>> mStreams;
+ };
+
+ mutable std::mutex mLock;
+ std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
+ android::AAudioService *mAAudioService = nullptr;
+};
+
+} /* namespace aaudio */
+
+#endif //ANDROID_AAUDIO_AAUDIO_CLIENT_TRACKER_H
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 3dc1feb..ec2f5b9 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioEndpointManager"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <assert.h>
+#include <functional>
#include <map>
#include <mutex>
+#include <sstream>
+#include <utility/AAudioUtilities.h>
#include "AAudioEndpointManager.h"
@@ -35,51 +38,114 @@
, mOutputs() {
}
-AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
- aaudio_direction_t direction) {
+std::string AAudioEndpointManager::dump() const {
+ std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "EndpointManager may be deadlocked\n";
+ }
+
+ result << "AAudioEndpointManager:" << "\n";
+ size_t inputs = mInputs.size();
+ result << "Input Endpoints: " << inputs << "\n";
+ for (const auto &input : mInputs) {
+ result << " Input: " << input->dump() << "\n";
+ }
+
+ size_t outputs = mOutputs.size();
+ result << "Output Endpoints: " << outputs << "\n";
+ for (const auto &output : mOutputs) {
+ result << " Output: " << output->dump() << "\n";
+ }
+
+ if (isLocked) {
+ mLock.unlock();
+ }
+ return result.str();
+}
+
+AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService,
+ const AAudioStreamConfiguration& configuration, aaudio_direction_t direction) {
AAudioServiceEndpoint *endpoint = nullptr;
+ AAudioServiceEndpointCapture *capture = nullptr;
+ AAudioServiceEndpointPlay *player = nullptr;
std::lock_guard<std::mutex> lock(mLock);
// Try to find an existing endpoint.
+
+
+
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
- endpoint = mInputs[deviceId];
+ for (AAudioServiceEndpoint *ep : mInputs) {
+ if (ep->matches(configuration)) {
+ endpoint = ep;
+ break;
+ }
+ }
break;
case AAUDIO_DIRECTION_OUTPUT:
- endpoint = mOutputs[deviceId];
+ for (AAudioServiceEndpoint *ep : mOutputs) {
+ if (ep->matches(configuration)) {
+ endpoint = ep;
+ break;
+ }
+ }
break;
default:
assert(false); // There are only two possible directions.
break;
}
ALOGD("AAudioEndpointManager::openEndpoint(), found %p for device = %d, dir = %d",
- endpoint, deviceId, (int)direction);
+ endpoint, configuration.getDeviceId(), (int)direction);
// If we can't find an existing one then open a new one.
if (endpoint == nullptr) {
- if (direction == AAUDIO_DIRECTION_INPUT) {
- AAudioServiceEndpointCapture *capture = new AAudioServiceEndpointCapture(audioService);
- if (capture->open(deviceId) != AAUDIO_OK) {
- ALOGE("AAudioEndpointManager::openEndpoint(), open failed");
- delete capture;
- } else {
- mInputs[deviceId] = capture;
+ // we must call openStream with audioserver identity
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ switch(direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ capture = new AAudioServiceEndpointCapture(audioService);
endpoint = capture;
- }
- } else if (direction == AAUDIO_DIRECTION_OUTPUT) {
- AAudioServiceEndpointPlay *player = new AAudioServiceEndpointPlay(audioService);
- if (player->open(deviceId) != AAUDIO_OK) {
- ALOGE("AAudioEndpointManager::openEndpoint(), open failed");
- delete player;
- } else {
- mOutputs[deviceId] = player;
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ player = new AAudioServiceEndpointPlay(audioService);
endpoint = player;
- }
+ break;
+ default:
+ break;
}
+ if (endpoint != nullptr) {
+ aaudio_result_t result = endpoint->open(configuration);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
+ delete endpoint;
+ endpoint = nullptr;
+ } else {
+ switch(direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ mInputs.push_back(capture);
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ mOutputs.push_back(player);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
+ endpoint, configuration.getDeviceId(), (int)direction);
+ IPCThreadState::self()->restoreCallingIdentity(token);
}
if (endpoint != nullptr) {
+ ALOGD("AAudioEndpointManager::openEndpoint(), sampleRate = %d, framesPerBurst = %d",
+ endpoint->getSampleRate(), endpoint->getFramesPerBurst());
// Increment the reference count under this lock.
endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
}
@@ -95,20 +161,32 @@
// Decrement the reference count under this lock.
int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
serviceEndpoint->setReferenceCount(newRefCount);
+ ALOGD("AAudioEndpointManager::closeEndpoint(%p) newRefCount = %d",
+ serviceEndpoint, newRefCount);
+
+ // If no longer in use then close and delete it.
if (newRefCount <= 0) {
aaudio_direction_t direction = serviceEndpoint->getDirection();
- int32_t deviceId = serviceEndpoint->getDeviceId();
+ // Track endpoints based on requested deviceId because UNSPECIFIED
+ // can change to a specific device after opening.
+ int32_t deviceId = serviceEndpoint->getRequestedDeviceId();
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
- mInputs.erase(deviceId);
+ mInputs.erase(
+ std::remove(mInputs.begin(), mInputs.end(), serviceEndpoint), mInputs.end());
break;
case AAUDIO_DIRECTION_OUTPUT:
- mOutputs.erase(deviceId);
+ mOutputs.erase(
+ std::remove(mOutputs.begin(), mOutputs.end(), serviceEndpoint), mOutputs.end());
+ break;
+ default:
break;
}
serviceEndpoint->close();
+ ALOGD("AAudioEndpointManager::closeEndpoint() delete %p for device %d, dir = %d",
+ serviceEndpoint, deviceId, (int)direction);
delete serviceEndpoint;
}
}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index db1103d..2511b2f 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -34,25 +34,37 @@
~AAudioEndpointManager() = default;
/**
+ * Returns information about the state of the this class.
+ *
+ * Will attempt to get the object lock, but will proceed
+ * even if it cannot.
+ *
+ * Each line of information ends with a newline.
+ *
+ * @return a string with useful information
+ */
+ std::string dump() const;
+
+ /**
* Find a service endpoint for the given deviceId and direction.
- * If an endpoint does not already exist then it will try to create one.
+ * If an endpoint does not already exist then try to create one.
*
* @param deviceId
* @param direction
* @return endpoint or nullptr
*/
AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
- int32_t deviceId,
+ const AAudioStreamConfiguration& configuration,
aaudio_direction_t direction);
void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
private:
- std::mutex mLock;
+ mutable std::mutex mLock;
- std::map<int32_t, AAudioServiceEndpointCapture *> mInputs;
- std::map<int32_t, AAudioServiceEndpointPlay *> mOutputs;
+ std::vector<AAudioServiceEndpointCapture *> mInputs;
+ std::vector<AAudioServiceEndpointPlay *> mOutputs;
};
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 43203d4..952aa82 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -18,9 +18,17 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
#include <cstring>
+#include <utils/Trace.h>
+
#include "AAudioMixer.h"
+#ifndef AAUDIO_MIXER_ATRACE_ENABLED
+#define AAUDIO_MIXER_ATRACE_ENABLED 1
+#endif
+
using android::WrappingBuffer;
using android::FifoBuffer;
using android::fifo_frames_t;
@@ -41,13 +49,28 @@
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
-bool AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+bool AAudioMixer::mix(int trackIndex, FifoBuffer *fifo, float volume) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
fifo_frames_t framesLeft = mFramesPerBurst;
+#if AAUDIO_MIXER_ATRACE_ENABLED
+ ATRACE_BEGIN("aaMix");
+#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
+
// Gather the data from the client. May be in two parts.
- fifo->getFullDataAvailable(&wrappingBuffer);
+ fifo_frames_t fullFrames = fifo->getFullDataAvailable(&wrappingBuffer);
+#if AAUDIO_MIXER_ATRACE_ENABLED
+ if (ATRACE_ENABLED()) {
+ char rdyText[] = "aaMixRdy#";
+ char letter = 'A' + (trackIndex % 26);
+ rdyText[sizeof(rdyText) - 2] = letter;
+ ATRACE_INT(rdyText, fullFrames);
+ }
+#else /* MIXER_ATRACE_ENABLED */
+ (void) trackIndex;
+ (void) fullFrames;
+#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
// Mix data in one or two parts.
int partIndex = 0;
@@ -65,11 +88,15 @@
}
partIndex++;
}
- fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
- if (framesLeft > 0) {
- //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
- // framesLeft, mFramesPerBurst);
- }
+ // Always advance by one burst even if we do not have the data.
+ // Otherwise the stream timing will drift whenever there is an underflow.
+ // This actual underflow can then be detected by the client for XRun counting.
+ fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst);
+
+#if AAUDIO_MIXER_ATRACE_ENABLED
+ ATRACE_END();
+#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
+
return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
}
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 9155fec..a8090bc 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -37,7 +37,7 @@
* @param volume
* @return true if underflowed
*/
- bool mix(android::FifoBuffer *fifo, float volume);
+ bool mix(int trackIndex, android::FifoBuffer *fifo, float volume);
void mixPart(float *destination, float *source, int32_t numFrames, float volume);
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index c9b9065..3992719 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <sstream>
//#include <time.h>
//#include <pthread.h>
@@ -26,16 +27,21 @@
#include <utils/String16.h>
#include "binding/AAudioServiceMessage.h"
+#include "AAudioClientTracker.h"
+#include "AAudioEndpointManager.h"
#include "AAudioService.h"
#include "AAudioServiceStreamMMAP.h"
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceStreamMMAP.h"
#include "binding/IAAudioService.h"
+#include "ServiceUtilities.h"
#include "utility/HandleTracker.h"
using namespace android;
using namespace aaudio;
+#define MAX_STREAMS_PER_PROCESS 8
+
typedef enum
{
AAUDIO_HANDLE_TYPE_STREAM
@@ -44,32 +50,76 @@
android::AAudioService::AAudioService()
: BnAAudioService() {
+ mAudioClient.clientUid = getuid(); // TODO consider using geteuid()
+ mAudioClient.clientPid = getpid();
+ mAudioClient.packageName = String16("");
+ AAudioClientTracker::getInstance().setAAudioService(this);
}
AAudioService::~AAudioService() {
}
+status_t AAudioService::dump(int fd, const Vector<String16>& args) {
+ std::string result;
+
+ if (!dumpAllowed()) {
+ std::stringstream ss;
+ ss << "Permission denial: can't dump AAudioService from pid="
+ << IPCThreadState::self()->getCallingPid() << ", uid="
+ << IPCThreadState::self()->getCallingUid() << "\n";
+ result = ss.str();
+ ALOGW("%s", result.c_str());
+ } else {
+ result = mHandleTracker.dump()
+ + AAudioClientTracker::getInstance().dump()
+ + AAudioEndpointManager::getInstance().dump();
+ }
+ (void)write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+}
+
+void AAudioService::registerClient(const sp<IAAudioClient>& client) {
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ AAudioClientTracker::getInstance().registerClient(pid, client);
+}
+
aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
aaudio_result_t result = AAUDIO_OK;
- AAudioServiceStreamBase *serviceStream = nullptr;
+ sp<AAudioServiceStreamBase> serviceStream;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
+ // Enforce limit on client processes.
+ pid_t pid = request.getProcessId();
+ if (pid != mAudioClient.clientPid) {
+ int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
+ if (count >= MAX_STREAMS_PER_PROCESS) {
+ ALOGE("AAudioService::openStream(): exceeded max streams per process %d >= %d",
+ count, MAX_STREAMS_PER_PROCESS);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+ }
+
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
- serviceStream = new AAudioServiceStreamMMAP();
+ // only trust audioserver for in service indication
+ bool inService = false;
+ if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
+ mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
+ inService = request.isInService();
+ }
+ serviceStream = new AAudioServiceStreamMMAP(mAudioClient, inService);
result = serviceStream->open(request, configurationOutput);
if (result != AAUDIO_OK) {
// fall back to using a shared stream
- ALOGD("AAudioService::openStream(), EXCLUSIVE mode failed");
- delete serviceStream;
- serviceStream = nullptr;
+ ALOGW("AAudioService::openStream(), could not open in EXCLUSIVE mode");
+ serviceStream.clear();
} else {
configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
}
@@ -84,28 +134,43 @@
}
if (result != AAUDIO_OK) {
- delete serviceStream;
- ALOGE("AAudioService::openStream(): failed, return %d", result);
+ serviceStream.clear();
+ ALOGE("AAudioService::openStream(): failed, return %d = %s",
+ result, AAudio_convertResultToText(result));
return result;
} else {
- aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream);
- ALOGV("AAudioService::openStream(): handle = 0x%08X", handle);
+ aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream.get());
if (handle < 0) {
ALOGE("AAudioService::openStream(): handle table full");
- delete serviceStream;
+ serviceStream->close();
+ serviceStream.clear();
+ } else {
+ ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
+ serviceStream->setHandle(handle);
+ pid_t pid = request.getProcessId();
+ AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
}
return handle;
}
}
aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
- AAudioServiceStreamBase *serviceStream = (AAudioServiceStreamBase *)
+ // Check permission and ownership first.
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+
+ ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
+ // Remove handle from tracker so that we cannot look up the raw address any more.
+ serviceStream = (AAudioServiceStreamBase *)
mHandleTracker.remove(AAUDIO_HANDLE_TYPE_STREAM,
streamHandle);
- ALOGV("AAudioService.closeStream(0x%08X)", streamHandle);
if (serviceStream != nullptr) {
serviceStream->close();
- delete serviceStream;
+ pid_t pid = serviceStream->getOwnerProcessId();
+ AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
return AAUDIO_OK;
}
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -113,8 +178,23 @@
AAudioServiceStreamBase *AAudioService::convertHandleToServiceStream(
aaudio_handle_t streamHandle) const {
- return (AAudioServiceStreamBase *) mHandleTracker.get(AAUDIO_HANDLE_TYPE_STREAM,
- (aaudio_handle_t)streamHandle);
+ AAudioServiceStreamBase *serviceStream = (AAudioServiceStreamBase *)
+ mHandleTracker.get(AAUDIO_HANDLE_TYPE_STREAM, (aaudio_handle_t)streamHandle);
+ if (serviceStream != nullptr) {
+ // Only allow owner or the aaudio service to access the stream.
+ const uid_t callingUserId = IPCThreadState::self()->getCallingUid();
+ const uid_t ownerUserId = serviceStream->getOwnerUserId();
+ bool callerOwnsIt = callingUserId == ownerUserId;
+ bool serverCalling = callingUserId == mAudioClient.clientUid;
+ bool serverOwnsIt = ownerUserId == mAudioClient.clientUid;
+ bool allowed = callerOwnsIt || serverCalling || serverOwnsIt;
+ if (!allowed) {
+ ALOGE("AAudioService: calling uid %d cannot access stream 0x%08X owned by %d",
+ callingUserId, streamHandle, ownerUserId);
+ serviceStream = nullptr;
+ }
+ }
+ return serviceStream;
}
aaudio_result_t AAudioService::getStreamDescription(
@@ -136,6 +216,7 @@
ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
+
aaudio_result_t result = serviceStream->start();
return result;
}
@@ -170,9 +251,8 @@
}
aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
- pid_t clientThreadId,
- int64_t periodNanoseconds) {
+ pid_t clientThreadId,
+ int64_t periodNanoseconds) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream == nullptr) {
ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
@@ -182,12 +262,14 @@
ALOGE("AAudioService::registerAudioThread(), thread already registered");
return AAUDIO_ERROR_INVALID_STATE;
}
+
+ const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
serviceStream->setRegisteredThread(clientThreadId);
- int err = android::requestPriority(clientProcessId, clientThreadId,
+ int err = android::requestPriority(ownerPid, clientThreadId,
DEFAULT_AUDIO_PRIORITY, true /* isForApp */);
if (err != 0){
- ALOGE("AAudioService::registerAudioThread() failed, errno = %d, priority = %d",
- errno, DEFAULT_AUDIO_PRIORITY);
+ ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d",
+ clientThreadId, errno, DEFAULT_AUDIO_PRIORITY);
return AAUDIO_ERROR_INTERNAL;
} else {
return AAUDIO_OK;
@@ -195,7 +277,6 @@
}
aaudio_result_t AAudioService::unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientProcessId,
pid_t clientThreadId) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream == nullptr) {
@@ -210,3 +291,26 @@
serviceStream->setRegisteredThread(0);
return AAUDIO_OK;
}
+
+aaudio_result_t AAudioService::startClient(aaudio_handle_t streamHandle,
+ const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::startClient(), illegal stream handle = 0x%0x",
+ streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+ return serviceStream->startClient(client, clientHandle);
+}
+
+aaudio_result_t AAudioService::stopClient(aaudio_handle_t streamHandle,
+ audio_port_handle_t clientHandle) {
+ AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::stopClient(), illegal stream handle = 0x%0x",
+ streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+ return serviceStream->stopClient(clientHandle);
+}
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index f5a7d2f..8421efc 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <binder/BinderService.h>
+#include <media/AudioClient.h>
#include <aaudio/AAudio.h>
#include "utility/HandleTracker.h"
@@ -44,8 +45,12 @@
static const char* getServiceName() { return AAUDIO_SERVICE_NAME; }
+ virtual status_t dump(int fd, const Vector<String16>& args) override;
+
+ virtual void registerClient(const sp<IAAudioClient>& client);
+
virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configuration);
+ aaudio::AAudioStreamConfiguration &configurationOutput);
virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle);
@@ -62,11 +67,18 @@
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t pid, pid_t tid,
- int64_t periodNanoseconds) ;
+ pid_t tid,
+ int64_t periodNanoseconds) ;
virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t pid, pid_t tid);
+ pid_t tid);
+
+ virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
+ const android::AudioClient& client,
+ audio_port_handle_t *clientHandle);
+
+ virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
+ audio_port_handle_t clientHandle);
private:
@@ -74,6 +86,8 @@
HandleTracker mHandleTracker;
+ android::AudioClient mAudioClient;
+
enum constants {
DEFAULT_AUDIO_PRIORITY = 2
};
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index d8ae284..0f863fe 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceEndpoint"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <algorithm>
#include <assert.h>
#include <map>
#include <mutex>
+#include <sstream>
+#include <vector>
+
#include <utils/Singleton.h>
#include "AAudioEndpointManager.h"
#include "AAudioServiceEndpoint.h"
-#include <algorithm>
-#include <mutex>
-#include <vector>
#include "core/AudioStreamBuilder.h"
#include "AAudioServiceEndpoint.h"
@@ -44,15 +45,48 @@
// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+std::string AAudioServiceEndpoint::dump() const {
+ std::stringstream result;
+
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mLockStreams.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "EndpointManager may be deadlocked\n";
+ }
+
+ AudioStreamInternal *stream = mStreamInternal;
+ if (stream == nullptr) {
+ result << "null stream!" << "\n";
+ } else {
+ result << "mmap stream: rate = " << stream->getSampleRate() << "\n";
+ }
+
+ result << " Registered Streams:" << "\n";
+ for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+ result << sharedStream->dump();
+ }
+
+ if (isLocked) {
+ mLockStreams.unlock();
+ }
+ return result.str();
+}
+
// Set up an EXCLUSIVE MMAP stream that will be shared.
-aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId) {
+aaudio_result_t AAudioServiceEndpoint::open(const AAudioStreamConfiguration& configuration) {
+ mRequestedDeviceId = configuration.getDeviceId();
mStreamInternal = getStreamInternal();
AudioStreamBuilder builder;
builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
// Don't fall back to SHARED because that would cause recursion.
builder.setSharingModeMatchRequired(true);
- builder.setDeviceId(deviceId);
+ builder.setDeviceId(mRequestedDeviceId);
+ builder.setFormat(configuration.getFormat());
+ builder.setSampleRate(configuration.getSampleRate());
+ builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
builder.setDirection(getDirection());
builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
@@ -60,45 +94,39 @@
}
aaudio_result_t AAudioServiceEndpoint::close() {
- return getStreamInternal()->close();
+ return getStreamInternal()->close();
}
// TODO, maybe use an interface to reduce exposure
-aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamShared>sharedStream) {
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.push_back(sharedStream);
return AAUDIO_OK;
}
-aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamShared>sharedStream) {
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
mRegisteredStreams.end());
return AAUDIO_OK;
}
-aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
- std::lock_guard<std::mutex> lock(mLockStreams);
- mRunningStreams.push_back(sharedStream);
- if (mRunningStreams.size() == 1) {
+aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
+ aaudio_result_t result = AAUDIO_OK;
+ if (++mRunningStreams == 1) {
+ // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+ std::lock_guard<std::mutex> lock(mLockStreams);
+ result = getStreamInternal()->requestStart();
startSharingThread_l();
}
- return AAUDIO_OK;
+ return result;
}
-aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
- int numRunningStreams = 0;
- {
- std::lock_guard<std::mutex> lock(mLockStreams);
- mRunningStreams.erase(
- std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
- mRunningStreams.end());
- numRunningStreams = mRunningStreams.size();
- }
- if (numRunningStreams == 0) {
- // Don't call this under a lock because the callbackLoop also uses the lock.
+aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
+ // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+ if (--mRunningStreams == 0) { // atomic
stopSharingThread();
+ getStreamInternal()->requestStop();
}
return AAUDIO_OK;
}
@@ -129,12 +157,27 @@
void AAudioServiceEndpoint::disconnectRegisteredStreams() {
std::lock_guard<std::mutex> lock(mLockStreams);
- for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
- sharedStream->onStop();
- }
- mRunningStreams.clear();
- for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
- sharedStream->onDisconnect();
+ for(auto sharedStream : mRegisteredStreams) {
+ sharedStream->stop();
+ sharedStream->disconnect();
}
mRegisteredStreams.clear();
}
+
+bool AAudioServiceEndpoint::matches(const AAudioStreamConfiguration& configuration) {
+ if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
+ configuration.getDeviceId() != mStreamInternal->getDeviceId()) {
+ return false;
+ }
+ if (configuration.getSampleRate() != AAUDIO_UNSPECIFIED &&
+ configuration.getSampleRate() != mStreamInternal->getSampleRate()) {
+ return false;
+ }
+ if (configuration.getSamplesPerFrame() != AAUDIO_UNSPECIFIED &&
+ configuration.getSamplesPerFrame() != mStreamInternal->getSamplesPerFrame()) {
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 50bf049..e40a670 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -36,18 +36,21 @@
public:
virtual ~AAudioServiceEndpoint() = default;
- virtual aaudio_result_t open(int32_t deviceId);
+ std::string dump() const;
+
+ virtual aaudio_result_t open(const AAudioStreamConfiguration& configuration);
int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame(); }
int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst(); }
- aaudio_result_t registerStream(AAudioServiceStreamShared *sharedStream);
- aaudio_result_t unregisterStream(AAudioServiceStreamShared *sharedStream);
- aaudio_result_t startStream(AAudioServiceStreamShared *sharedStream);
- aaudio_result_t stopStream(AAudioServiceStreamShared *sharedStream);
+ aaudio_result_t registerStream(android::sp<AAudioServiceStreamShared> sharedStream);
+ aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamShared> sharedStream);
+ aaudio_result_t startStream(android::sp<AAudioServiceStreamShared> sharedStream);
+ aaudio_result_t stopStream(android::sp<AAudioServiceStreamShared> sharedStream);
aaudio_result_t close();
+ int32_t getRequestedDeviceId() const { return mRequestedDeviceId; }
int32_t getDeviceId() const { return mStreamInternal->getDeviceId(); }
aaudio_direction_t getDirection() const { return mStreamInternal->getDirection(); }
@@ -66,14 +69,17 @@
mReferenceCount = count;
}
+ bool matches(const AAudioStreamConfiguration& configuration);
+
virtual AudioStreamInternal *getStreamInternal() = 0;
- std::atomic<bool> mCallbackEnabled;
+ std::atomic<bool> mCallbackEnabled{false};
- std::mutex mLockStreams;
+ mutable std::mutex mLockStreams;
- std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
- std::vector<AAudioServiceStreamShared *> mRunningStreams;
+ std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
+
+ std::atomic<int> mRunningStreams{0};
private:
aaudio_result_t startSharingThread_l();
@@ -81,6 +87,7 @@
AudioStreamInternal *mStreamInternal = nullptr;
int32_t mReferenceCount = 0;
+ int32_t mRequestedDeviceId = 0;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 29d6cb9..6a37330 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -42,8 +42,8 @@
delete mDistributionBuffer;
}
-aaudio_result_t AAudioServiceEndpointCapture::open(int32_t deviceId) {
- aaudio_result_t result = AAudioServiceEndpoint::open(deviceId);
+aaudio_result_t AAudioServiceEndpointCapture::open(const AAudioStreamConfiguration& configuration) {
+ aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
if (result == AAUDIO_OK) {
delete mDistributionBuffer;
int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
@@ -57,9 +57,7 @@
void *AAudioServiceEndpointCapture::callbackLoop() {
ALOGD("AAudioServiceEndpointCapture(): callbackLoop() entering");
int32_t underflowCount = 0;
-
- aaudio_result_t result = getStreamInternal()->requestStart();
-
+ aaudio_result_t result = AAUDIO_OK;
int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
// result might be a frame count
@@ -78,21 +76,21 @@
// Distribute data to each active stream.
{ // use lock guard
std::lock_guard <std::mutex> lock(mLockStreams);
- for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
- if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
- getFramesPerBurst()) {
- underflowCount++;
- } else {
- fifo->write(mDistributionBuffer, getFramesPerBurst());
+ for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+ if (sharedStream->isRunning()) {
+ FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
+ getFramesPerBurst()) {
+ underflowCount++;
+ } else {
+ fifo->write(mDistributionBuffer, getFramesPerBurst());
+ }
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
}
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
}
}
}
- result = getStreamInternal()->requestStop();
-
ALOGD("AAudioServiceEndpointCapture(): callbackLoop() exiting, %d underflows", underflowCount);
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 35857d1..8a3d72f 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -27,7 +27,7 @@
explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
virtual ~AAudioServiceEndpointCapture();
- aaudio_result_t open(int32_t deviceId) override;
+ aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
AudioStreamInternal *getStreamInternal() override {
return &mStreamInternalCapture;
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index cc09cc3..b83b918 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -46,8 +46,8 @@
AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
}
-aaudio_result_t AAudioServiceEndpointPlay::open(int32_t deviceId) {
- aaudio_result_t result = AAudioServiceEndpoint::open(deviceId);
+aaudio_result_t AAudioServiceEndpointPlay::open(const AAudioStreamConfiguration& configuration) {
+ aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
if (result == AAUDIO_OK) {
mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
getStreamInternal()->getFramesPerBurst());
@@ -57,7 +57,6 @@
mLatencyTuningEnabled = true;
burstsPerBuffer = BURSTS_PER_BUFFER_DEFAULT;
}
- ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
int32_t desiredBufferSize = burstsPerBuffer * getStreamInternal()->getFramesPerBurst();
getStreamInternal()->setBufferSize(desiredBufferSize);
}
@@ -66,11 +65,8 @@
// Mix data from each application stream and write result to the shared MMAP stream.
void *AAudioServiceEndpointPlay::callbackLoop() {
- ALOGD("AAudioServiceEndpointPlay(): callbackLoop() entering");
int32_t underflowCount = 0;
-
- aaudio_result_t result = getStreamInternal()->requestStart();
-
+ aaudio_result_t result = AAUDIO_OK;
int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
// result might be a frame count
@@ -78,14 +74,18 @@
// Mix data from each active stream.
mMixer.clear();
{ // use lock guard
+ int index = 0;
std::lock_guard <std::mutex> lock(mLockStreams);
- for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
- float volume = 0.5; // TODO get from system
- bool underflowed = mMixer.mix(fifo, volume);
- underflowCount += underflowed ? 1 : 0;
- // TODO log underflows in each stream
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
+ for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+ if (sharedStream->isRunning()) {
+ FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ float volume = 1.0; // to match legacy volume
+ bool underflowed = mMixer.mix(index, fifo, volume);
+ underflowCount += underflowed ? 1 : 0;
+ // TODO log underflows in each stream
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
+ }
+ index++;
}
}
@@ -102,8 +102,8 @@
}
}
- result = getStreamInternal()->requestStop();
+ ALOGW_IF((underflowCount > 0),
+ "AAudioServiceEndpointPlay(): callbackLoop() had %d underflows", underflowCount);
- ALOGD("AAudioServiceEndpointPlay(): callbackLoop() exiting, %d underflows", underflowCount);
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h
index b977960..c22f510 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.h
+++ b/services/oboeservice/AAudioServiceEndpointPlay.h
@@ -32,12 +32,15 @@
namespace aaudio {
+/**
+ * Contains a mixer and a stream for writing the result of the mix.
+ */
class AAudioServiceEndpointPlay : public AAudioServiceEndpoint {
public:
explicit AAudioServiceEndpointPlay(android::AAudioService &audioService);
virtual ~AAudioServiceEndpointPlay();
- aaudio_result_t open(int32_t deviceId) override;
+ aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
AudioStreamInternal *getStreamInternal() override {
return &mStreamInternalPlay;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 8f0abc2..5f7d179 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamBase"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -38,14 +38,42 @@
AAudioServiceStreamBase::AAudioServiceStreamBase()
: mUpMessageQueue(nullptr)
, mAAudioThread() {
+ mMmapClient.clientUid = -1;
+ mMmapClient.clientPid = -1;
+ mMmapClient.packageName = String16("");
}
AAudioServiceStreamBase::~AAudioServiceStreamBase() {
- close();
+ ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroying %p", this);
+ // If the stream is deleted when OPEN or in use then audio resources will leak.
+ // This would indicate an internal error. So we want to find this ASAP.
+ LOG_ALWAYS_FATAL_IF(!(mState == AAUDIO_STREAM_STATE_CLOSED
+ || mState == AAUDIO_STREAM_STATE_UNINITIALIZED
+ || mState == AAUDIO_STREAM_STATE_DISCONNECTED),
+ "service stream still open, state = %d", mState);
+}
+
+std::string AAudioServiceStreamBase::dump() const {
+ std::stringstream result;
+
+ result << " -------- handle = 0x" << std::hex << mHandle << std::dec << "\n";
+ result << " state = " << AAudio_convertStreamStateToText(mState) << "\n";
+ result << " format = " << mAudioFormat << "\n";
+ result << " framesPerBurst = " << mFramesPerBurst << "\n";
+ result << " channelCount = " << mSamplesPerFrame << "\n";
+ result << " capacityFrames = " << mCapacityInFrames << "\n";
+ result << " owner uid = " << mMmapClient.clientUid << "\n";
+
+ return result.str();
}
aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
+
+ mMmapClient.clientUid = request.getUserId();
+ mMmapClient.clientPid = request.getProcessId();
+ mMmapClient.packageName.setTo(String16("")); // FIXME what should we do here?
+
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
if (mUpMessageQueue != nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
@@ -56,14 +84,20 @@
}
aaudio_result_t AAudioServiceStreamBase::close() {
- std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
- delete mUpMessageQueue;
- mUpMessageQueue = nullptr;
-
+ if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+ stopTimestampThread();
+ std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+ delete mUpMessageQueue;
+ mUpMessageQueue = nullptr;
+ mState = AAUDIO_STREAM_STATE_CLOSED;
+ }
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceStreamBase::start() {
+ if (isRunning()) {
+ return AAUDIO_OK;
+ }
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
mState = AAUDIO_STREAM_STATE_STARTED;
mThreadEnabled.store(true);
@@ -71,12 +105,15 @@
}
aaudio_result_t AAudioServiceStreamBase::pause() {
-
+ aaudio_result_t result = AAUDIO_OK;
+ if (!isRunning()) {
+ return result;
+ }
sendCurrentTimestamp();
mThreadEnabled.store(false);
- aaudio_result_t result = mAAudioThread.stop();
+ result = mAAudioThread.stop();
if (result != AAUDIO_OK) {
- processError();
+ disconnect();
return result;
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
@@ -85,12 +122,15 @@
}
aaudio_result_t AAudioServiceStreamBase::stop() {
+ aaudio_result_t result = AAUDIO_OK;
+ if (!isRunning()) {
+ return result;
+ }
// TODO wait for data to be played out
- sendCurrentTimestamp();
- mThreadEnabled.store(false);
- aaudio_result_t result = mAAudioThread.stop();
+ sendCurrentTimestamp(); // warning - this calls a virtual function
+ result = stopTimestampThread();
if (result != AAUDIO_OK) {
- processError();
+ disconnect();
return result;
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
@@ -98,6 +138,15 @@
return result;
}
+aaudio_result_t AAudioServiceStreamBase::stopTimestampThread() {
+ aaudio_result_t result = AAUDIO_OK;
+ // clear flag that tells thread to loop
+ if (mThreadEnabled.exchange(false)) {
+ result = mAAudioThread.stop();
+ }
+ return result;
+}
+
aaudio_result_t AAudioServiceStreamBase::flush() {
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
mState = AAUDIO_STREAM_STATE_FLUSHED;
@@ -120,14 +169,18 @@
nextTime = timestampScheduler.nextAbsoluteTime();
} else {
// Sleep until it is time to send the next timestamp.
+ // TODO Wait for a signal with a timeout so that we can stop more quickly.
AudioClock::sleepUntilNanoTime(nextTime);
}
}
ALOGD("AAudioServiceStreamBase::run() exiting ----------------");
}
-void AAudioServiceStreamBase::processError() {
- sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
+void AAudioServiceStreamBase::disconnect() {
+ if (mState != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
+ mState = AAUDIO_STREAM_STATE_DISCONNECTED;
+ }
}
aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
@@ -166,6 +219,8 @@
// (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
+ } else if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ result = AAUDIO_OK; // just not available yet, try again later
}
return result;
}
@@ -179,4 +234,4 @@
mUpMessageQueue->fillParcelable(parcelable,
parcelable.mUpMessageQueueParcelable);
return getDownDataDescription(parcelable);
-}
\ No newline at end of file
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index ee52c39..cebefec 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,11 +20,14 @@
#include <assert.h>
#include <mutex>
+#include <utils/RefBase.h>
+
#include "fifo/FifoBuffer.h"
#include "binding/IAAudioService.h"
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceMessage.h"
#include "utility/AAudioUtilities.h"
+#include <media/AudioClient.h>
#include "SharedRingBuffer.h"
#include "AAudioThread.h"
@@ -39,7 +42,8 @@
* Base class for a stream in the AAudio service.
*/
class AAudioServiceStreamBase
- : public Runnable {
+ : public virtual android::RefBase
+ , public Runnable {
public:
AAudioServiceStreamBase();
@@ -49,6 +53,8 @@
ILLEGAL_THREAD_ID = 0
};
+ std::string dump() const;
+
// -------------------------------------------------------------------
/**
* Open the device.
@@ -73,11 +79,26 @@
*/
virtual aaudio_result_t stop();
+ aaudio_result_t stopTimestampThread();
+
/**
* Discard any data held by the underlying HAL or Service.
*/
virtual aaudio_result_t flush();
+ virtual aaudio_result_t startClient(const android::AudioClient& client __unused,
+ audio_port_handle_t *clientHandle __unused) {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle __unused) {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ bool isRunning() const {
+ return mState == AAUDIO_STREAM_STATE_STARTED;
+ }
+
// -------------------------------------------------------------------
/**
@@ -111,33 +132,67 @@
void run() override; // to implement Runnable
- void processError();
+ void disconnect();
+
+ uid_t getOwnerUserId() const {
+ return mMmapClient.clientUid;
+ }
+
+ pid_t getOwnerProcessId() const {
+ return mMmapClient.clientPid;
+ }
+
+ aaudio_handle_t getHandle() const {
+ return mHandle;
+ }
+ void setHandle(aaudio_handle_t handle) {
+ mHandle = handle;
+ }
+
+ aaudio_stream_state_t getState() const {
+ return mState;
+ }
protected:
+
+ void setState(aaudio_stream_state_t state) {
+ mState = state;
+ }
+
aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
aaudio_result_t sendCurrentTimestamp();
+ /**
+ * @param positionFrames
+ * @param timeNanos
+ * @return AAUDIO_OK or AAUDIO_ERROR_UNAVAILABLE or other negative error
+ */
virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
- aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+ aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
- pid_t mRegisteredClientThread = ILLEGAL_THREAD_ID;
+ pid_t mRegisteredClientThread = ILLEGAL_THREAD_ID;
- SharedRingBuffer* mUpMessageQueue;
- std::mutex mLockUpMessageQueue;
+ SharedRingBuffer* mUpMessageQueue;
+ std::mutex mLockUpMessageQueue;
- AAudioThread mAAudioThread;
+ AAudioThread mAAudioThread;
// This is used by one thread to tell another thread to exit. So it must be atomic.
- std::atomic<bool> mThreadEnabled;
+ std::atomic<bool> mThreadEnabled;
- aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
- int32_t mFramesPerBurst = 0;
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mCapacityInFrames = AAUDIO_UNSPECIFIED;
+ aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ int32_t mFramesPerBurst = 0;
+ int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mCapacityInFrames = AAUDIO_UNSPECIFIED;
+ android::AudioClient mMmapClient;
+ audio_port_handle_t mClientHandle = AUDIO_PORT_HANDLE_NONE;
+
+private:
+ aaudio_handle_t mHandle = -1;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamExclusive.h b/services/oboeservice/AAudioServiceStreamExclusive.h
deleted file mode 100644
index db382a3..0000000
--- a/services/oboeservice/AAudioServiceStreamExclusive.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 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 AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
-#define AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
-
-#include "AAudioServiceStreamMMAP.h"
-
-namespace aaudio {
-
-/**
- * Exclusive mode stream in the AAudio service.
- *
- * This is currently a stub.
- * We may move code from AAudioServiceStreamMMAP into this class.
- * If not, then it will be removed.
- */
-class AAudioServiceStreamExclusive : public AAudioServiceStreamMMAP {
-
-public:
- AAudioServiceStreamExclusive() {};
- virtual ~AAudioServiceStreamExclusive() = default;
-};
-
-} /* namespace aaudio */
-
-#endif //AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 97b9937..08dd680 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamMMAP"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -31,6 +31,7 @@
#include "SharedMemoryProxy.h"
#include "utility/AAudioUtilities.h"
+using android::base::unique_fd;
using namespace android;
using namespace aaudio;
@@ -38,30 +39,30 @@
#define AAUDIO_SAMPLE_RATE_DEFAULT 48000
/**
- * Stream that uses an MMAP buffer.
+ * Service Stream that uses an MMAP buffer.
*/
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP()
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(const android::AudioClient& serviceClient,
+ bool inService)
: AAudioServiceStreamBase()
, mMmapStreamCallback(new MyMmapStreamCallback(*this))
, mPreviousFrameCounter(0)
- , mMmapStream(nullptr) {
-}
-
-AAudioServiceStreamMMAP::~AAudioServiceStreamMMAP() {
- close();
+ , mMmapStream(nullptr)
+ , mServiceClient(serviceClient)
+ , mInService(inService) {
}
aaudio_result_t AAudioServiceStreamMMAP::close() {
- mMmapStream.clear(); // TODO review. Is that all we have to do?
- // Apparently the above close is asynchronous. An attempt to open a new device
- // right after a close can fail. Also some callbacks may still be in flight!
- // FIXME Make closing synchronous.
- AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
-
- if (mAudioDataFileDescriptor != -1) {
- ::close(mAudioDataFileDescriptor);
- mAudioDataFileDescriptor = -1;
+ if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ return AAUDIO_OK;
+ }
+ stop();
+ if (mMmapStream != 0) {
+ mMmapStream.clear(); // TODO review. Is that all we have to do?
+ // Apparently the above close is asynchronous. An attempt to open a new device
+ // right after a close can fail. Also some callbacks may still be in flight!
+ // FIXME Make closing synchronous.
+ AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
}
return AAudioServiceStreamBase::close();
@@ -87,13 +88,10 @@
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
audio_port_handle_t deviceId = configurationInput.getDeviceId();
-
- mMmapClient.clientUid = request.getUserId();
- mMmapClient.clientPid = request.getProcessId();
aaudio_direction_t direction = request.getDirection();
// Fill in config
- aaudio_format_t aaudioFormat = configurationInput.getAudioFormat();
+ aaudio_format_t aaudioFormat = configurationInput.getFormat();
if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
aaudioFormat = AAUDIO_FORMAT_PCM_I16;
}
@@ -120,8 +118,6 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
- mMmapClient.packageName.setTo(String16("aaudio_service")); // FIXME what should we do here?
-
MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
@@ -132,27 +128,51 @@
mMmapClient,
&deviceId,
mMmapStreamCallback,
- mMmapStream);
+ mMmapStream,
+ &mPortHandle);
if (status != OK) {
ALOGE("openMmapStream returned status %d", status);
return AAUDIO_ERROR_UNAVAILABLE;
}
+ if (deviceId == AAUDIO_UNSPECIFIED) {
+ ALOGW("AAudioServiceStreamMMAP::open() - openMmapStream() failed to set deviceId");
+ }
+
// Create MMAP/NOIRQ buffer.
int32_t minSizeFrames = configurationInput.getBufferCapacity();
- if (minSizeFrames == 0) { // zero will get rejected
+ if (minSizeFrames <= 0) { // zero will get rejected
minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
}
status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
if (status != OK) {
- ALOGE("%s: createMmapBuffer() returned status %d, return AAUDIO_ERROR_UNAVAILABLE",
- __FILE__, status);
+ ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d",
+ status);
return AAUDIO_ERROR_UNAVAILABLE;
} else {
- ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d",
- status, mMmapBufferinfo.shared_memory_address,
- mMmapBufferinfo.buffer_size_frames,
- mMmapBufferinfo.burst_size_frames);
+ ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
+ ", Sharable FD: %s",
+ status,
+ abs(mMmapBufferinfo.buffer_size_frames),
+ mMmapBufferinfo.burst_size_frames,
+ mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
+ }
+
+ mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
+ // FIXME: the audio HAL indicates if the shared memory fd can be shared outside of audioserver
+ // by returning a negative buffer size
+ if (mCapacityInFrames < 0) {
+ // Exclusive mode is possible from any client
+ mCapacityInFrames = -mCapacityInFrames;
+ } else {
+ // exclusive mode is only possible if the final fd destination is inside audioserver
+ if ((mMmapClient.clientUid != mServiceClient.clientUid) &&
+ configurationInput.getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ // Fallback is handled by caller but indicate what is possible in case
+ // this is used in the future
+ configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
}
// Get information about the stream and pass it back to the caller.
@@ -160,9 +180,14 @@
? audio_channel_count_from_out_mask(config.channel_mask)
: audio_channel_count_from_in_mask(config.channel_mask);
- mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
+ // AAudio creates a copy of this FD and retains ownership of the copy.
+ // Assume that AudioFlinger will close the original shared_memory_fd.
+ mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
+ if (mAudioDataFileDescriptor.get() == -1) {
+ ALOGE("AAudioServiceStreamMMAP::open() - could not dup shared_memory_fd");
+ return AAUDIO_ERROR_INTERNAL; // TODO review
+ }
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
- mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
mSampleRate = config.sample_rate;
@@ -181,12 +206,16 @@
ALOGD("AAudioServiceStreamMMAP::open() original burst = %d, minMicros = %d, final burst = %d\n",
mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+ ALOGD("AAudioServiceStreamMMAP::open() actual rate = %d, channels = %d, deviceId = %d\n",
+ mSampleRate, mSamplesPerFrame, deviceId);
+
// Fill in AAudioStreamConfiguration
configurationOutput.setSampleRate(mSampleRate);
configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
- configurationOutput.setAudioFormat(mAudioFormat);
+ configurationOutput.setFormat(mAudioFormat);
configurationOutput.setDeviceId(deviceId);
+ setState(AAUDIO_STREAM_STATE_OPEN);
return AAUDIO_OK;
}
@@ -194,15 +223,21 @@
* Start the flow of data.
*/
aaudio_result_t AAudioServiceStreamMMAP::start() {
+ if (isRunning()) {
+ return AAUDIO_OK;
+ }
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
aaudio_result_t result;
- status_t status = mMmapStream->start(mMmapClient, &mPortHandle);
+ status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
if (status != OK) {
ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
- processError();
+ disconnect();
result = AAudioConvert_androidToAAudioResult(status);
} else {
result = AAudioServiceStreamBase::start();
+ if (!mInService && result == AAUDIO_OK) {
+ startClient(mMmapClient, &mClientHandle);
+ }
}
return result;
}
@@ -211,18 +246,28 @@
* Stop the flow of data such that start() can resume with loss of data.
*/
aaudio_result_t AAudioServiceStreamMMAP::pause() {
+ if (!isRunning()) {
+ return AAUDIO_OK;
+ }
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-
aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+ if (!mInService) {
+ stopClient(mClientHandle);
+ }
status_t status = mMmapStream->stop(mPortHandle);
mFramesRead.reset32();
return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
}
aaudio_result_t AAudioServiceStreamMMAP::stop() {
+ if (!isRunning()) {
+ return AAUDIO_OK;
+ }
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-
aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+ if (!mInService) {
+ stopClient(mClientHandle);
+ }
aaudio_result_t status = mMmapStream->stop(mPortHandle);
mFramesRead.reset32();
return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
@@ -234,34 +279,43 @@
aaudio_result_t AAudioServiceStreamMMAP::flush() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
// TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
- sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
- mState = AAUDIO_STREAM_STATE_FLUSHED;
return AAudioServiceStreamBase::flush();;
}
+aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ return AAudioConvert_androidToAAudioResult(mMmapStream->start(client, clientHandle));
+}
+
+aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
+ return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+}
aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
struct audio_mmap_position position;
if (mMmapStream == nullptr) {
- processError();
+ disconnect();
return AAUDIO_ERROR_NULL;
}
status_t status = mMmapStream->getMmapPosition(&position);
- if (status != OK) {
- ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status);
- processError();
- return AAudioConvert_androidToAAudioResult(status);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+ if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data yet");
+ } else if (result != AAUDIO_OK) {
+ ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
+ disconnect();
} else {
mFramesRead.update32(position.position_frames);
*positionFrames = mFramesRead.get();
*timeNanos = position.time_nanoseconds;
}
- return AAUDIO_OK;
+ return result;
}
void AAudioServiceStreamMMAP::onTearDown() {
- ALOGE("AAudioServiceStreamMMAP::onTearDown() called - TODO");
+ ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
+ disconnect();
};
void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
@@ -274,11 +328,11 @@
void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
- deviceId, mPortHandle);
- if (mPortHandle > 0 && mPortHandle != deviceId) {
- sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
+ deviceId, mDeviceId);
+ if (mDeviceId != AUDIO_PORT_HANDLE_NONE && mDeviceId != deviceId) {
+ disconnect();
}
- mPortHandle = deviceId;
+ mDeviceId = deviceId;
};
/**
@@ -295,4 +349,4 @@
parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
return AAUDIO_OK;
-}
\ No newline at end of file
+}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index fe75a10..b69dc46 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -19,6 +19,7 @@
#include <atomic>
+#include <android-base/unique_fd.h>
#include <media/audiohal/StreamHalInterface.h>
#include <media/MmapStreamCallback.h>
#include <media/MmapStreamInterface.h>
@@ -33,6 +34,7 @@
#include "TimestampScheduler.h"
#include "utility/MonotonicCounter.h"
+
namespace aaudio {
/**
@@ -43,9 +45,8 @@
, public android::MmapStreamCallback {
public:
- AAudioServiceStreamMMAP();
- virtual ~AAudioServiceStreamMMAP();
-
+ AAudioServiceStreamMMAP(const android::AudioClient& serviceClient, bool inService);
+ virtual ~AAudioServiceStreamMMAP() = default;
aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) override;
@@ -78,6 +79,11 @@
aaudio_result_t close() override;
+ virtual aaudio_result_t startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle);
+
+ virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle);
+
/**
* Send a MMAP/NOIRQ buffer timestamp to the client.
*/
@@ -127,13 +133,15 @@
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
int32_t mPreviousFrameCounter = 0; // from HAL
- int mAudioDataFileDescriptor = -1;
// Interface to the AudioFlinger MMAP support.
android::sp<android::MmapStreamInterface> mMmapStream;
struct audio_mmap_buffer_info mMmapBufferinfo;
- android::MmapStreamInterface::Client mMmapClient;
- audio_port_handle_t mPortHandle = -1; // TODO review best default
+ audio_port_handle_t mPortHandle = AUDIO_PORT_HANDLE_NONE;
+ audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
+ android::AudioClient mServiceClient;
+ bool mInService = false;
+ android::base::unique_fd mAudioDataFileDescriptor;
};
} // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 494b18e..5654113 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceStreamShared"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -34,93 +34,155 @@
using namespace android;
using namespace aaudio;
-#define MIN_BURSTS_PER_BUFFER 2
-#define MAX_BURSTS_PER_BUFFER 32
+#define MIN_BURSTS_PER_BUFFER 2
+#define DEFAULT_BURSTS_PER_BUFFER 16
+// This is an arbitrary range. TODO review.
+#define MAX_FRAMES_PER_BUFFER (32 * 1024)
AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
: mAudioService(audioService)
{
}
-AAudioServiceStreamShared::~AAudioServiceStreamShared() {
- close();
+int32_t AAudioServiceStreamShared::calculateBufferCapacity(int32_t requestedCapacityFrames,
+ int32_t framesPerBurst) {
+
+ if (requestedCapacityFrames > MAX_FRAMES_PER_BUFFER) {
+ ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() requested capacity %d > max %d",
+ requestedCapacityFrames, MAX_FRAMES_PER_BUFFER);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ // Determine how many bursts will fit in the buffer.
+ int32_t numBursts;
+ if (requestedCapacityFrames == AAUDIO_UNSPECIFIED) {
+ // Use fewer bursts if default is too many.
+ if ((DEFAULT_BURSTS_PER_BUFFER * framesPerBurst) > MAX_FRAMES_PER_BUFFER) {
+ numBursts = MAX_FRAMES_PER_BUFFER / framesPerBurst;
+ } else {
+ numBursts = DEFAULT_BURSTS_PER_BUFFER;
+ }
+ } else {
+ // round up to nearest burst boundary
+ numBursts = (requestedCapacityFrames + framesPerBurst - 1) / framesPerBurst;
+ }
+
+ // Clip to bare minimum.
+ if (numBursts < MIN_BURSTS_PER_BUFFER) {
+ numBursts = MIN_BURSTS_PER_BUFFER;
+ }
+ // Check for numeric overflow.
+ if (numBursts > 0x8000 || framesPerBurst > 0x8000) {
+ ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() overflow, capacity = %d * %d",
+ numBursts, framesPerBurst);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ int32_t capacityInFrames = numBursts * framesPerBurst;
+
+ // Final sanity check.
+ if (capacityInFrames > MAX_FRAMES_PER_BUFFER) {
+ ALOGE("AAudioServiceStreamShared::calculateBufferCapacity() calc capacity %d > max %d",
+ capacityInFrames, MAX_FRAMES_PER_BUFFER);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ ALOGD("AAudioServiceStreamShared::calculateBufferCapacity() requested %d frames, actual = %d",
+ requestedCapacityFrames, capacityInFrames);
+ return capacityInFrames;
}
aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
+ sp<AAudioServiceStreamShared> keep(this);
+
aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamBase open returned %d", result);
+ ALOGE("AAudioServiceStreamBase open() returned %d", result);
return result;
}
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
- int32_t deviceId = configurationInput.getDeviceId();
aaudio_direction_t direction = request.getDirection();
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
+ mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, configurationOutput, direction);
if (mServiceEndpoint == nullptr) {
- ALOGE("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
+ ALOGE("AAudioServiceStreamShared::open() mServiceEndPoint = %p", mServiceEndpoint);
return AAUDIO_ERROR_UNAVAILABLE;
}
// Is the request compatible with the shared endpoint?
- mAudioFormat = configurationInput.getAudioFormat();
+ mAudioFormat = configurationInput.getFormat();
if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
} else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
- ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
- return AAUDIO_ERROR_INVALID_FORMAT;
+ ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", mAudioFormat);
+ result = AAUDIO_ERROR_INVALID_FORMAT;
+ goto error;
}
mSampleRate = configurationInput.getSampleRate();
if (mSampleRate == AAUDIO_UNSPECIFIED) {
mSampleRate = mServiceEndpoint->getSampleRate();
} else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
- ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
+ ALOGE("AAudioServiceStreamShared::open() mSampleRate = %d, need %d",
mSampleRate, mServiceEndpoint->getSampleRate());
- return AAUDIO_ERROR_INVALID_RATE;
+ result = AAUDIO_ERROR_INVALID_RATE;
+ goto error;
}
mSamplesPerFrame = configurationInput.getSamplesPerFrame();
if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
} else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
- ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+ ALOGE("AAudioServiceStreamShared::open() mSamplesPerFrame = %d, need %d",
mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
- return AAUDIO_ERROR_OUT_OF_RANGE;
+ result = AAUDIO_ERROR_OUT_OF_RANGE;
+ goto error;
}
- // Determine this stream's shared memory buffer capacity.
mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
- int32_t minCapacityFrames = configurationInput.getBufferCapacity();
- int32_t numBursts = MAX_BURSTS_PER_BUFFER;
- if (minCapacityFrames != AAUDIO_UNSPECIFIED) {
- numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
- if (numBursts < MIN_BURSTS_PER_BUFFER) {
- numBursts = MIN_BURSTS_PER_BUFFER;
- } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
- numBursts = MAX_BURSTS_PER_BUFFER;
- }
+ ALOGD("AAudioServiceStreamShared::open() mSampleRate = %d, mFramesPerBurst = %d",
+ mSampleRate, mFramesPerBurst);
+
+ mCapacityInFrames = calculateBufferCapacity(configurationInput.getBufferCapacity(),
+ mFramesPerBurst);
+ if (mCapacityInFrames < 0) {
+ result = mCapacityInFrames; // negative error code
+ mCapacityInFrames = 0;
+ goto error;
}
- mCapacityInFrames = numBursts * mFramesPerBurst;
- ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
// Create audio data shared memory buffer for client.
mAudioDataQueue = new SharedRingBuffer();
- mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+ result = mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
+ mCapacityInFrames);
+ result = AAUDIO_ERROR_NO_MEMORY;
+ goto error;
+ }
+
+ ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
+ mSampleRate, mSamplesPerFrame, mServiceEndpoint->getDeviceId());
// Fill in configuration for client.
configurationOutput.setSampleRate(mSampleRate);
configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
- configurationOutput.setAudioFormat(mAudioFormat);
- configurationOutput.setDeviceId(deviceId);
+ configurationOutput.setFormat(mAudioFormat);
+ configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
- mServiceEndpoint->registerStream(this);
+ result = mServiceEndpoint->registerStream(keep);
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+ setState(AAUDIO_STREAM_STATE_OPEN);
return AAUDIO_OK;
+
+error:
+ close();
+ return result;
}
/**
@@ -129,6 +191,9 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::start() {
+ if (isRunning()) {
+ return AAUDIO_OK;
+ }
AAudioServiceEndpoint *endpoint = mServiceEndpoint;
if (endpoint == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
@@ -137,11 +202,14 @@
aaudio_result_t result = endpoint->startStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
- processError();
+ disconnect();
} else {
- result = AAudioServiceStreamBase::start();
+ result = endpoint->getStreamInternal()->startClient(mMmapClient, &mClientHandle);
+ if (result == AAUDIO_OK) {
+ result = AAudioServiceStreamBase::start();
+ }
}
- return AAUDIO_OK;
+ return result;
}
/**
@@ -150,29 +218,35 @@
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::pause() {
+ if (!isRunning()) {
+ return AAUDIO_OK;
+ }
AAudioServiceEndpoint *endpoint = mServiceEndpoint;
if (endpoint == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- // Add this stream to the mixer.
+ endpoint->getStreamInternal()->stopClient(mClientHandle);
aaudio_result_t result = endpoint->stopStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
- processError();
+ disconnect(); // TODO should we return or pause Base first?
}
return AAudioServiceStreamBase::pause();
}
aaudio_result_t AAudioServiceStreamShared::stop() {
+ if (!isRunning()) {
+ return AAUDIO_OK;
+ }
AAudioServiceEndpoint *endpoint = mServiceEndpoint;
if (endpoint == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- // Add this stream to the mixer.
+ endpoint->getStreamInternal()->stopClient(mClientHandle);
aaudio_result_t result = endpoint->stopStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
- processError();
+ disconnect();
}
return AAudioServiceStreamBase::stop();
}
@@ -183,22 +257,37 @@
* An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::flush() {
- // TODO make sure we are paused
- // TODO actually flush the data
- return AAudioServiceStreamBase::flush() ;
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+ ALOGE("AAudioServiceStreamShared::flush() stream not paused, state = %s",
+ AAudio_convertStreamStateToText(mState));
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ // Data will get flushed when the client receives the FLUSHED event.
+ return AAudioServiceStreamBase::flush();
}
aaudio_result_t AAudioServiceStreamShared::close() {
- pause();
- // TODO wait for pause() to synchronize
- AAudioServiceEndpoint *endpoint = mServiceEndpoint;
- if (endpoint != nullptr) {
- endpoint->unregisterStream(this);
-
- AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mEndpointManager.closeEndpoint(endpoint);
- mServiceEndpoint = nullptr;
+ if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ return AAUDIO_OK;
}
+
+ stop();
+
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ endpoint->unregisterStream(this);
+
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ mEndpointManager.closeEndpoint(endpoint);
+ mServiceEndpoint = nullptr;
+
if (mAudioDataQueue != nullptr) {
delete mAudioDataQueue;
mAudioDataQueue = nullptr;
@@ -218,14 +307,6 @@
return AAUDIO_OK;
}
-void AAudioServiceStreamShared::onStop() {
-}
-
-void AAudioServiceStreamShared::onDisconnect() {
- mServiceEndpoint->close();
- mServiceEndpoint = nullptr;
-}
-
void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
mMarkedTime = nanoseconds;
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index dfdbbb3..6b67337 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -44,7 +44,7 @@
public:
AAudioServiceStreamShared(android::AAudioService &aAudioService);
- virtual ~AAudioServiceStreamShared();
+ virtual ~AAudioServiceStreamShared() = default;
aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) override;
@@ -87,16 +87,20 @@
*/
void markTransferTime(int64_t nanoseconds);
- void onStop();
-
- void onDisconnect();
-
protected:
aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ /**
+ * @param requestedCapacityFrames
+ * @param framesPerBurst
+ * @return capacity or negative error
+ */
+ static int32_t calculateBufferCapacity(int32_t requestedCapacityFrames,
+ int32_t framesPerBurst);
+
private:
android::AAudioService &mAudioService;
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index b447725..a896a7a 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -25,6 +25,7 @@
$(LIBAAUDIO_SRC_DIR)/utility/HandleTracker.cpp \
SharedMemoryProxy.cpp \
SharedRingBuffer.cpp \
+ AAudioClientTracker.cpp \
AAudioEndpointManager.cpp \
AAudioMixer.cpp \
AAudioService.cpp \
@@ -46,9 +47,11 @@
LOCAL_SHARED_LIBRARIES := \
libaaudio \
libaudioflinger \
+ libaudioclient \
libbinder \
libcutils \
libmediautils \
+ libserviceutility \
libutils \
liblog
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index 6b3fb4c..83b25b3 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -35,11 +35,6 @@
munmap(mSharedMemory, mSharedMemorySizeInBytes);
mSharedMemory = nullptr;
}
- if (mFileDescriptor != -1) {
- ALOGV("SharedRingBuffer: LEAK? close(mFileDescriptor = %d)\n", mFileDescriptor);
- close(mFileDescriptor);
- mFileDescriptor = -1;
- }
}
aaudio_result_t SharedRingBuffer::allocate(fifo_frames_t bytesPerFrame,
@@ -49,17 +44,17 @@
// Create shared memory large enough to hold the data and the read and write counters.
mDataMemorySizeInBytes = bytesPerFrame * capacityInFrames;
mSharedMemorySizeInBytes = mDataMemorySizeInBytes + (2 * (sizeof(fifo_counter_t)));
- mFileDescriptor = ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes);
- ALOGV("SharedRingBuffer::allocate() LEAK? mFileDescriptor = %d\n", mFileDescriptor);
- if (mFileDescriptor < 0) {
+ mFileDescriptor.reset(ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes));
+ if (mFileDescriptor.get() == -1) {
ALOGE("SharedRingBuffer::allocate() ashmem_create_region() failed %d", errno);
return AAUDIO_ERROR_INTERNAL;
}
+ ALOGV("SharedRingBuffer::allocate() mFileDescriptor = %d\n", mFileDescriptor.get());
- int err = ashmem_set_prot_region(mFileDescriptor, PROT_READ|PROT_WRITE); // TODO error handling?
+ int err = ashmem_set_prot_region(mFileDescriptor.get(), PROT_READ|PROT_WRITE); // TODO error handling?
if (err < 0) {
ALOGE("SharedRingBuffer::allocate() ashmem_set_prot_region() failed %d", errno);
- close(mFileDescriptor);
+ mFileDescriptor.reset();
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
@@ -67,10 +62,10 @@
mSharedMemory = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
PROT_READ|PROT_WRITE,
MAP_SHARED,
- mFileDescriptor, 0);
+ mFileDescriptor.get(), 0);
if (mSharedMemory == MAP_FAILED) {
ALOGE("SharedRingBuffer::allocate() mmap() failed %d", errno);
- close(mFileDescriptor);
+ mFileDescriptor.reset();
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
diff --git a/services/oboeservice/SharedRingBuffer.h b/services/oboeservice/SharedRingBuffer.h
index a2c3766..79169bc 100644
--- a/services/oboeservice/SharedRingBuffer.h
+++ b/services/oboeservice/SharedRingBuffer.h
@@ -17,6 +17,7 @@
#ifndef AAUDIO_SHARED_RINGBUFFER_H
#define AAUDIO_SHARED_RINGBUFFER_H
+#include <android-base/unique_fd.h>
#include <stdint.h>
#include <cutils/ashmem.h>
#include <sys/mman.h>
@@ -51,12 +52,12 @@
}
private:
- int mFileDescriptor = -1;
- android::FifoBuffer *mFifoBuffer = nullptr;
- uint8_t *mSharedMemory = nullptr;
- int32_t mSharedMemorySizeInBytes = 0;
- int32_t mDataMemorySizeInBytes = 0;
- android::fifo_frames_t mCapacityInFrames = 0;
+ android::base::unique_fd mFileDescriptor;
+ android::FifoBuffer *mFifoBuffer = nullptr;
+ uint8_t *mSharedMemory = nullptr;
+ int32_t mSharedMemorySizeInBytes = 0;
+ int32_t mDataMemorySizeInBytes = 0;
+ android::fifo_frames_t mCapacityInFrames = 0;
};
} /* namespace aaudio */