Merge "Fix null pointer dereferencing in TWGBP" into oc-mr1-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f3946f0..793cbf4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -79,6 +79,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libaudiopreprocessing.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmediacodecservice.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_xmlparser@1.0.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_soft_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/libstagefright_soft_*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
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/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index de0167a..bc32bbe 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -78,7 +78,7 @@
static bool gWantFrameTime = false; // do we want times on each frame?
static uint32_t gVideoWidth = 0; // default width+height
static uint32_t gVideoHeight = 0;
-static uint32_t gBitRate = 4000000; // 4Mbps
+static uint32_t gBitRate = 20000000; // 20Mbps
static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
// Set by signal handler to stop recording.
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index d7c2e87..d70282b 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -870,7 +870,9 @@
sp<IMemory> mem =
retriever->getFrameAtTime(-1,
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+ MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
+ HAL_PIXEL_FORMAT_RGB_565,
+ false /*metaOnly*/);
if (mem != NULL) {
failed = false;
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 3150e3c..bc37557 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -257,6 +257,11 @@
plugin = hPlugin;
}
);
+
+ if (!hResult.isOk()) {
+ ALOGE("createPlugin remote call failed");
+ }
+
return plugin;
}
@@ -408,8 +413,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;
@@ -424,12 +432,14 @@
closeOpenSessions();
reportMetrics();
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;
}
@@ -486,18 +496,21 @@
return mInitCheck;
}
- Status status = mPlugin->closeSession(toHidlVec(sessionId));
- if (status == Status::OK) {
- DrmSessionManager::Instance()->removeSession(sessionId);
- for (size_t i = 0; i < mOpenSessions.size(); i++) {
- if (mOpenSessions[i] == sessionId) {
- mOpenSessions.removeAt(i);
- break;
+ Return<Status> status = mPlugin->closeSession(toHidlVec(sessionId));
+ if (status.isOk()) {
+ if (status == Status::OK) {
+ DrmSessionManager::Instance()->removeSession(sessionId);
+ for (size_t i = 0; i < mOpenSessions.size(); i++) {
+ if (mOpenSessions[i] == sessionId) {
+ mOpenSessions.removeAt(i);
+ break;
+ }
}
}
+ reportMetrics();
+ return toStatusT(status);
}
- reportMetrics();
- return toStatusT(status);
+ return DEAD_OBJECT;
}
status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
@@ -997,11 +1010,14 @@
Mutex::Autolock autoLock(mLock);
closeOpenSessions();
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/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
index ed050f7..0259a42 100644
--- a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
@@ -21,12 +21,19 @@
namespace clearkeydrm {
bool isClearKeyUUID(const uint8_t uuid[16]) {
- static const uint8_t kClearKeyUUID[16] = {
+ static const uint8_t kCommonPsshBoxUUID[16] = {
0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
};
- return !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
+ // To be used in mpd to specify drm scheme for players
+ static const uint8_t kClearKeyUUID[16] = {
+ 0xE2,0x71,0x9D,0x58,0xA9,0x85,0xB3,0xC9,
+ 0x78,0x1A,0xB0,0x30,0xAF,0x78,0xD3,0x0E
+ };
+
+ return !memcmp(uuid, kCommonPsshBoxUUID, sizeof(kCommonPsshBoxUUID)) ||
+ !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
}
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
index 6a4f8d5..caff393 100644
--- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -136,7 +136,7 @@
AString encodedId;
for (size_t i = 0; i < keyIds.size(); ++i) {
encodedId.clear();
- android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+ android::encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId);
if (i != 0) {
request.append(",");
}
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
index 84ed242..8c49656 100644
--- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -59,7 +59,7 @@
(size_t)requestString.find(kRequestSuffix));
for (size_t i = 0; i < expectedKeys.size(); ++i) {
AString encodedIdAString;
- android::encodeBase64(expectedKeys[i], kKeyIdSize,
+ android::encodeBase64Url(expectedKeys[i], kKeyIdSize,
&encodedIdAString);
String8 encodedId(encodedIdAString.c_str());
encodedId.removeAll(kBase64Padding);
@@ -231,5 +231,4 @@
attemptParseExpectingFailure(initData, kCencMimeType);
}
-
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
index c3b0d84..d9f3ea6 100644
--- a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -284,14 +284,14 @@
"\"keys\":"
"[{"
"\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
- "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+ "\"k\":\"SGVsbG8gRnJpZW5kICE-Pw\""
"\"kty\":\"oct\""
"\"alg\":\"A128KW1\""
"}"
"{"
"\"kty\":\"oct\""
"\"alg\":\"A128KW2\""
- "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"k\":\"SGVsbG8gRnJpZW5kICE_\""
"\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
"}"
"{"
@@ -303,7 +303,7 @@
"{"
"\"alg\":\"A128KW3\""
"\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
- "\"k\":\"R29vZCBkYXkh\""
+ "\"k\":\"SGVsbG8gPz4-IEZyaWVuZCA_Pg\""
"\"kty\":\"oct\""
"}]"
"}");
@@ -313,8 +313,8 @@
EXPECT_TRUE(keys.size() == 3);
const String8 clearKeys[] =
- { String8("Hello Friend!!"), String8("Hello Friend!"),
- String8("Good day!") };
+ { String8("Hello Friend !>?"), String8("Hello Friend !?"),
+ String8("Hello ?>> Friend ?>") };
verifyKeys(keys, clearKeys);
}
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 51050cd..a9d4dd1 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -30,14 +30,41 @@
class VideoFrame
{
public:
- VideoFrame(): mWidth(0), mHeight(0), mDisplayWidth(0), mDisplayHeight(0), mSize(0),
- mRotationAngle(0), mData(0) {}
+ // Construct a VideoFrame object with the specified parameters,
+ // will allocate frame buffer if |allocate| is set to true, will
+ // allocate buffer to hold ICC data if |iccData| and |iccSize|
+ // indicate its presence.
+ VideoFrame(uint32_t width, uint32_t height,
+ uint32_t displayWidth, uint32_t displayHeight,
+ uint32_t angle, uint32_t bpp, bool allocate,
+ const void *iccData, size_t iccSize):
+ mWidth(width), mHeight(height),
+ mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
+ mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width),
+ mSize(0), mIccSize(0), mReserved(0), mData(0), mIccData(0) {
+ if (allocate) {
+ mSize = mRowBytes * mHeight;
+ mData = new uint8_t[mSize];
+ if (mData == NULL) {
+ mSize = 0;
+ }
+ }
+ if (iccData != NULL && iccSize > 0) {
+ mIccSize = iccSize;
+ mIccData = new uint8_t[iccSize];
+ if (mIccData != NULL) {
+ memcpy(mIccData, iccData, iccSize);
+ } else {
+ mIccSize = 0;
+ }
+ }
+ }
+
+ // Deep copy of both the information fields and the frame data
VideoFrame(const VideoFrame& copy) {
- mWidth = copy.mWidth;
- mHeight = copy.mHeight;
- mDisplayWidth = copy.mDisplayWidth;
- mDisplayHeight = copy.mDisplayHeight;
+ copyInfoOnly(copy);
+
mSize = copy.mSize;
mData = NULL; // initialize it first
if (mSize > 0 && copy.mData != NULL) {
@@ -48,26 +75,99 @@
mSize = 0;
}
}
- mRotationAngle = copy.mRotationAngle;
+
+ mIccSize = copy.mIccSize;
+ mIccData = NULL; // initialize it first
+ if (mIccSize > 0 && copy.mIccData != NULL) {
+ mIccData = new uint8_t[mIccSize];
+ if (mIccData != NULL) {
+ memcpy(mIccData, copy.mIccData, mIccSize);
+ } else {
+ mIccSize = 0;
+ }
+ }
}
~VideoFrame() {
if (mData != 0) {
delete[] mData;
}
+ if (mIccData != 0) {
+ delete[] mIccData;
+ }
+ }
+
+ // Copy |copy| to a flattened VideoFrame in IMemory, 'this' must point to
+ // a chunk of memory back by IMemory of size at least getFlattenedSize()
+ // of |copy|.
+ void copyFlattened(const VideoFrame& copy) {
+ copyInfoOnly(copy);
+
+ mSize = copy.mSize;
+ mData = NULL; // initialize it first
+ if (copy.mSize > 0 && copy.mData != NULL) {
+ memcpy(getFlattenedData(), copy.mData, copy.mSize);
+ }
+
+ mIccSize = copy.mIccSize;
+ mIccData = NULL; // initialize it first
+ if (copy.mIccSize > 0 && copy.mIccData != NULL) {
+ memcpy(getFlattenedIccData(), copy.mIccData, copy.mIccSize);
+ }
+ }
+
+ // Calculate the flattened size to put it in IMemory
+ size_t getFlattenedSize() const {
+ return sizeof(VideoFrame) + mSize + mIccSize;
+ }
+
+ // Get the pointer to the frame data in a flattened VideoFrame in IMemory
+ uint8_t* getFlattenedData() const {
+ return (uint8_t*)this + sizeof(VideoFrame);
+ }
+
+ // Get the pointer to the ICC data in a flattened VideoFrame in IMemory
+ uint8_t* getFlattenedIccData() const {
+ return (uint8_t*)this + sizeof(VideoFrame) + mSize;
}
// Intentional public access modifier:
- uint32_t mWidth;
- uint32_t mHeight;
- uint32_t mDisplayWidth;
- uint32_t mDisplayHeight;
+ uint32_t mWidth; // Decoded image width before rotation
+ uint32_t mHeight; // Decoded image height before rotation
+ uint32_t mDisplayWidth; // Display width before rotation
+ uint32_t mDisplayHeight; // Display height before rotation
+ int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
+ uint32_t mBytesPerPixel; // Number of bytes per pixel
+ uint32_t mRowBytes; // Number of bytes per row before rotation
uint32_t mSize; // Number of bytes in mData
- int32_t mRotationAngle; // rotation angle, clockwise, should be multiple of 90
- // mData should be 64 bit aligned to prevent additional padding
+ uint32_t mIccSize; // Number of bytes in mIccData
+ uint32_t mReserved; // (padding to make mData 64-bit aligned)
+
+ // mData should be 64-bit aligned to prevent additional padding
uint8_t* mData; // Actual binary data
- // pad structure so it's the same size on 64 bit and 32 bit
+ // pad structure so it's the same size on 64-bit and 32-bit
char mPadding[8 - sizeof(mData)];
+
+ // mIccData should be 64-bit aligned to prevent additional padding
+ uint8_t* mIccData; // Actual binary data
+ // pad structure so it's the same size on 64-bit and 32-bit
+ char mIccPadding[8 - sizeof(mIccData)];
+
+private:
+ //
+ // Utility methods used only within VideoFrame struct
+ //
+
+ // Copy the information fields only
+ void copyInfoOnly(const VideoFrame& copy) {
+ mWidth = copy.mWidth;
+ mHeight = copy.mHeight;
+ mDisplayWidth = copy.mDisplayWidth;
+ mDisplayHeight = copy.mDisplayHeight;
+ mRotationAngle = copy.mRotationAngle;
+ mBytesPerPixel = copy.mBytesPerPixel;
+ mRowBytes = copy.mRowBytes;
+ }
};
}; // namespace android
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index edf644a..9feb118 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -27,9 +27,11 @@
#include "AAudioSimpleRecorder.h"
// TODO support FLOAT
-#define REQUIRED_FORMAT AAUDIO_FORMAT_PCM_I16
+#define REQUIRED_FORMAT AAUDIO_FORMAT_PCM_I16
#define MIN_FRAMES_TO_READ 48 /* arbitrary, 1 msec at 48000 Hz */
+static const int FRAMES_PER_LINE = 20000;
+
int main(int argc, const char **argv)
{
AAudioArgsParser argParser;
@@ -46,7 +48,10 @@
int32_t framesPerRead = 0;
int32_t framesToRecord = 0;
int32_t framesLeft = 0;
+ int32_t nextFrameCount = 0;
+ int32_t frameCount = 0;
int32_t xRunCount = 0;
+ int64_t previousFramePosition = -1;
int16_t *data = nullptr;
float peakLevel = 0.0;
int loopCounter = 0;
@@ -56,7 +61,7 @@
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Monitor input level using AAudio\n", argv[0]);
+ printf("%s - Monitor input level using AAudio read, V0.1.2\n", argv[0]);
argParser.setFormat(REQUIRED_FORMAT);
if (argParser.parseArgs(argc, argv)) {
@@ -133,6 +138,7 @@
goto finish;
}
framesLeft -= actual;
+ frameCount += actual;
// Peak finder.
for (int frameIndex = 0; frameIndex < actual; frameIndex++) {
@@ -143,9 +149,36 @@
}
// Display level as stars, eg. "******".
- if ((loopCounter++ % 10) == 0) {
+ if (frameCount > nextFrameCount) {
displayPeakLevel(peakLevel);
peakLevel = 0.0;
+ nextFrameCount += FRAMES_PER_LINE;
+ }
+
+ // Print timestamps.
+ int64_t framePosition = 0;
+ int64_t frameTime = 0;
+ aaudio_result_t timeResult;
+ timeResult = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC,
+ &framePosition, &frameTime);
+
+ if (timeResult == AAUDIO_OK) {
+ if (framePosition > (previousFramePosition + FRAMES_PER_LINE)) {
+ int64_t realTime = getNanoseconds();
+ int64_t framesRead = AAudioStream_getFramesRead(aaudioStream);
+
+ double latencyMillis = calculateLatencyMillis(framesRead, realTime,
+ framePosition, frameTime,
+ actualSampleRate);
+
+ printf("--- timestamp: result = %4d, position = %lld, at %lld nanos"
+ ", latency = %7.2f msec\n",
+ timeResult,
+ (long long) framePosition,
+ (long long) frameTime,
+ latencyMillis);
+ previousFramePosition = framePosition;
+ }
}
}
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
index 9de2eb0..893795b 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
@@ -41,7 +41,7 @@
// 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 - Display audio input using an AAudio callback\n", argv[0]);
+ printf("%s - Display audio input using an AAudio callback, V0.1.2\n", argv[0]);
result = recorder.open(2, 48000, AAUDIO_FORMAT_PCM_I16,
SimpleRecorderDataCallbackProc, SimpleRecorderErrorCallbackProc, &myData);
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 57d45cd..df0df04 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -14,8 +14,7 @@
* 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>
@@ -28,460 +27,38 @@
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
+#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 5
#define NUM_INPUT_CHANNELS 1
#define FILENAME "/data/oboe_input.raw"
-
-#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)
-
-#define MAX_ZEROTH_PARTIAL_BINS 40
-
-static const float s_Impulse[] = {
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // silence on each side of the impulse
- 0.5f, 0.9f, 0.0f, -0.9f, -0.5f, // bipolar
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
-
-
-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 ic;
- int maxCorrelations = haystackSize - needleSize;
- int numCorrelations = std::min(maxCorrelations, resultSize);
-
- for (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;
-
- 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 < 0.5 * 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.
-int measureLatencyFromEchos(const float *haystack, int haystackSize,
- const float *needle, int needleSize,
- LatencyReport *report) {
- 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;
- int generous = 48000 * 2;
- int numCorrelations = std::min(remaining, generous);
- float *correlations = new float[numCorrelations];
- float *harmonicSums = new float[numCorrelations](); // cleared 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 = 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 process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) = 0;
-
-
- virtual void report() = 0;
-
- void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSampleRate() {
- return mSampleRate;
- }
-
-private:
- int32_t mSampleRate = SAMPLE_RATE;
-};
-
-
-// ====================================================================================
-class EchoAnalyzer : public LoopbackProcessor {
-public:
-
- EchoAnalyzer() : LoopbackProcessor() {
- audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
- }
-
- void setGain(float gain) {
- mGain = gain;
- }
-
- float getGain() {
- return mGain;
- }
-
- void report() override {
-
- 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(RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames);
- printf(RESULT_TAG "latency.msec = %8.2f\n", latencyMillis);
- printf(RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence);
- }
- }
-
- void process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) override {
- int channelsValid = std::min(inputChannelCount, outputChannelCount);
-
- audioRecorder.write(inputData, inputChannelCount, numFrames);
-
- if (mLoopCounter < mLoopStart) {
- // Output silence at the beginning.
- for (int i = 0; i < numFrames; i++) {
- int ic;
- for (ic = 0; ic < outputChannelCount; ic++) {
- outputData[ic] = 0;
- }
- inputData += inputChannelCount;
- outputData += outputChannelCount;
- }
- } else if (mLoopCounter == mLoopStart) {
- // Send a bipolar impulse that we can easily detect.
- for (float sample : s_Impulse) {
- *outputData = sample;
- outputData += outputChannelCount;
- }
- } else {
- // Echo input to output.
- for (int i = 0; i < numFrames; i++) {
- int ic;
- for (ic = 0; ic < channelsValid; ic++) {
- outputData[ic] = inputData[ic] * mGain;
- }
- for (; ic < outputChannelCount; ic++) {
- outputData[ic] = 0;
- }
- inputData += inputChannelCount;
- outputData += outputChannelCount;
- }
- }
-
- mLoopCounter++;
- }
-
-private:
- int mLoopCounter = 0;
- int mLoopStart = 1000;
- float mGain = 1.0f;
-
- AudioRecording audioRecorder;
- LatencyReport latencyReport;
-};
-
-
-// ====================================================================================
-class SineAnalyzer : public LoopbackProcessor {
-public:
-
- void report() override {
- double magnitude = calculateMagnitude();
- printf("sine magnitude = %7.5f\n", magnitude);
- printf("sine frames = %7d\n", mFrameCounter);
- printf("sine frequency = %7.1f Hz\n", mFrequency);
- }
-
- double calculateMagnitude(double *phasePtr = NULL) {
- if (mFrameCounter == 0) {
- return 0.0;
- }
- double sinMean = mSinAccumulator / mFrameCounter;
- double cosMean = mCosAccumulator / mFrameCounter;
- double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
- if( phasePtr != NULL )
- {
- double phase = atan2( sinMean, cosMean );
- *phasePtr = phase;
- }
- return magnitude;
- }
-
- void process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) override {
- double phaseIncrement = 2.0 * M_PI * mFrequency / getSampleRate();
-
- for (int i = 0; i < numFrames; i++) {
- // Multiply input by sine/cosine
- float sample = inputData[i * inputChannelCount];
- float sinOut = sinf(mPhase);
- mSinAccumulator += sample * sinOut;
- mCosAccumulator += sample * cosf(mPhase);
- // Advance and wrap phase
- mPhase += phaseIncrement;
- if (mPhase > (2.0 * M_PI)) {
- mPhase -= (2.0 * M_PI);
- }
-
- // Output sine wave so we can measure it.
- outputData[i * outputChannelCount] = sinOut;
- }
- mFrameCounter += numFrames;
-
- double magnitude = calculateMagnitude();
- if (mWaiting) {
- if (magnitude < 0.001) {
- // discard silence
- mFrameCounter = 0;
- mSinAccumulator = 0.0;
- mCosAccumulator = 0.0;
- } else {
- mWaiting = false;
- }
- }
- };
-
- void setFrequency(int32_t frequency) {
- mFrequency = frequency;
- }
-
- int32_t getFrequency() {
- return mFrequency;
- }
-
-private:
- double mFrequency = 300.0;
- double mPhase = 0.0;
- int32_t mFrameCounter = 0;
- double mSinAccumulator = 0.0;
- double mCosAccumulator = 0.0;
- bool mWaiting = true;
-};
+#define APP_VERSION "0.1.22"
-// TODO make this a class that manages its own buffer allocation
struct LoopbackData {
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;
+ aaudio_result_t inputError = AAUDIO_OK;
+ aaudio_result_t outputError = AAUDIO_OK;
+
SineAnalyzer sineAnalyzer;
EchoAnalyzer echoAnalyzer;
+ AudioRecording audioRecorder;
LoopbackProcessor *loopbackProcessor;
};
@@ -517,6 +94,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 {
@@ -524,6 +108,7 @@
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--;
@@ -534,9 +119,14 @@
numFrames, 0);
if (framesRead < 0) {
myData->inputError = framesRead;
+ printf("ERROR in read = %d", framesRead);
result = AAUDIO_CALLBACK_RESULT_STOP;
} else if (framesRead > 0) {
+ myData->audioRecorder.write(myData->inputData,
+ myData->actualInputChannelCount,
+ numFrames);
+
int32_t numSamples = framesRead * myData->actualInputChannelCount;
convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
@@ -545,12 +135,25 @@
outputData,
myData->actualOutputChannelCount,
framesRead);
+ myData->isDone = myData->loopbackProcessor->isDone();
+ if (myData->isDone) {
+ result = AAUDIO_CALLBACK_RESULT_STOP;
+ }
}
}
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: -n{numBursts} -p{outPerf} -P{inPerf} -t{test} -g{gain} -f{freq}\n");
@@ -571,7 +174,7 @@
}
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':
@@ -612,86 +215,117 @@
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;
- int requestedInputChannelCount = NUM_INPUT_CHANNELS;
- 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;
+
int testMode = TEST_ECHO_LATENCY;
- double frequency = 1000.0;
double gain = 1.0;
- 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;
-
- 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
+ // 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 V" APP_VERSION "\n", argv[0]);
+
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
- if (arg[0] == '-') {
- char option = arg[1];
- switch (option) {
- case 'c':
- requestedInputChannelCount = atoi(&arg[2]);
- break;
- case 'f':
- frequency = atof(&arg[2]);
- break;
- case 'g':
- gain = atof(&arg[2]);
- break;
- case 'm':
- AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
- break;
- case 'n':
- burstsPerBuffer = atoi(&arg[2]);
- break;
- case 'p':
- outputPerformanceLevel = parsePerformanceMode(arg[2]);
- break;
- case 'P':
- inputPerformanceLevel = parsePerformanceMode(arg[2]);
- break;
- case 't':
- testMode = parseTestMode(arg[2]);
- break;
- default:
- usage();
- exit(0);
- break;
+ 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;
}
- } else {
- usage();
- exit(0);
- break;
}
+
}
+ if (inputPerformanceLevel < 0) {
+ printf("illegal inputPerformanceLevel = %d\n", inputPerformanceLevel);
+ exit(EXIT_FAILURE);
+ }
+
+ int32_t requestedDuration = argParser.getDurationSeconds();
+ int32_t recordingDuration = std::min(60, requestedDuration);
+ loopbackData.audioRecorder.allocate(recordingDuration * SAMPLE_RATE);
switch(testMode) {
case TEST_SINE_MAGNITUDE:
- loopbackData.sineAnalyzer.setFrequency(frequency);
loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
break;
case TEST_ECHO_LATENCY:
@@ -703,106 +337,44 @@
break;
}
- // 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]);
-
- // Use an AAudioStreamBuilder to contain requested parameters.
- result = AAudio_createStreamBuilder(&builder);
- if (result < 0) {
- goto finish;
- }
-
- // 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;
- }
-
- // 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);
-
- result = AAudioStreamBuilder_openStream(builder, &outputStream);
- printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
+ 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));
- printf(" bufferSize = %d\n",
- AAudioStream_getBufferSizeInFrames(loopbackData.inputStream));
- printf(" bufferCapacity = %d\n",
- AAudioStream_getBufferCapacityInFrames(loopbackData.inputStream));
+ actualOutputFormat = AAudioStream_getFormat(outputStream);
+ assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
- actualSharingMode = AAudioStream_getSharingMode(loopbackData.inputStream);
- printf(" sharingMode: requested = %d, actual = %d\n",
- requestedSharingMode, actualSharingMode);
-
- actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
- printf(" dataFormat: requested = %d, actual = %d\n",
- requestedInputFormat, actualInputFormat);
- assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
-
- printf(" is MMAP used? = %s\n", AAudioStream_isMMapUsed(loopbackData.inputStream)
- ? "yes" : "no");
-
-
- 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.echoAnalyzer.setSampleRate(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);
- 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(" bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(outputStream));
- 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);
- printf(" is MMAP used? = %s\n", AAudioStream_isMMapUsed(outputStream)
- ? "yes" : "no");
+ loopbackData.actualInputChannelCount = recorder.getChannelCount();
+ loopbackData.actualOutputChannelCount = player.getChannelCount();
// Allocate a buffer for the audio data.
loopbackData.inputFramesMaximum = 32 * framesPerBurst;
@@ -813,49 +385,82 @@
loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
loopbackData.actualInputChannelCount];
+ 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 inputFramesWritten = AAudioStream_getFramesWritten(loopbackData.inputStream);
+ int64_t inputFramesRead = AAudioStream_getFramesRead(loopbackData.inputStream);
+ int64_t outputFramesWritten = AAudioStream_getFramesWritten(outputStream);
+ int64_t outputFramesRead = AAudioStream_getFramesRead(outputStream);
+ printf(" INPUT: wr %lld rd %lld state %s, OUTPUT: wr %lld rd %lld state %s, xruns %d\n",
+ (long long) inputFramesWritten,
+ (long long) inputFramesRead,
+ AAudio_convertStreamStateToText(AAudioStream_getState(loopbackData.inputStream)),
+ (long long) outputFramesWritten,
+ (long long) outputFramesRead,
+ AAudio_convertStreamStateToText(AAudioStream_getState(outputStream)),
+ 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);
- loopbackData.loopbackProcessor->report();
+ 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 mono samples to %s on Android device\n", written, FILENAME);
-// }
-
+ {
+ int written = loopbackData.audioRecorder.save(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(RESULT_TAG "error = %d = %s\n", result, AAudio_convertResultToText(result));
if ((result != AAUDIO_OK)) {
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
index 54217a5..ada37e2 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -24,7 +24,8 @@
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
-#include <AAudioExampleUtils.h>
+
+#include "AAudioExampleUtils.h"
// TODO use this as a base class within AAudio
class AAudioParameters {
@@ -121,7 +122,7 @@
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
- int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED;
+ int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED;
};
class AAudioArgsParser : public AAudioParameters {
@@ -149,11 +150,18 @@
setChannelCount(atoi(&arg[2]));
break;
case 'd':
+ setDeviceId(atoi(&arg[2]));
+ break;
+ case 's':
mDurationSeconds = atoi(&arg[2]);
break;
- case 'm':
- AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
- 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;
@@ -197,8 +205,13 @@
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 enable MMAP\n");
+ printf(" -d{deviceId} default is %d\n", AAUDIO_UNSPECIFIED);
+ printf(" -s{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");
@@ -231,7 +244,7 @@
* Print stream parameters in comparison with requested values.
* @param stream
*/
- void compareWithStream(AAudioStream *stream) {
+ void compareWithStream(AAudioStream *stream) const {
printf(" DeviceId: requested = %d, actual = %d\n",
getDeviceId(), AAudioStream_getDeviceId(stream));
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 66de25f..9ef62c9 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -17,15 +17,20 @@
#ifndef AAUDIO_EXAMPLE_UTILS_H
#define AAUDIO_EXAMPLE_UTILS_H
-#include <unistd.h>
+#include <atomic>
+#include <linux/futex.h>
#include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
#include <aaudio/AAudio.h>
+#include <utils/Errors.h>
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
-static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
+const char *getSharingModeText(aaudio_sharing_mode_t mode) {
const char *modeText = "unknown";
switch (mode) {
case AAUDIO_SHARING_MODE_EXCLUSIVE:
@@ -40,6 +45,12 @@
return modeText;
}
+static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
+ time->tv_sec = nanoseconds / NANOS_PER_SECOND;
+ // Calculate the fractional nanoseconds. Avoids expensive % operation.
+ time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
+}
+
static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
struct timespec time;
int result = clock_gettime(clockId, &time);
@@ -49,7 +60,7 @@
return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
}
-void displayPeakLevel(float peakLevel) {
+static void displayPeakLevel(float peakLevel) {
printf("%5.3f ", peakLevel);
const int maxStars = 50; // arbitrary, fits on one line
int numStars = (int) (peakLevel * maxStars);
@@ -59,4 +70,97 @@
printf("\n");
}
+/**
+ * @param position1 position of hardware frame
+ * @param nanoseconds1
+ * @param position2 position of client read/write
+ * @param nanoseconds2
+ * @param sampleRate
+ * @return latency in milliseconds
+ */
+static double calculateLatencyMillis(int64_t position1, int64_t nanoseconds1,
+ int64_t position2, int64_t nanoseconds2,
+ int64_t sampleRate) {
+ int64_t deltaFrames = position2 - position1;
+ int64_t deltaTime =
+ (NANOS_PER_SECOND * deltaFrames / sampleRate);
+ int64_t timeCurrentFramePlayed = nanoseconds1 + deltaTime;
+ int64_t latencyNanos = timeCurrentFramePlayed - nanoseconds2;
+ double latencyMillis = latencyNanos / 1000000.0;
+ return latencyMillis;
+}
+
+// ================================================================================
+// These Futex calls are common online examples.
+static android::status_t sys_futex(void *addr1, int op, int val1,
+ struct timespec *timeout, void *addr2, int val3) {
+ android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
+ op, val1, timeout,
+ addr2, val3);
+ return (result == 0) ? 0 : -errno;
+}
+
+static android::status_t futex_wake(void *addr, int numWake) {
+ // Use _PRIVATE because we are just using the futex in one process.
+ return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
+}
+
+static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
+ // Use _PRIVATE because we are just using the futex in one process.
+ return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
+}
+
+// TODO better name?
+/**
+ * The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
+ */
+class WakeUp {
+public:
+ WakeUp() : mValue(0) {}
+ explicit WakeUp(int32_t value) : mValue(value) {}
+
+ /**
+ * Wait until the internal value no longer matches the given value.
+ * Note that this code uses a futex, which is subject to spurious wake-ups.
+ * So check to make sure that the desired condition has been met.
+ *
+ * @return zero if the value changes or various negative errors including
+ * -ETIMEDOUT if a timeout occurs,
+ * or -EINTR if interrupted by a signal,
+ * or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
+ */
+ android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
+ struct timespec time;
+ convertNanosecondsToTimespec(timeoutNanoseconds, &time);
+ return futex_wait(&mValue, value, &time);
+ }
+
+ /**
+ * Increment value and wake up any threads that need to be woken.
+ *
+ * @return number of waiters woken up
+ */
+ android::status_t wake() {
+ ++mValue;
+ return futex_wake(&mValue, INT_MAX);
+ }
+
+ /**
+ * Set value and wake up any threads that need to be woken.
+ *
+ * @return number of waiters woken up
+ */
+ android::status_t wake(int32_t value) {
+ mValue.store(value);
+ return futex_wake(&mValue, INT_MAX);
+ }
+
+ int32_t get() {
+ return mValue.load();
+ }
+
+private:
+ std::atomic<int32_t> mValue;
+};
+
#endif // AAUDIO_EXAMPLE_UTILS_H
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 19f8aff..d2e7f23 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -23,6 +23,7 @@
#include <sched.h>
#include <aaudio/AAudio.h>
+#include <atomic>
#include "AAudioArgsParser.h"
#include "SineGenerator.h"
@@ -30,6 +31,11 @@
#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.
*/
@@ -214,13 +220,26 @@
AAudioStream *mStream = nullptr;
aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
+
};
typedef struct SineThreadedData_s {
+
SineGenerator sineOsc1;
SineGenerator sineOsc2;
- int scheduler;
- bool schedulerChecked;
+ int64_t framesTotal = 0;
+ int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
+ int32_t minNumFrames = INT32_MAX;
+ int32_t maxNumFrames = 0;
+
+ int scheduler = 0;
+ bool schedulerChecked = false;
+ bool forceUnderruns = false;
+
+ AAudioSimplePlayer simplePlayer;
+ int32_t callbackCount = 0;
+ WakeUp waker{AAUDIO_OK};
+
} SineThreadedData_t;
// Callback function that fills the audio output buffer.
@@ -233,16 +252,34 @@
// 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->callbackCount++;
+
+ 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.
@@ -277,9 +314,16 @@
void SimplePlayerErrorCallbackProc(
AAudioStream *stream __unused,
void *userData __unused,
- aaudio_result_t error)
-{
- printf("Error Callback, error: %d\n",(int)error);
+ aaudio_result_t error) {
+ // should not happen but just in case...
+ if (userData == nullptr) {
+ printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
+ return;
+ }
+ SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+ android::status_t ret = sineData->waker.wake(error);
+ printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
}
+
#endif //AAUDIO_SIMPLE_PLAYER_H
diff --git a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
index 6be9112..1344273 100644
--- a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
+++ b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
@@ -73,12 +73,20 @@
/**
* Only call this after open() has been called.
*/
- int32_t getSamplesPerFrame() {
+ 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.
*/
diff --git a/media/libaaudio/examples/utils/SineGenerator.h b/media/libaaudio/examples/utils/SineGenerator.h
index 64b772d..a755582 100644
--- a/media/libaaudio/examples/utils/SineGenerator.h
+++ b/media/libaaudio/examples/utils/SineGenerator.h
@@ -58,6 +58,13 @@
}
}
+ void setAmplitude(double amplitude) {
+ mAmplitude = amplitude;
+ }
+ double getAmplitude() const {
+ return mAmplitude;
+ }
+
private:
void advancePhase() {
mPhase += mPhaseIncrement;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 0125c0f..677fb6c 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -16,11 +16,14 @@
// 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"
@@ -48,11 +51,13 @@
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 V0.1.1\n", argv[0]);
+ printf("%s - Play a sine wave using AAudio V0.1.2\n", argv[0]);
if (argParser.parseArgs(argc, argv)) {
return EXIT_FAILURE;
@@ -95,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();
@@ -176,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 2211b72..2280b72 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -15,6 +15,7 @@
*/
// Play sine waves using an AAudio callback.
+// If a disconnection occurs then reopen the stream on the new device.
#include <assert.h>
#include <unistd.h>
@@ -22,33 +23,31 @@
#include <sched.h>
#include <stdio.h>
#include <math.h>
+#include <string.h>
#include <time.h>
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
+#include "../../utils/AAudioSimplePlayer.h"
-// Application data that gets passed to the callback.
-#define MAX_FRAME_COUNT_RECORDS 256
-
-int main(int argc, const char **argv)
+/**
+ * Open stream, play some sine waves, then close the stream.
+ *
+ * @param argParser
+ * @return AAUDIO_OK or negative error code
+ */
+static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser)
{
- AAudioArgsParser argParser;
- AAudioSimplePlayer player;
SineThreadedData_t myData;
- aaudio_result_t result;
- int32_t actualSampleRate;
+ AAudioSimplePlayer &player = myData.simplePlayer;
+ aaudio_result_t result = AAUDIO_OK;
+ bool disconnected = false;
+ int64_t startedAtNanos;
- // 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 V0.1.1\n", argv[0]);
-
+ printf("----------------------- run complete test --------------------------\n");
myData.schedulerChecked = false;
-
- if (argParser.parseArgs(argc, argv)) {
- return EXIT_FAILURE;
- }
+ myData.callbackCount = 0;
+ myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
result = player.open(argParser,
SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
@@ -59,13 +58,19 @@
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, actualSampleRate);
- myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+ // Setup sine wave generators.
+ {
+ int32_t actualSampleRate = player.getSampleRate();
+ myData.sineOsc1.setup(440.0, actualSampleRate);
+ myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
+ myData.sineOsc1.setAmplitude(0.2);
+ myData.sineOsc2.setup(660.0, actualSampleRate);
+ myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+ myData.sineOsc2.setAmplitude(0.2);
+ }
#if 0
+ // writes not allowed for callback streams
result = player.prime(); // FIXME crashes AudioTrack.cpp
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - player.prime() returned %d\n", result);
@@ -79,31 +84,32 @@
goto error;
}
+ // Play a sine wave in the background.
printf("Sleep for %d seconds while audio plays in a callback thread.\n",
argParser.getDurationSeconds());
+ startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
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*/);
-
- aaudio_stream_state_t state;
- result = AAudioStream_waitForStateChange(player.getStream(),
- AAUDIO_STREAM_STATE_CLOSED,
- &state,
- 0);
+ // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
+ long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
+ int64_t millis = (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
+ result = myData.waker.get();
+ printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
+ ", second = %d, framesWritten = %8d, underruns = %d\n",
+ ret, result, (int) millis,
+ second,
+ (int) AAudioStream_getFramesWritten(player.getStream()),
+ (int) AAudioStream_getXRunCount(player.getStream()));
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_waitForStateChange() returned %d\n", result);
- goto error;
- }
- if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
- printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
+ if (result == AAUDIO_ERROR_DISCONNECTED) {
+ disconnected = true;
+ }
break;
}
- printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(player.getStream()));
}
- printf("Woke up now.\n");
+ printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- printf("call stop()\n");
+ printf("call stop() callback # = %d\n", myData.callbackCount);
result = player.stop();
if (result != AAUDIO_OK) {
goto error;
@@ -120,11 +126,32 @@
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:
player.close();
- printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- return EXIT_FAILURE;
+ return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
}
+int main(int argc, const char **argv)
+{
+ AAudioArgsParser argParser;
+ aaudio_result_t result;
+
+ // 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 V0.1.2\n", argv[0]);
+
+ if (argParser.parseArgs(argc, argv)) {
+ return EXIT_FAILURE;
+ }
+
+ // Keep looping until we can complete the test without disconnecting.
+ while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED);
+
+ return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 30fbdd6..3c23736 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -234,6 +234,15 @@
int32_t channelCount);
/**
+ * Identical to AAudioStreamBuilder_setChannelCount().
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param samplesPerFrame Number of samples in a frame.
+ */
+AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
+ int32_t samplesPerFrame);
+
+/**
* Request a sample data format, for example AAUDIO_FORMAT_PCM_I16.
*
* The default, if you do not call this function, is AAUDIO_UNSPECIFIED.
@@ -721,6 +730,14 @@
AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream);
/**
+ * Identical to AAudioStream_getChannelCount().
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return actual number of samples frame
+ */
+AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream);
+
+/**
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return actual device ID
*/
diff --git a/media/libaaudio/libaaudio.map.txt b/media/libaaudio/libaaudio.map.txt
index b9012e5..2ba5250 100644
--- a/media/libaaudio/libaaudio.map.txt
+++ b/media/libaaudio/libaaudio.map.txt
@@ -11,6 +11,7 @@
AAudioStreamBuilder_setErrorCallback;
AAudioStreamBuilder_setFramesPerDataCallback;
AAudioStreamBuilder_setSampleRate;
+ AAudioStreamBuilder_setSamplesPerFrame;
AAudioStreamBuilder_setChannelCount;
AAudioStreamBuilder_setFormat;
AAudioStreamBuilder_setSharingMode;
@@ -34,6 +35,7 @@
AAudioStream_getBufferCapacityInFrames;
AAudioStream_getXRunCount;
AAudioStream_getSampleRate;
+ AAudioStream_getSamplesPerFrame;
AAudioStream_getChannelCount;
AAudioStream_getPerformanceMode;
AAudioStream_getDeviceId;
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index a268e49..07ee2de 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -45,6 +45,7 @@
using android::IInterface;
using android::IAAudioService;
using android::Mutex;
+using android::ProcessState;
using android::sp;
using android::wp;
@@ -52,15 +53,19 @@
ANDROID_SINGLETON_STATIC_INSTANCE(AAudioBinderClient);
+// If we don't keep a strong pointer here then this singleton can get deleted!
+android::sp<AAudioBinderClient> gKeepBinderClient;
+
AAudioBinderClient::AAudioBinderClient()
: AAudioServiceInterface()
, Singleton<AAudioBinderClient>() {
-
+ gKeepBinderClient = this; // so this singleton won't get deleted
mAAudioClient = new AAudioClient(this);
- ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+ ALOGV("AAudioBinderClient() this = %p, created mAAudioClient = %p", this, mAAudioClient.get());
}
AAudioBinderClient::~AAudioBinderClient() {
+ ALOGV("AAudioBinderClient()::~AAudioBinderClient() destroying %p", this);
Mutex::Autolock _l(mServiceLock);
if (mAAudioService != 0) {
IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
@@ -75,19 +80,19 @@
bool needToRegister = false;
{
Mutex::Autolock _l(mServiceLock);
- if (mAAudioService == 0) {
+ if (mAAudioService.get() == nullptr) {
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) {
+ if (binder.get() != nullptr) {
break;
}
} while (retries-- > 0);
- if (binder != 0) {
+ if (binder.get() != nullptr) {
// Ask for notification if the service dies.
status_t status = binder->linkToDeath(mAAudioClient);
// TODO review what we should do if this fails
@@ -98,7 +103,7 @@
mAAudioService = interface_cast<IAAudioService>(binder);
needToRegister = true;
// Make sure callbacks can be received by mAAudioClient
- android::ProcessState::self()->startThreadPool();
+ ProcessState::self()->startThreadPool();
} else {
ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
}
@@ -106,7 +111,7 @@
aaudioService = mAAudioService;
}
// Do this outside the mutex lock.
- if (needToRegister && aaudioService != 0) { // new client?
+ if (needToRegister && aaudioService.get() != nullptr) { // new client?
aaudioService->registerClient(mAAudioClient);
}
return aaudioService;
@@ -117,7 +122,6 @@
mAAudioService.clear(); // force a reconnect
}
-
/**
* @param request info needed to create the stream
* @param configuration contains information about the created stream
@@ -128,14 +132,12 @@
aaudio_handle_t stream;
for (int i = 0; i < 2; i++) {
const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) {
- return AAUDIO_ERROR_NO_SERVICE;
- }
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
stream = service->openStream(request, configurationOutput);
if (stream == AAUDIO_ERROR_NO_SERVICE) {
- ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+ ALOGE("AAudioBinderClient::openStream lost connection to AAudioService.");
dropAAudioService(); // force a reconnect
} else {
break;
@@ -145,8 +147,8 @@
}
aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->closeStream(streamHandle);
}
@@ -155,32 +157,32 @@
*/
aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
AudioEndpointParcelable &parcelable) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->getStreamDescription(streamHandle, parcelable);
}
aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->startStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->pauseStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->stopStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->flushStream(streamHandle);
}
@@ -190,8 +192,8 @@
aaudio_result_t AAudioBinderClient::registerAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId,
int64_t periodNanoseconds) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->registerAudioThread(streamHandle,
clientThreadId,
periodNanoseconds);
@@ -199,8 +201,8 @@
aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId) {
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ const sp<IAAudioService> service = getAAudioService();
+ if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
return service->unregisterAudioThread(streamHandle,
clientThreadId);
}
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 89ae85c..f9da8b4 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -118,13 +118,13 @@
{
public:
AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
- : mBinderClient(aaudioBinderClient) {
+ : mBinderClient(aaudioBinderClient) {
}
// implement DeathRecipient
virtual void binderDied(const android::wp<android::IBinder>& who __unused) {
android::sp<AAudioBinderClient> client = mBinderClient.promote();
- if (client != 0) {
+ if (client.get() != nullptr) {
client->dropAAudioService();
}
ALOGW("AAudio service binderDied()!");
@@ -133,7 +133,7 @@
// implement BnAAudioClient
void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
android::sp<AAudioBinderClient> client = mBinderClient.promote();
- if (client != 0) {
+ if (client.get() != nullptr) {
client->onStreamChange(handle, opcode, value);
}
}
@@ -141,10 +141,11 @@
android::wp<AAudioBinderClient> mBinderClient;
};
+private:
- android::Mutex mServiceLock;
+ android::Mutex mServiceLock;
android::sp<android::IAAudioService> mAAudioService;
- android::sp<AAudioClient> mAAudioClient;
+ android::sp<AAudioClient> mAAudioClient;
};
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index b4377fb..54e8001 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -28,7 +28,6 @@
// Used to send information about the HAL to the client.
struct AAudioMessageTimestamp {
int64_t position; // number of frames transferred so far
- int64_t deviceOffset; // add to client position to get device position
int64_t timestamp; // time when that position was reached
};
@@ -51,7 +50,8 @@
typedef struct AAudioServiceMessage_s {
enum class code : uint32_t {
NOTHING,
- TIMESTAMP,
+ TIMESTAMP_SERVICE, // when frame is read or written by the service to the client
+ TIMESTAMP_HARDWARE, // when frame is at DAC or ADC
EVENT,
};
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index e763934..153fce3 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -46,6 +46,8 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) getFormat());
if (status != NO_ERROR) goto error;
+ status = parcel->writeInt32((int32_t) getDirection());
+ if (status != NO_ERROR) goto error;
status = parcel->writeInt32(getBufferCapacity());
if (status != NO_ERROR) goto error;
return NO_ERROR;
@@ -73,9 +75,12 @@
setFormat(value);
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
+ setDirection((aaudio_direction_t) 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;
-}
\ No newline at end of file
+}
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index abdcf5b..1200ab2 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -46,9 +46,6 @@
status_t status = parcel->writeInt32((int32_t) mUserId);
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;
@@ -71,10 +68,6 @@
if (status != NO_ERROR) goto error;
mUserId = (uid_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;
@@ -98,7 +91,6 @@
void AAudioStreamRequest::dump() const {
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 b0fa96a..492f69d 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -52,14 +52,6 @@
mProcessId = processId;
}
- aaudio_direction_t getDirection() const {
- return mDirection;
- }
-
- void setDirection(aaudio_direction_t direction) {
- mDirection = direction;
- }
-
bool isSharingModeMatchRequired() const {
return mSharingModeMatchRequired;
}
@@ -94,9 +86,8 @@
protected:
AAudioStreamConfiguration mConfiguration;
- uid_t mUserId;
- pid_t mProcessId;
- aaudio_direction_t mDirection;
+ uid_t mUserId = (uid_t) -1;
+ pid_t mProcessId = (pid_t) -1;
bool mSharingModeMatchRequired = false;
bool mInService = false; // Stream opened by AAudioservice
};
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/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 6ec285f..604eed5 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -32,14 +32,17 @@
#define RIDICULOUSLY_LARGE_FRAME_SIZE 4096
AudioEndpoint::AudioEndpoint()
- : mFreeRunning(false)
+ : mUpCommandQueue(nullptr)
+ , mDataQueue(nullptr)
+ , mFreeRunning(false)
, mDataReadCounter(0)
, mDataWriteCounter(0)
{
}
-AudioEndpoint::~AudioEndpoint()
-{
+AudioEndpoint::~AudioEndpoint() {
+ delete mDataQueue;
+ delete mUpCommandQueue;
}
static aaudio_result_t AudioEndpoint_validateQueueDescriptor(const char *type,
@@ -118,24 +121,28 @@
{
aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
if (result != AAUDIO_OK) {
- ALOGE("AudioEndpoint_validateQueueDescriptor returned %d %s",
- result, AAudio_convertResultToText(result));
return result;
}
// ============================ up message queue =============================
const RingBufferDescriptor *descriptor = &pEndpointDescriptor->upMessageQueueDescriptor;
if(descriptor->bytesPerFrame != sizeof(AAudioServiceMessage)) {
- ALOGE("AudioEndpoint::configure() bytesPerFrame != sizeof(AAudioServiceMessage) = %d",
+ ALOGE("AudioEndpoint.configure() bytesPerFrame != sizeof(AAudioServiceMessage) = %d",
descriptor->bytesPerFrame);
return AAUDIO_ERROR_INTERNAL;
}
if(descriptor->readCounterAddress == nullptr || descriptor->writeCounterAddress == nullptr) {
- ALOGE("AudioEndpoint_validateQueueDescriptor() NULL counter address");
+ ALOGE("AudioEndpoint.configure() NULL counter address");
return AAUDIO_ERROR_NULL;
}
+ // Prevent memory leak and reuse.
+ if(mUpCommandQueue != nullptr || mDataQueue != nullptr) {
+ ALOGE("AudioEndpoint.configure() endpoint already used");
+ return AAUDIO_ERROR_INTERNAL;
+ }
+
mUpCommandQueue = new FifoBuffer(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
@@ -146,8 +153,8 @@
// ============================ data queue =============================
descriptor = &pEndpointDescriptor->dataQueueDescriptor;
- ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
- ALOGV("AudioEndpoint::configure() data readCounterAddress = %p",
+ ALOGV("AudioEndpoint.configure() data framesPerBurst = %d", descriptor->framesPerBurst);
+ 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
@@ -156,7 +163,7 @@
? descriptor->readCounterAddress // read by other side
: descriptor->writeCounterAddress; // written by other side
mFreeRunning = (remoteCounter == nullptr);
- ALOGV("AudioEndpoint::configure() mFreeRunning = %d", mFreeRunning ? 1 : 0);
+ ALOGV("AudioEndpoint.configure() mFreeRunning = %d", mFreeRunning ? 1 : 0);
int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
? &mDataReadCounter
@@ -254,3 +261,7 @@
ALOGD("AudioEndpoint: data readCounter = %lld", (long long) mDataQueue->getReadCounter());
ALOGD("AudioEndpoint: data writeCounter = %lld", (long long) mDataQueue->getWriteCounter());
}
+
+void AudioEndpoint::eraseDataMemory() {
+ mDataQueue->eraseMemory();
+}
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 81a4f7b..f5b67e8 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -86,6 +86,11 @@
int32_t getBufferCapacityInFrames() const;
+ /**
+ * Write zeros to the data queue memory.
+ */
+ void eraseDataMemory();
+
void dump() const;
private:
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4c7d0f7..7f2e495 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -68,6 +68,7 @@
, mServiceInterface(serviceInterface)
, mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
, mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
+ , mAtomicTimestamp()
{
ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
mWakeupDelayNanos, mMinimumSleepNanos);
@@ -79,9 +80,16 @@
aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AAUDIO_OK;
+ int32_t capacity;
AAudioStreamRequest request;
- AAudioStreamConfiguration configuration;
+ AAudioStreamConfiguration configurationOutput;
+ if (getState() != AAUDIO_STREAM_STATE_UNINITIALIZED) {
+ ALOGE("AudioStreamInternal::open(): already open! state = %d", getState());
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ // Copy requested parameters to the stream.
result = AudioStream::open(builder);
if (result < 0) {
return result;
@@ -97,98 +105,106 @@
// 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());
request.getConfiguration().setSamplesPerFrame(getSamplesPerFrame());
+ request.getConfiguration().setDirection(getDirection());
request.getConfiguration().setSharingMode(getSharingMode());
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
- mServiceStreamHandle = mServiceInterface.openStream(request, configuration);
+ mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
if (mServiceStreamHandle < 0) {
result = mServiceStreamHandle;
- ALOGE("AudioStreamInternal.open(): openStream() returned %d", result);
- } else {
- result = configuration.validate();
- if (result != AAUDIO_OK) {
- close();
- return result;
- }
- // Save results of the open.
- 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.getFormat();
-
- result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
- if (result != AAUDIO_OK) {
- mServiceInterface.closeStream(mServiceStreamHandle);
- return result;
- }
-
- // resolve parcelable into a descriptor
- result = mEndPointParcelable.resolve(&mEndpointDescriptor);
- if (result != AAUDIO_OK) {
- mServiceInterface.closeStream(mServiceStreamHandle);
- return result;
- }
-
- // Configure endpoint based on descriptor.
- mAudioEndpoint.configure(&mEndpointDescriptor, getDirection());
-
- mFramesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
- int32_t capacity = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
-
- // Validate result from server.
- if (mFramesPerBurst < 16 || mFramesPerBurst > 16 * 1024) {
- ALOGE("AudioStream::open(): framesPerBurst out of range = %d", mFramesPerBurst);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
- if (capacity < mFramesPerBurst || capacity > 32 * 1024) {
- ALOGE("AudioStream::open(): bufferCapacity out of range = %d", capacity);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
-
- mClockModel.setSampleRate(getSampleRate());
- mClockModel.setFramesPerBurst(mFramesPerBurst);
-
- if (getDataCallbackProc()) {
- mCallbackFrames = builder.getFramesPerDataCallback();
- if (mCallbackFrames > getBufferCapacity() / 2) {
- 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");
- mServiceInterface.closeStream(mServiceStreamHandle);
- return AAUDIO_ERROR_OUT_OF_RANGE;
-
- }
- if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
- mCallbackFrames = mFramesPerBurst;
- }
-
- int32_t bytesPerFrame = getSamplesPerFrame()
- * AAudioConvert_formatToSizeInBytes(getFormat());
- int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
- mCallbackBuffer = new uint8_t[callbackBufferSize];
- }
-
- 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);
- }
+ ALOGE("AudioStreamInternal::open(): openStream() returned %d", result);
+ return result;
}
+
+ result = configurationOutput.validate();
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+ // Save results of the open.
+ setSampleRate(configurationOutput.getSampleRate());
+ setSamplesPerFrame(configurationOutput.getSamplesPerFrame());
+ setDeviceId(configurationOutput.getDeviceId());
+ setSharingMode(configurationOutput.getSharingMode());
+
+ // Save device format so we can do format conversion and volume scaling together.
+ mDeviceFormat = configurationOutput.getFormat();
+
+ result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+
+ // Resolve parcelable into a descriptor.
+ result = mEndPointParcelable.resolve(&mEndpointDescriptor);
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+
+ // Configure endpoint based on descriptor.
+ result = mAudioEndpoint.configure(&mEndpointDescriptor, getDirection());
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+
+ mFramesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
+ capacity = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+
+ // Validate result from server.
+ if (mFramesPerBurst < 16 || mFramesPerBurst > 16 * 1024) {
+ ALOGE("AudioStreamInternal::open(): framesPerBurst out of range = %d", mFramesPerBurst);
+ result = AAUDIO_ERROR_OUT_OF_RANGE;
+ goto error;
+ }
+ if (capacity < mFramesPerBurst || capacity > 32 * 1024) {
+ ALOGE("AudioStreamInternal::open(): bufferCapacity out of range = %d", capacity);
+ result = AAUDIO_ERROR_OUT_OF_RANGE;
+ goto error;
+ }
+
+ mClockModel.setSampleRate(getSampleRate());
+ mClockModel.setFramesPerBurst(mFramesPerBurst);
+
+ if (getDataCallbackProc()) {
+ mCallbackFrames = builder.getFramesPerDataCallback();
+ if (mCallbackFrames > getBufferCapacity() / 2) {
+ ALOGE("AudioStreamInternal::open(): framesPerCallback too big = %d, capacity = %d",
+ mCallbackFrames, getBufferCapacity());
+ result = AAUDIO_ERROR_OUT_OF_RANGE;
+ goto error;
+
+ } else if (mCallbackFrames < 0) {
+ ALOGE("AudioStreamInternal::open(): framesPerCallback negative");
+ result = AAUDIO_ERROR_OUT_OF_RANGE;
+ goto error;
+
+ }
+ if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
+ mCallbackFrames = mFramesPerBurst;
+ }
+
+ int32_t bytesPerFrame = getSamplesPerFrame()
+ * AAudioConvert_formatToSizeInBytes(getFormat());
+ int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
+ mCallbackBuffer = new uint8_t[callbackBufferSize];
+ }
+
+ 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;
+
+error:
+ close();
return result;
}
@@ -223,7 +239,6 @@
}
}
-
static void *aaudio_callback_thread_proc(void *context)
{
AudioStreamInternal *stream = (AudioStreamInternal *)context;
@@ -240,9 +255,11 @@
int64_t startTime;
ALOGD("AudioStreamInternal()::requestStart()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal::requestStart() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
if (isActive()) {
+ ALOGE("AudioStreamInternal::requestStart() already active");
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_stream_state_t originalState = getState();
@@ -319,6 +336,7 @@
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal::registerThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.registerAudioThread(mServiceStreamHandle,
@@ -328,6 +346,7 @@
aaudio_result_t AudioStreamInternal::unregisterThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal::unregisterThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
@@ -338,6 +357,7 @@
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
+
return mServiceInterface.startClient(mServiceStreamHandle, client, clientHandle);
}
@@ -351,15 +371,18 @@
aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) {
- // TODO Generate in server and pass to client. Return latest.
- int64_t time = AudioClock::getNanoseconds();
- *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;
+ // Generated in server and passed to client. Return latest.
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *framePosition = timestamp.getPosition();
+ *timeNanoseconds = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
}
-aaudio_result_t AudioStreamInternal::updateStateWhileWaiting() {
+aaudio_result_t AudioStreamInternal::updateStateMachine() {
if (isDataCallbackActive()) {
return AAUDIO_OK; // state is getting updated by the callback thread read/write call
}
@@ -385,7 +408,7 @@
oldTime = nanoTime;
}
-aaudio_result_t AudioStreamInternal::onTimestampFromServer(AAudioServiceMessage *message) {
+aaudio_result_t AudioStreamInternal::onTimestampService(AAudioServiceMessage *message) {
#if LOG_TIMESTAMPS
logTimestamp(*message);
#endif
@@ -393,23 +416,29 @@
return AAUDIO_OK;
}
+aaudio_result_t AudioStreamInternal::onTimestampHardware(AAudioServiceMessage *message) {
+ Timestamp timestamp(message->timestamp.position, message->timestamp.timestamp);
+ mAtomicTimestamp.write(timestamp);
+ return AAUDIO_OK;
+}
+
aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *message) {
aaudio_result_t result = AAUDIO_OK;
switch (message->event.event) {
case AAUDIO_SERVICE_EVENT_STARTED:
- ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_STARTED");
+ ALOGD("AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_STARTED");
if (getState() == AAUDIO_STREAM_STATE_STARTING) {
setState(AAUDIO_STREAM_STATE_STARTED);
}
break;
case AAUDIO_SERVICE_EVENT_PAUSED:
- ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_PAUSED");
+ ALOGD("AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_PAUSED");
if (getState() == AAUDIO_STREAM_STATE_PAUSING) {
setState(AAUDIO_STREAM_STATE_PAUSED);
}
break;
case AAUDIO_SERVICE_EVENT_STOPPED:
- ALOGD("AudioStreamInternal::onEventFromServergot() AAUDIO_SERVICE_EVENT_STOPPED");
+ ALOGD("AudioStreamInternal::onEventFromServer() got AAUDIO_SERVICE_EVENT_STOPPED");
if (getState() == AAUDIO_STREAM_STATE_STOPPING) {
setState(AAUDIO_STREAM_STATE_STOPPED);
}
@@ -426,10 +455,14 @@
setState(AAUDIO_STREAM_STATE_CLOSED);
break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
+ // Prevent hardware from looping on old data and making buzzing sounds.
+ if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ mAudioEndpoint.eraseDataMemory();
+ }
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
ALOGW("WARNING - AudioStreamInternal::onEventFromServer()"
- " AAUDIO_SERVICE_EVENT_DISCONNECTED");
+ " AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared");
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
mStreamVolume = (float)message->event.dataDouble;
@@ -456,8 +489,12 @@
break; // no command this time, no problem
}
switch (message.what) {
- case AAudioServiceMessage::code::TIMESTAMP:
- result = onTimestampFromServer(&message);
+ case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
+ result = onTimestampService(&message);
+ break;
+
+ case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
+ result = onTimestampHardware(&message);
break;
case AAudioServiceMessage::code::EVENT:
@@ -517,12 +554,19 @@
wakeTimeNanos += mWakeupDelayNanos;
}
+ currentTimeNanos = AudioClock::getNanoseconds();
+ int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
+ // Guarantee a minimum sleep time.
+ if (wakeTimeNanos < earliestWakeTime) {
+ wakeTimeNanos = earliestWakeTime;
+ }
+
if (wakeTimeNanos > deadlineNanos) {
// If we time out, just return the framesWritten so far.
// TODO remove after we fix the deadline bug
ALOGW("AudioStreamInternal::processData(): entered at %lld nanos, currently %lld",
(long long) entryTimeNanos, (long long) currentTimeNanos);
- ALOGW("AudioStreamInternal::processData(): timed out after %lld nanos",
+ ALOGW("AudioStreamInternal::processData(): TIMEOUT after %lld nanos",
(long long) timeoutNanoseconds);
ALOGW("AudioStreamInternal::processData(): wakeTime = %lld, deadline = %lld nanos",
(long long) wakeTimeNanos, (long long) deadlineNanos);
@@ -533,13 +577,6 @@
break;
}
- 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);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 1b991de..3523294 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -50,7 +50,7 @@
int64_t *framePosition,
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t updateStateWhileWaiting() override;
+ virtual aaudio_result_t updateStateMachine() override;
aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -93,6 +93,10 @@
aaudio_result_t stopClient(audio_port_handle_t clientHandle);
+ aaudio_handle_t getServiceHandle() const {
+ return mServiceStreamHandle;
+ }
+
protected:
aaudio_result_t processData(void *buffer,
@@ -122,7 +126,9 @@
aaudio_result_t onEventFromServer(AAudioServiceMessage *message);
- aaudio_result_t onTimestampFromServer(AAudioServiceMessage *message);
+ aaudio_result_t onTimestampService(AAudioServiceMessage *message);
+
+ aaudio_result_t onTimestampHardware(AAudioServiceMessage *message);
void logTimestamp(AAudioServiceMessage &message);
@@ -181,6 +187,11 @@
AudioEndpointParcelable mEndPointParcelable; // description of the buffers filled by service
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
+
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
+
+ int64_t mServiceLatencyNanos = 0;
+
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index ca42444..5089b00 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -101,7 +101,6 @@
*/
static aaudio_policy_t s_MMapPolicy = AAUDIO_UNSPECIFIED;
-
static AudioStream *convertAAudioStreamToAudioStream(AAudioStream* stream)
{
return (AudioStream*) stream;
@@ -144,12 +143,18 @@
}
AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
- int32_t channelCount)
+ int32_t channelCount)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setSamplesPerFrame(channelCount);
}
+AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
+ int32_t channelCount)
+{
+ AAudioStreamBuilder_setChannelCount(builder, channelCount);
+}
+
AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder,
aaudio_direction_t direction)
{
@@ -248,7 +253,7 @@
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestStart(%p) called --------------", stream);
aaudio_result_t result = audioStream->requestStart();
- ALOGD("AAudioStream_requestStart(%p) returned ------------", stream);
+ ALOGD("AAudioStream_requestStart(%p) returned %d ---------", stream, result);
return result;
}
@@ -350,6 +355,11 @@
return audioStream->getSamplesPerFrame();
}
+AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream)
+{
+ return AAudioStream_getChannelCount(stream);
+}
+
AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 65c2b46..82445e7 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -34,6 +34,16 @@
AAudioStreamParameters::AAudioStreamParameters() {}
AAudioStreamParameters::~AAudioStreamParameters() {}
+void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) {
+ mSamplesPerFrame = other.mSamplesPerFrame;
+ mSampleRate = other.mSampleRate;
+ mDeviceId = other.mDeviceId;
+ mSharingMode = other.mSharingMode;
+ mAudioFormat = other.mAudioFormat;
+ mDirection = other.mDirection;
+ mBufferCapacity = other.mBufferCapacity;
+}
+
aaudio_result_t AAudioStreamParameters::validate() const {
if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
&& (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
@@ -78,6 +88,16 @@
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+ switch (mDirection) {
+ case AAUDIO_DIRECTION_INPUT:
+ case AAUDIO_DIRECTION_OUTPUT:
+ break; // valid
+ default:
+ ALOGE("AAudioStreamParameters: direction not valid = %d", mDirection);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ // break;
+ }
+
return AAUDIO_OK;
}
@@ -87,5 +107,7 @@
ALOGD("AAudioStreamParameters mSamplesPerFrame = %d", mSamplesPerFrame);
ALOGD("AAudioStreamParameters mSharingMode = %d", (int)mSharingMode);
ALOGD("AAudioStreamParameters mAudioFormat = %d", (int)mAudioFormat);
+ ALOGD("AAudioStreamParameters mDirection = %d", mDirection);
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
index 97379cc..5e67c93 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <aaudio/AAudio.h>
+#include <utility/AAudioUtilities.h>
namespace aaudio {
@@ -79,6 +80,24 @@
mBufferCapacity = frames;
}
+ aaudio_direction_t getDirection() const {
+ return mDirection;
+ }
+
+ void setDirection(aaudio_direction_t direction) {
+ mDirection = direction;
+ }
+
+ int32_t calculateBytesPerFrame() const {
+ return getSamplesPerFrame() * AAudioConvert_formatToSizeInBytes(getFormat());
+ }
+
+ /**
+ * Copy variables defined in other AAudioStreamParameters instance to this one.
+ * @param other
+ */
+ void copyFrom(const AAudioStreamParameters &other);
+
virtual aaudio_result_t validate() const;
void dump() const;
@@ -89,9 +108,10 @@
int32_t mDeviceId = AAUDIO_UNSPECIFIED;
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
};
} /* namespace aaudio */
-#endif //AAUDIO_STREAM_PARAMETERS_H
\ No newline at end of file
+#endif //AAUDIO_STREAM_PARAMETERS_H
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 4859c69..4f1cc37 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -98,7 +98,7 @@
aaudio_stream_state_t *nextState,
int64_t timeoutNanoseconds)
{
- aaudio_result_t result = updateStateWhileWaiting();
+ aaudio_result_t result = updateStateMachine();
if (result != AAUDIO_OK) {
return result;
}
@@ -112,7 +112,7 @@
AudioClock::sleepForNanos(durationNanos);
timeoutNanoseconds -= durationNanos;
- aaudio_result_t result = updateStateWhileWaiting();
+ aaudio_result_t result = updateStateMachine();
if (result != AAUDIO_OK) {
return result;
}
@@ -153,6 +153,7 @@
void* threadArg)
{
if (mHasThread) {
+ ALOGE("AudioStream::createThread() - mHasThread already true");
return AAUDIO_ERROR_INVALID_STATE;
}
if (threadProc == nullptr) {
@@ -174,6 +175,7 @@
aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds)
{
if (!mHasThread) {
+ ALOGE("AudioStream::joinThread() - but has no thread");
return AAUDIO_ERROR_INVALID_STATE;
}
#if 0
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index e5fdcc6..ad18751 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -68,10 +68,10 @@
/**
- * Update state while in the middle of waitForStateChange()
+ * Update state machine.()
* @return
*/
- virtual aaudio_result_t updateStateWhileWaiting() = 0;
+ virtual aaudio_result_t updateStateMachine() = 0;
// =========== End ABSTRACT methods ===========================
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 43a1ef1..09ebb3e 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -184,16 +184,6 @@
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:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 6e548b1..a43cfa8 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -35,15 +35,6 @@
~AudioStreamBuilder();
- aaudio_direction_t getDirection() const {
- return mDirection;
- }
-
- AudioStreamBuilder* setDirection(aaudio_direction_t direction) {
- mDirection = direction;
- return this;
- }
-
bool isSharingModeMatchRequired() const {
return mSharingModeMatchRequired;
}
@@ -113,7 +104,6 @@
private:
bool mSharingModeMatchRequired = false; // must match sharing mode requested
- aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
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 8d2c62d..a869886 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -210,3 +210,9 @@
return mFifo->getCapacity();
}
+void FifoBuffer::eraseMemory() {
+ int32_t numBytes = convertFramesToBytes(getBufferCapacityInFrames());
+ if (numBytes > 0) {
+ memset(mStorage, 0, (size_t) numBytes);
+ }
+}
diff --git a/media/libaaudio/src/fifo/FifoBuffer.h b/media/libaaudio/src/fifo/FifoBuffer.h
index a94e9b0..f5a9e27 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.h
+++ b/media/libaaudio/src/fifo/FifoBuffer.h
@@ -111,6 +111,11 @@
mFifo->setWriteCounter(n);
}
+ /*
+ * This is generally only called before or after the buffer is used.
+ */
+ void eraseMemory();
+
private:
void fillWrappingBuffer(WrappingBuffer *wrappingBuffer,
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index dd5e3c0..2816bac 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -94,12 +94,15 @@
} else {
audioBuffer->size = 0;
}
- break;
+
+ if (updateStateMachine() == AAUDIO_OK) {
+ break; // don't fall through
+ }
}
}
/// FALL THROUGH
- // Stream got rerouted so we disconnect.
+ // Stream got rerouted so we disconnect.
case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
ALOGD("processCallbackCommon() stream disconnected");
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 8e8070c..041280d 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -159,6 +159,9 @@
actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
}
setPerformanceMode(actualPerformanceMode);
+
+ setSharingMode(AAUDIO_SHARING_MODE_SHARED); // EXCLUSIVE mode not supported in legacy
+
// Log warning if we did not get what we asked for.
ALOGW_IF(actualFlags != flags,
"AudioStreamRecord::open() flags changed from 0x%08X to 0x%08X",
@@ -207,7 +210,7 @@
if (mAudioRecord.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- // Get current position so we can detect when the track is playing.
+ // Get current position so we can detect when the track is recording.
status_t err = mAudioRecord->getPosition(&mPositionWhenStarting);
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
@@ -235,7 +238,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamRecord::updateStateWhileWaiting()
+aaudio_result_t AudioStreamRecord::updateStateMachine()
{
aaudio_result_t result = AAUDIO_OK;
aaudio_wrapping_frames_t position;
@@ -292,6 +295,12 @@
}
int32_t framesRead = (int32_t)(bytesRead / bytesPerFrame);
incrementFramesRead(framesRead);
+
+ result = updateStateMachine();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
return (aaudio_result_t) framesRead;
}
@@ -330,3 +339,21 @@
}
return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
}
+
+int64_t AudioStreamRecord::getFramesWritten() {
+ aaudio_wrapping_frames_t position;
+ status_t result;
+ switch (getState()) {
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ case AAUDIO_STREAM_STATE_STOPPING:
+ result = mAudioRecord->getPosition(&position);
+ if (result == OK) {
+ mFramesWritten.update32(position);
+ }
+ break;
+ default:
+ break;
+ }
+ return AudioStreamLegacy::getFramesWritten();
+}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 2c6a7eb..c1723ba 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -59,9 +59,11 @@
int32_t getXRunCount() const override;
+ int64_t getFramesWritten() override;
+
int32_t getFramesPerBurst() const override;
- aaudio_result_t updateStateWhileWaiting() override;
+ aaudio_result_t updateStateMachine() override;
aaudio_direction_t getDirection() const override {
return AAUDIO_DIRECTION_INPUT;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 77f31e2..155362c 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -171,18 +171,20 @@
setDeviceId(mAudioTrack->getRoutedDeviceId());
mAudioTrack->addAudioDeviceCallback(mDeviceCallback);
- // Update performance mode based on the actual stream.
+ // Update performance mode based on the actual stream flags.
// For example, if the sample rate is not allowed then you won't get a FAST track.
audio_output_flags_t actualFlags = mAudioTrack->getFlags();
aaudio_performance_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
- if ((actualFlags & (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW))
- == (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW)) {
+ // We may not get the RAW flag. But as long as we get the FAST flag we can call it LOW_LATENCY.
+ if ((actualFlags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-
} else if ((actualFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
}
setPerformanceMode(actualPerformanceMode);
+
+ setSharingMode(AAUDIO_SHARING_MODE_SHARED); // EXCLUSIVE mode not supported in legacy
+
// Log warning if we did not get what we asked for.
ALOGW_IF(actualFlags != flags,
"AudioStreamTrack::open() flags changed from 0x%08X to 0x%08X",
@@ -227,6 +229,7 @@
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
+ ALOGE("AudioStreamTrack::requestStart() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
}
// Get current position so we can detect when the track is playing.
@@ -250,6 +253,7 @@
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
+ ALOGE("AudioStreamTrack::requestPause() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_STARTING
&& getState() != AAUDIO_STREAM_STATE_STARTED) {
@@ -271,8 +275,10 @@
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
+ ALOGE("AudioStreamTrack::requestFlush() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
+ ALOGE("AudioStreamTrack::requestFlush() not paused");
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_FLUSHING);
@@ -286,6 +292,7 @@
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
+ ALOGE("AudioStreamTrack::requestStop() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
}
onStop();
@@ -296,7 +303,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::updateStateWhileWaiting()
+aaudio_result_t AudioStreamTrack::updateStateMachine()
{
status_t err;
aaudio_wrapping_frames_t position;
@@ -373,6 +380,12 @@
}
int32_t framesWritten = (int32_t)(bytesWritten / bytesPerFrame);
incrementFramesWritten(framesWritten);
+
+ result = updateStateMachine();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
return framesWritten;
}
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index ff429ea..3230ac8 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -67,7 +67,7 @@
return AAUDIO_DIRECTION_OUTPUT;
}
- aaudio_result_t updateStateWhileWaiting() override;
+ aaudio_result_t updateStateMachine() override;
// This is public so it can be called from the C callback function.
void processCallback(int event, void *info) override;
@@ -81,8 +81,7 @@
// adapts between variable sized blocks and fixed size blocks
FixedBlockReader mFixedBlockReader;
- // TODO add 64-bit position reporting to AudioRecord and use it.
- aaudio_wrapping_frames_t mPositionWhenStarting = 0;
+ // TODO add 64-bit position reporting to AudioTrack and use it.
aaudio_wrapping_frames_t mPositionWhenPausing = 0;
};
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index acd319b..b0c6c94 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -258,4 +258,74 @@
}
}
+
+/**
+ * Simple double buffer for a structure that can be written occasionally and read occasionally.
+ * This allows a SINGLE writer with multiple readers.
+ *
+ * It is OK if the FIFO overflows and we lose old values.
+ * It is also OK if we read an old value.
+ * Thread may return a non-atomic result if the other thread is rapidly writing
+ * new values on another core.
+ */
+template <class T>
+class SimpleDoubleBuffer {
+public:
+ SimpleDoubleBuffer()
+ : mValues()
+ , mCounter(0) {}
+
+ __attribute__((no_sanitize("integer")))
+ void write(T value) {
+ int index = mCounter.load() & 1;
+ mValues[index] = value;
+ mCounter++; // Increment AFTER updating storage, OK if it wraps.
+ }
+
+ T read() const {
+ T result;
+ int before;
+ int after;
+ int timeout = 3;
+ do {
+ // Check to see if a write occurred while were reading.
+ before = mCounter.load();
+ int index = (before & 1) ^ 1;
+ result = mValues[index];
+ after = mCounter.load();
+ } while ((after != before) && --timeout > 0);
+ return result;
+ }
+
+ /**
+ * @return true if at least one value has been written
+ */
+ bool isValid() const {
+ return mCounter.load() > 0;
+ }
+
+private:
+ T mValues[2];
+ std::atomic<int> mCounter;
+};
+
+class Timestamp {
+public:
+ Timestamp()
+ : mPosition(0)
+ , mNanoseconds(0) {}
+ Timestamp(int64_t position, int64_t nanoseconds)
+ : mPosition(position)
+ , mNanoseconds(nanoseconds) {}
+
+ int64_t getPosition() const { return mPosition; }
+
+ int64_t getNanoseconds() const { return mNanoseconds; }
+
+private:
+ // These cannot be const because we need to implement the copy assignment operator.
+ int64_t mPosition;
+ int64_t mNanoseconds;
+};
+
#endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index e4eef06..4402919 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -34,6 +34,17 @@
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src \
+ frameworks/av/media/libaaudio/examples
+LOCAL_SRC_FILES:= test_timestamps.cpp
+LOCAL_SHARED_LIBRARIES := libaaudio
+LOCAL_MODULE := test_timestamps
+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_linear_ramp.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
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_timestamps.cpp b/media/libaaudio/tests/test_timestamps.cpp
new file mode 100644
index 0000000..d9ca391
--- /dev/null
+++ b/media/libaaudio/tests/test_timestamps.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 <unistd.h>
+
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+
+#include "utils/AAudioExampleUtils.h"
+
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+
+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;
+ int32_t originalBufferSize = 0;
+ int32_t requestedBufferSize = 0;
+ int32_t finalBufferSize = 0;
+ aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
+ aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ int32_t framesMax;
+ int64_t framesTotal;
+ int64_t printAt;
+ int samplesPerBurst;
+ int64_t previousFramePosition = -1;
+
+ 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("Test Timestamps V0.1.1\n");
+
+ AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+
+ // 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);
+ //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+ // 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);
+ printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
+ ? "yes" : "no");
+
+ // 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(" framesPerBurst = %3d\n", framesPerBurst);
+
+ originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
+ requestedBufferSize = 2 * framesPerBurst;
+ finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
+
+ printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
+ originalBufferSize, requestedBufferSize, finalBufferSize);
+
+ 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 very briefly.
+ framesMax = actualSampleRate * 4;
+ framesTotal = 0;
+ 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;
+ }
+ }
+
+ // Print timestamps.
+ int64_t framePosition = 0;
+ int64_t frameTime = 0;
+ aaudio_result_t timeResult;
+ timeResult = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC,
+ &framePosition, &frameTime);
+
+ if (timeResult == AAUDIO_OK) {
+ if (framePosition > (previousFramePosition + 5000)) {
+ int64_t realTime = getNanoseconds();
+ int64_t framesWritten = AAudioStream_getFramesWritten(aaudioStream);
+
+ double latencyMillis = calculateLatencyMillis(framePosition, frameTime,
+ framesWritten, realTime,
+ actualSampleRate);
+
+ printf("--- timestamp: result = %4d, position = %lld, at %lld nanos"
+ ", latency = %7.2f msec\n",
+ timeResult,
+ (long long) framePosition,
+ (long long) frameTime,
+ latencyMillis);
+ previousFramePosition = framePosition;
+ }
+ }
+ }
+
+ 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/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index a5f9ab6..b1cb0e7 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -135,7 +135,11 @@
&mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
- ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
+ char typeBuffer[64], uuidBuffer[64];
+ guidToString(type, typeBuffer, sizeof(typeBuffer));
+ guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
+ ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
+ typeBuffer, uuidBuffer, mStatus);
if (iEffect == 0) {
mStatus = NO_INIT;
}
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index e49945b..7bf4f99 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
@@ -675,7 +679,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;
}
@@ -761,7 +766,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) {
@@ -1056,7 +1064,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;
@@ -1068,8 +1078,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/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
index b49b975..61fb6bab 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -40,7 +40,7 @@
namespace android {
EffectHalHidl::EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId)
- : mEffect(effect), mEffectId(effectId), mBuffersChanged(true) {
+ : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) {
}
EffectHalHidl::~EffectHalHidl() {
@@ -49,6 +49,9 @@
mEffect.clear();
hardware::IPCThreadState::self()->flushCommands();
}
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
}
// static
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/StreamHalHidl.cpp
index 176abee..0cafa36 100644
--- a/media/libaudiohal/StreamHalHidl.cpp
+++ b/media/libaudiohal/StreamHalHidl.cpp
@@ -47,7 +47,8 @@
StreamHalHidl::StreamHalHidl(IStream *stream)
: ConversionHelperHidl("Stream"),
mStream(stream),
- mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT) {
+ mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT),
+ mCachedBufferSize(0){
// Instrument audio signal power logging.
// Note: This assumes channel mask, format, and sample rate do not change after creation.
@@ -73,7 +74,11 @@
status_t StreamHalHidl::getBufferSize(size_t *size) {
if (!mStream) return NO_INIT;
- return processReturn("getBufferSize", mStream->getBufferSize(), size);
+ status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size);
+ if (status == OK) {
+ mCachedBufferSize = *size;
+ }
+ return status;
}
status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
@@ -202,6 +207,14 @@
return OK;
}
+status_t StreamHalHidl::getCachedBufferSize(size_t *size) {
+ if (mCachedBufferSize != 0) {
+ *size = mCachedBufferSize;
+ return OK;
+ }
+ return getBufferSize(size);
+}
+
bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) {
if (mHalThreadPriority == HAL_THREAD_PRIORITY_DEFAULT) {
return true;
@@ -320,8 +333,19 @@
}
status_t status;
- if (!mDataMQ && (status = prepareForWriting(bytes)) != OK) {
- return status;
+ if (!mDataMQ) {
+ // In case if playback starts close to the end of a compressed track, the bytes
+ // that need to be written is less than the actual buffer size. Need to use
+ // full buffer size for the MQ since otherwise after seeking back to the middle
+ // data will be truncated.
+ size_t bufferSize;
+ if ((status = getCachedBufferSize(&bufferSize)) != OK) {
+ return status;
+ }
+ if (bytes > bufferSize) bufferSize = bytes;
+ if ((status = prepareForWriting(bufferSize)) != OK) {
+ return status;
+ }
}
status = callWriterThread(
diff --git a/media/libaudiohal/StreamHalHidl.h b/media/libaudiohal/StreamHalHidl.h
index a69cce8..d4ab943 100644
--- a/media/libaudiohal/StreamHalHidl.h
+++ b/media/libaudiohal/StreamHalHidl.h
@@ -102,6 +102,8 @@
// The destructor automatically closes the stream.
virtual ~StreamHalHidl();
+ status_t getCachedBufferSize(size_t *size);
+
bool requestHalThreadPriority(pid_t threadPid, pid_t threadId);
// mStreamPowerLog is used for audio signal power logging.
@@ -111,6 +113,7 @@
const int HAL_THREAD_PRIORITY_DEFAULT = -1;
IStream *mStream;
int mHalThreadPriority;
+ size_t mCachedBufferSize;
};
class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl {
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index 519f4a8..b4a1d77 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -563,6 +563,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 ab6b63c..ee9406d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -188,12 +188,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 */
@@ -428,9 +429,7 @@
size_t ii;
LVM_INT32 temp;
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
+ for (ii = 0; ii < n; ii++) {
temp = (LVM_INT32)((*src) * 32768.0f);
if (temp >= 32767) {
*dst = 32767;
@@ -439,8 +438,8 @@
} else {
*dst = (LVM_INT16)temp;
}
- src--;
- dst--;
+ src++;
+ dst++;
}
return;
}
@@ -1870,12 +1869,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;
@@ -1899,6 +1899,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");
@@ -1974,6 +1979,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
@@ -2144,7 +2174,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/libheif/Android.bp b/media/libheif/Android.bp
new file mode 100644
index 0000000..7d5a4eb
--- /dev/null
+++ b/media/libheif/Android.bp
@@ -0,0 +1,23 @@
+cc_library_shared {
+ name: "libheif",
+
+ srcs: [
+ "HeifDecoderImpl.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libmedia",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ include_dirs: [],
+
+ export_include_dirs: ["include"],
+}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
new file mode 100644
index 0000000..115baff
--- /dev/null
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -0,0 +1,435 @@
+/*
+ * 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 "HeifDecoderImpl"
+
+#include "HeifDecoderImpl.h"
+
+#include <stdio.h>
+
+#include <binder/IMemory.h>
+#include <drm/drm_framework_common.h>
+#include <media/IDataSource.h>
+#include <media/mediametadataretriever.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaSource.h>
+#include <private/media/VideoFrame.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+HeifDecoder* createHeifDecoder() {
+ return new android::HeifDecoderImpl();
+}
+
+namespace android {
+
+/*
+ * HeifDataSource
+ *
+ * Proxies data requests over IDataSource interface from MediaMetadataRetriever
+ * to the HeifStream interface we received from the heif decoder client.
+ */
+class HeifDataSource : public BnDataSource {
+public:
+ /*
+ * Constructs HeifDataSource; will take ownership of |stream|.
+ */
+ HeifDataSource(HeifStream* stream)
+ : mStream(stream), mEOS(false),
+ mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
+
+ ~HeifDataSource() override {}
+
+ /*
+ * Initializes internal resources.
+ */
+ bool init();
+
+ sp<IMemory> getIMemory() override { return mMemory; }
+ ssize_t readAt(off64_t offset, size_t size) override;
+ status_t getSize(off64_t* size) override ;
+ void close() {}
+ uint32_t getFlags() override { return 0; }
+ String8 toString() override { return String8("HeifDataSource"); }
+ sp<DecryptHandle> DrmInitialization(const char*) override {
+ return nullptr;
+ }
+
+private:
+ enum {
+ /*
+ * Buffer size for passing the read data to mediaserver. Set to 64K
+ * (which is what MediaDataSource Java API's jni implementation uses).
+ */
+ kBufferSize = 64 * 1024,
+ /*
+ * Initial and max cache buffer size.
+ */
+ kInitialCacheBufferSize = 4 * 1024 * 1024,
+ kMaxCacheBufferSize = 64 * 1024 * 1024,
+ };
+ sp<IMemory> mMemory;
+ std::unique_ptr<HeifStream> mStream;
+ bool mEOS;
+ std::unique_ptr<uint8_t> mCache;
+ off64_t mCachedOffset;
+ size_t mCachedSize;
+ size_t mCacheBufferSize;
+};
+
+bool HeifDataSource::init() {
+ sp<MemoryDealer> memoryDealer =
+ new MemoryDealer(kBufferSize, "HeifDataSource");
+ mMemory = memoryDealer->allocate(kBufferSize);
+ if (mMemory == nullptr) {
+ ALOGE("Failed to allocate shared memory!");
+ return false;
+ }
+ mCache.reset(new uint8_t[kInitialCacheBufferSize]);
+ if (mCache.get() == nullptr) {
+ ALOGE("mFailed to allocate cache!");
+ return false;
+ }
+ mCacheBufferSize = kInitialCacheBufferSize;
+ return true;
+}
+
+ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
+ ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
+
+ if (offset < mCachedOffset) {
+ // try seek, then rewind/skip, fail if none worked
+ if (mStream->seek(offset)) {
+ ALOGV("readAt: seek to offset=%lld", (long long)offset);
+ mCachedOffset = offset;
+ mCachedSize = 0;
+ mEOS = false;
+ } else if (mStream->rewind()) {
+ ALOGV("readAt: rewind to offset=0");
+ mCachedOffset = 0;
+ mCachedSize = 0;
+ mEOS = false;
+ } else {
+ ALOGE("readAt: couldn't seek or rewind!");
+ mEOS = true;
+ }
+ }
+
+ if (mEOS && (offset < mCachedOffset ||
+ offset >= (off64_t)(mCachedOffset + mCachedSize))) {
+ ALOGV("readAt: EOS");
+ return ERROR_END_OF_STREAM;
+ }
+
+ // at this point, offset must be >= mCachedOffset, other cases should
+ // have been caught above.
+ CHECK(offset >= mCachedOffset);
+
+ if (size == 0) {
+ return 0;
+ }
+
+ // Can only read max of kBufferSize
+ if (size > kBufferSize) {
+ size = kBufferSize;
+ }
+
+ // copy from cache if the request falls entirely in cache
+ if (offset + size <= mCachedOffset + mCachedSize) {
+ memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ return size;
+ }
+
+ // need to fetch more, check if we need to expand the cache buffer.
+ if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
+ // it's reaching max cache buffer size, need to roll window, and possibly
+ // expand the cache buffer.
+ size_t newCacheBufferSize = mCacheBufferSize;
+ std::unique_ptr<uint8_t> newCache;
+ uint8_t* dst = mCache.get();
+ if (newCacheBufferSize < kMaxCacheBufferSize) {
+ newCacheBufferSize = kMaxCacheBufferSize;
+ newCache.reset(new uint8_t[newCacheBufferSize]);
+ dst = newCache.get();
+ }
+
+ // when rolling the cache window, try to keep about half the old bytes
+ // in case that the client goes back.
+ off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
+ if (newCachedOffset < mCachedOffset) {
+ newCachedOffset = mCachedOffset;
+ }
+
+ int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
+ if (newCachedSize > 0) {
+ // in this case, the new cache region partially overlop the old cache,
+ // move the portion of the cache we want to save to the beginning of
+ // the cache buffer.
+ memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
+ } else if (newCachedSize < 0){
+ // in this case, the new cache region is entirely out of the old cache,
+ // in order to guarantee sequential read, we need to skip a number of
+ // bytes before reading.
+ size_t bytesToSkip = -newCachedSize;
+ size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
+ if (bytesSkipped != bytesToSkip) {
+ // bytesSkipped is invalid, there is not enough bytes to reach
+ // the requested offset.
+ ALOGE("readAt: skip failed, EOS");
+
+ mEOS = true;
+ mCachedOffset = newCachedOffset;
+ mCachedSize = 0;
+ return ERROR_END_OF_STREAM;
+ }
+ // set cache size to 0, since we're not keeping any old cache
+ newCachedSize = 0;
+ }
+
+ if (newCache.get() != nullptr) {
+ mCache.reset(newCache.release());
+ mCacheBufferSize = newCacheBufferSize;
+ }
+ mCachedOffset = newCachedOffset;
+ mCachedSize = newCachedSize;
+
+ ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
+ (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
+ } else {
+ // expand cache buffer, but no need to roll the window
+ size_t newCacheBufferSize = mCacheBufferSize;
+ while (offset + size > mCachedOffset + newCacheBufferSize) {
+ newCacheBufferSize *= 2;
+ }
+ CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
+ if (mCacheBufferSize < newCacheBufferSize) {
+ uint8_t* newCache = new uint8_t[newCacheBufferSize];
+ memcpy(newCache, mCache.get(), mCachedSize);
+ mCache.reset(newCache);
+ mCacheBufferSize = newCacheBufferSize;
+
+ ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
+ (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
+ }
+ }
+ size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
+ size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
+ if (bytesRead > bytesToRead || bytesRead == 0) {
+ // bytesRead is invalid
+ mEOS = true;
+ bytesRead = 0;
+ } else if (bytesRead < bytesToRead) {
+ // read some bytes but not all, set EOS
+ mEOS = true;
+ }
+ mCachedSize += bytesRead;
+ ALOGV("readAt: current cache window (%lld, %zu)",
+ (long long) mCachedOffset, mCachedSize);
+
+ // here bytesAvailable could be negative if offset jumped past EOS.
+ int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
+ if (bytesAvailable <= 0) {
+ return ERROR_END_OF_STREAM;
+ }
+ if (bytesAvailable < (int64_t)size) {
+ size = bytesAvailable;
+ }
+ memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ return size;
+}
+
+status_t HeifDataSource::getSize(off64_t* size) {
+ if (!mStream->hasLength()) {
+ *size = -1;
+ ALOGE("getSize: not supported!");
+ return ERROR_UNSUPPORTED;
+ }
+ *size = mStream->getLength();
+ ALOGV("getSize: size=%lld", (long long)*size);
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+HeifDecoderImpl::HeifDecoderImpl() :
+ // output color format should always be set via setOutputColor(), in case
+ // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
+ mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
+ mCurScanline(0),
+ mFrameDecoded(false) {
+}
+
+HeifDecoderImpl::~HeifDecoderImpl() {
+}
+
+bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
+ mFrameDecoded = false;
+ sp<HeifDataSource> dataSource = new HeifDataSource(stream);
+ if (!dataSource->init()) {
+ return false;
+ }
+ mDataSource = dataSource;
+
+ mRetriever = new MediaMetadataRetriever();
+ status_t err = mRetriever->setDataSource(mDataSource, "video/mp4");
+ if (err != OK) {
+ ALOGE("failed to set data source!");
+
+ mRetriever.clear();
+ mDataSource.clear();
+ return false;
+ }
+ ALOGV("successfully set data source.");
+
+ const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
+ if (!hasVideo || strcasecmp(hasVideo, "yes")) {
+ ALOGE("no video: %s", hasVideo ? hasVideo : "null");
+ return false;
+ }
+
+ mFrameMemory = mRetriever->getFrameAtTime(0,
+ IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
+ mOutputColor, true /*metaOnly*/);
+ if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ ALOGE("getFrameAtTime: videoFrame is a nullptr");
+ return false;
+ }
+
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+
+ ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
+ videoFrame->mWidth,
+ videoFrame->mHeight,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mIccSize);
+
+ if (frameInfo != nullptr) {
+ frameInfo->set(
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mBytesPerPixel,
+ videoFrame->mIccSize,
+ videoFrame->getFlattenedIccData());
+ }
+ return true;
+}
+
+bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
+ ALOGW("getEncodedColor: not implemented!");
+ return false;
+}
+
+bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
+ switch(heifColor) {
+ case kHeifColorFormat_RGB565:
+ {
+ mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
+ return true;
+ }
+ case kHeifColorFormat_RGBA_8888:
+ {
+ mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
+ return true;
+ }
+ case kHeifColorFormat_BGRA_8888:
+ {
+ mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
+ return true;
+ }
+ default:
+ break;
+ }
+ ALOGE("Unsupported output color format %d", heifColor);
+ return false;
+}
+
+bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
+ // reset scanline pointer
+ mCurScanline = 0;
+
+ if (mFrameDecoded) {
+ return true;
+ }
+
+ mFrameMemory = mRetriever->getFrameAtTime(0,
+ IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
+ if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ ALOGE("getFrameAtTime: videoFrame is a nullptr");
+ return false;
+ }
+
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ if (videoFrame->mSize == 0 ||
+ mFrameMemory->size() < videoFrame->getFlattenedSize()) {
+ ALOGE("getFrameAtTime: videoFrame size is invalid");
+ return false;
+ }
+
+ ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
+ videoFrame->mWidth,
+ videoFrame->mHeight,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mRowBytes,
+ videoFrame->mSize);
+
+ if (frameInfo != nullptr) {
+ frameInfo->set(
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mRotationAngle,
+ videoFrame->mBytesPerPixel,
+ videoFrame->mIccSize,
+ videoFrame->getFlattenedIccData());
+ }
+ mFrameDecoded = true;
+ return true;
+}
+
+bool HeifDecoderImpl::getScanline(uint8_t* dst) {
+ if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ return false;
+ }
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ if (mCurScanline >= videoFrame->mDisplayHeight) {
+ ALOGE("no more scanline available");
+ return false;
+ }
+ uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
+ memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
+ return true;
+}
+
+size_t HeifDecoderImpl::skipScanlines(size_t count) {
+ if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ return 0;
+ }
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+
+ uint32_t oldScanline = mCurScanline;
+ mCurScanline += count;
+ if (mCurScanline > videoFrame->mDisplayHeight) {
+ mCurScanline = videoFrame->mDisplayHeight;
+ }
+ return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
+}
+
+} // namespace android
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
new file mode 100644
index 0000000..c2e4ff3
--- /dev/null
+++ b/media/libheif/HeifDecoderImpl.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _HEIF_DECODER_IMPL_
+#define _HEIF_DECODER_IMPL_
+
+#include "include/HeifDecoderAPI.h"
+#include <system/graphics.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class IDataSource;
+class IMemory;
+class MediaMetadataRetriever;
+
+/*
+ * An implementation of HeifDecoder based on Android's MediaMetadataRetriever.
+ */
+class HeifDecoderImpl : public HeifDecoder {
+public:
+
+ HeifDecoderImpl();
+ ~HeifDecoderImpl() override;
+
+ bool init(HeifStream* stream, HeifFrameInfo* frameInfo) override;
+
+ bool getEncodedColor(HeifEncodedColor* outColor) const override;
+
+ bool setOutputColor(HeifColorFormat heifColor) override;
+
+ bool decode(HeifFrameInfo* frameInfo) override;
+
+ bool getScanline(uint8_t* dst) override;
+
+ size_t skipScanlines(size_t count) override;
+
+private:
+ sp<IDataSource> mDataSource;
+ sp<MediaMetadataRetriever> mRetriever;
+ sp<IMemory> mFrameMemory;
+ android_pixel_format_t mOutputColor;
+ size_t mCurScanline;
+ bool mFrameDecoded;
+};
+
+} // namespace android
+
+#endif // _HEIF_DECODER_IMPL_
diff --git a/media/libheif/include/HeifDecoderAPI.h b/media/libheif/include/HeifDecoderAPI.h
new file mode 100644
index 0000000..5183c39
--- /dev/null
+++ b/media/libheif/include/HeifDecoderAPI.h
@@ -0,0 +1,181 @@
+/*
+ * 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 _HEIF_DECODER_API_
+#define _HEIF_DECODER_API_
+
+#include <memory>
+
+/*
+ * The output color pixel format of heif decoder.
+ */
+typedef enum {
+ kHeifColorFormat_RGB565 = 0,
+ kHeifColorFormat_RGBA_8888 = 1,
+ kHeifColorFormat_BGRA_8888 = 2,
+} HeifColorFormat;
+
+/*
+ * The color spaces encoded in the heif image.
+ */
+typedef enum {
+ kHeifEncodedColor_RGB = 0,
+ kHeifEncodedColor_YUV = 1,
+ kHeifEncodedColor_CMYK = 2,
+} HeifEncodedColor;
+
+/*
+ * Represents a color converted (RGB-based) video frame
+ */
+struct HeifFrameInfo
+{
+ HeifFrameInfo() :
+ mWidth(0), mHeight(0), mRotationAngle(0), mBytesPerPixel(0),
+ mIccSize(0), mIccData(nullptr) {}
+
+ // update the frame info, will make a copy of |iccData| internally
+ void set(uint32_t width, uint32_t height, int32_t rotation, uint32_t bpp,
+ uint32_t iccSize, uint8_t* iccData) {
+ mWidth = width;
+ mHeight = height;
+ mRotationAngle = rotation;
+ mBytesPerPixel = bpp;
+
+ if (mIccData != nullptr) {
+ mIccData.reset(nullptr);
+ }
+ mIccSize = iccSize;
+ if (iccSize > 0) {
+ mIccData.reset(new uint8_t[iccSize]);
+ if (mIccData.get() != nullptr) {
+ memcpy(mIccData.get(), iccData, iccSize);
+ } else {
+ mIccSize = 0;
+ }
+ }
+ }
+
+ // Intentional public access modifiers:
+ uint32_t mWidth;
+ uint32_t mHeight;
+ int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
+ uint32_t mBytesPerPixel; // Number of bytes for one pixel
+ uint32_t mIccSize; // Number of bytes in mIccData
+ std::unique_ptr<uint8_t> mIccData; // Actual ICC data, memory is owned by this structure
+};
+
+/*
+ * Abstract interface to provide data to HeifDecoder.
+ */
+struct HeifStream {
+ HeifStream() {}
+
+ virtual ~HeifStream() {}
+
+ /*
+ * Reads or skips size number of bytes. return the number of bytes actually
+ * read or skipped.
+ * If |buffer| == NULL, skip size bytes, return how many were skipped.
+ * If |buffer| != NULL, copy size bytes into buffer, return how many were copied.
+ */
+ virtual size_t read(void* buffer, size_t size) = 0;
+
+ /*
+ * Rewinds to the beginning of the stream. Returns true if the stream is known
+ * to be at the beginning after this call returns.
+ */
+ virtual bool rewind() = 0;
+
+ /*
+ * Seeks to an absolute position in the stream. If this cannot be done, returns false.
+ * If an attempt is made to seek past the end of the stream, the position will be set
+ * to the end of the stream.
+ */
+ virtual bool seek(size_t /*position*/) = 0;
+
+ /** Returns true if this stream can report its total length. */
+ virtual bool hasLength() const = 0;
+
+ /** Returns the total length of the stream. If this cannot be done, returns 0. */
+ virtual size_t getLength() const = 0;
+
+private:
+ HeifStream(const HeifFrameInfo&) = delete;
+ HeifStream& operator=(const HeifFrameInfo&) = delete;
+};
+
+/*
+ * Abstract interface to decode heif images from a HeifStream data source.
+ */
+struct HeifDecoder {
+ HeifDecoder() {}
+
+ virtual ~HeifDecoder() {}
+
+ /*
+ * Returns true if it successfully sets outColor to the encoded color,
+ * and false otherwise.
+ */
+ virtual bool getEncodedColor(HeifEncodedColor* outColor) const = 0;
+
+ /*
+ * Returns true if it successfully sets the output color format to color,
+ * and false otherwise.
+ */
+ virtual bool setOutputColor(HeifColorFormat color) = 0;
+
+ /*
+ * Returns true if it successfully initialize heif decoder with source,
+ * and false otherwise. |frameInfo| will be filled with information of
+ * the primary picture upon success and unmodified upon failure.
+ * Takes ownership of |stream| regardless of result.
+ */
+ virtual bool init(HeifStream* stream, HeifFrameInfo* frameInfo) = 0;
+
+ /*
+ * Decode the picture internally, returning whether it succeeded. |frameInfo|
+ * will be filled with information of the primary picture upon success and
+ * unmodified upon failure.
+ *
+ * After this succeeded, getScanline can be called to read the scanlines
+ * that were decoded.
+ */
+ virtual bool decode(HeifFrameInfo* frameInfo) = 0;
+
+ /*
+ * Read the next scanline (in top-down order), returns true upon success
+ * and false otherwise.
+ */
+ virtual bool getScanline(uint8_t* dst) = 0;
+
+ /*
+ * Skip the next |count| scanlines, returns true upon success and
+ * false otherwise.
+ */
+ virtual size_t skipScanlines(size_t count) = 0;
+
+private:
+ HeifDecoder(const HeifFrameInfo&) = delete;
+ HeifDecoder& operator=(const HeifFrameInfo&) = delete;
+};
+
+/*
+ * Creates a HeifDecoder. Returns a HeifDecoder instance upon success, or NULL
+ * if the creation has failed.
+ */
+HeifDecoder* createHeifDecoder();
+
+#endif // _HEIF_DECODER_API_
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index fb4fe4b..a462f3a 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -15,6 +15,9 @@
cc_library {
name: "libmedia_helper",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["AudioParameter.cpp", "TypeConverter.cpp"],
cflags: [
"-Werror",
@@ -121,6 +124,9 @@
cc_library_shared {
name: "libmedia_omx",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
defaults: ["libmedia_omx_defaults"],
}
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 7058ee8..5ea2e8b 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -127,22 +127,32 @@
return reply.readInt32();
}
- status_t setDataSource(const sp<IDataSource>& source)
+ status_t setDataSource(const sp<IDataSource>& source, const char *mime)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(source));
+
+ if (mime != NULL) {
+ data.writeInt32(1);
+ data.writeCString(mime);
+ } else {
+ data.writeInt32(0);
+ }
remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
return reply.readInt32();
}
- sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
+ sp<IMemory> getFrameAtTime(int64_t timeUs, int option, int colorFormat, bool metaOnly)
{
- ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
+ ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d) metaOnly(%d)",
+ timeUs, option, colorFormat, metaOnly);
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
data.writeInt64(timeUs);
data.writeInt32(option);
+ data.writeInt32(colorFormat);
+ data.writeInt32(metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
sendSchedPolicy(data);
#endif
@@ -258,7 +268,12 @@
if (source == NULL) {
reply->writeInt32(BAD_VALUE);
} else {
- reply->writeInt32(setDataSource(source));
+ int32_t hasMime = data.readInt32();
+ const char *mime = NULL;
+ if (hasMime) {
+ mime = data.readCString();
+ }
+ reply->writeInt32(setDataSource(source, mime));
}
return NO_ERROR;
} break;
@@ -266,11 +281,14 @@
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
int64_t timeUs = data.readInt64();
int option = data.readInt32();
- ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
+ int colorFormat = data.readInt32();
+ bool metaOnly = (data.readInt32() != 0);
+ ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d), metaOnly(%d)",
+ timeUs, option, colorFormat, metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
setSchedPolicy(data);
#endif
- sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
+ sp<IMemory> bitmap = getFrameAtTime(timeUs, option, colorFormat, metaOnly);
if (bitmap != 0) { // Don't send NULL across the binder interface
reply->writeInt32(NO_ERROR);
reply->writeStrongBinder(IInterface::asBinder(bitmap));
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 1f188f3..2a74512 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -105,11 +105,17 @@
ProfileLevel profileLevel;
profileLevel.mProfile = profile;
profileLevel.mLevel = level;
- mProfileLevels.push_back(profileLevel);
+ if (mProfileLevelsSorted.indexOf(profileLevel) < 0) {
+ mProfileLevels.push_back(profileLevel);
+ mProfileLevelsSorted.add(profileLevel);
+ }
}
void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
- mColorFormats.push(format);
+ if (mColorFormatsSorted.indexOf(format) < 0) {
+ mColorFormats.push(format);
+ mColorFormatsSorted.add(format);
+ }
}
void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
diff --git a/media/libmedia/MediaUtils.cpp b/media/libmedia/MediaUtils.cpp
index dc2bc82..bcdc3bd 100644
--- a/media/libmedia/MediaUtils.cpp
+++ b/media/libmedia/MediaUtils.cpp
@@ -24,6 +24,8 @@
#include "MediaUtils.h"
+extern "C" size_t __cfi_shadow_size();
+
namespace android {
void limitProcessMemory(
@@ -62,6 +64,19 @@
if (propVal > 0 && uint64_t(propVal) <= SIZE_MAX) {
maxMem = propVal;
}
+
+ // Increase by the size of the CFI shadow mapping. Most of the shadow is not
+ // backed with physical pages, and it is possible for the result to be
+ // higher than total physical memory. This is fine for RLIMIT_AS.
+ size_t cfi_size = __cfi_shadow_size();
+ if (cfi_size) {
+ ALOGV("cfi shadow size: %zu", cfi_size);
+ if (maxMem <= SIZE_MAX - cfi_size) {
+ maxMem += cfi_size;
+ } else {
+ maxMem = SIZE_MAX;
+ }
+ }
ALOGV("actual limit: %zu", maxMem);
struct rlimit limit;
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index a6eba86..e6c8f9c 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -375,7 +375,7 @@
audio_channel_mask_t channelMaskFromString(const std::string &literalChannels)
{
audio_channel_mask_t channels;
- if (!OutputChannelConverter::fromString(literalChannels, channels) ||
+ if (!OutputChannelConverter::fromString(literalChannels, channels) &&
!InputChannelConverter::fromString(literalChannels, channels)) {
return AUDIO_CHANNEL_INVALID;
}
diff --git a/media/libmedia/include/media/IDataSource.h b/media/libmedia/include/media/IDataSource.h
index 655f337..3858f78 100644
--- a/media/libmedia/include/media/IDataSource.h
+++ b/media/libmedia/include/media/IDataSource.h
@@ -35,7 +35,9 @@
// Get the memory that readAt writes into.
virtual sp<IMemory> getIMemory() = 0;
// Read up to |size| bytes into the memory returned by getIMemory(). Returns
- // the number of bytes read, or -1 on error. |size| must not be larger than
+ // the number of bytes read, or negative value on error (eg.
+ // ERROR_END_OF_STREAM indicating EOS. This is needed by CallbackDataSource
+ // to properly handle reading of last chunk). |size| must not be larger than
// the buffer.
virtual ssize_t readAt(off64_t offset, size_t size) = 0;
// Get the size, or -1 if the size is unknown.
diff --git a/media/libmedia/include/media/IMediaMetadataRetriever.h b/media/libmedia/include/media/IMediaMetadataRetriever.h
index c90f254..ea95161 100644
--- a/media/libmedia/include/media/IMediaMetadataRetriever.h
+++ b/media/libmedia/include/media/IMediaMetadataRetriever.h
@@ -19,13 +19,12 @@
#define ANDROID_IMEDIAMETADATARETRIEVER_H
#include <binder/IInterface.h>
-#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
namespace android {
-
+class Parcel;
class IDataSource;
struct IMediaHTTPService;
@@ -41,8 +40,10 @@
const KeyedVector<String8, String8> *headers = NULL) = 0;
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
- virtual status_t setDataSource(const sp<IDataSource>& dataSource) = 0;
- virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option) = 0;
+ virtual status_t setDataSource(
+ const sp<IDataSource>& dataSource, const char *mime) = 0;
+ virtual sp<IMemory> getFrameAtTime(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly) = 0;
virtual sp<IMemory> extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h
index d868860..e69c02d 100644
--- a/media/libmedia/include/media/IOMX.h
+++ b/media/libmedia/include/media/IOMX.h
@@ -31,6 +31,7 @@
#include <media/openmax/OMX_Core.h>
#include <media/openmax/OMX_Video.h>
+#include <media/openmax/OMX_VideoExt.h>
namespace android {
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index 6b50f22..ef641d2 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -40,6 +40,9 @@
struct ProfileLevel {
uint32_t mProfile;
uint32_t mLevel;
+ bool operator <(const ProfileLevel &o) const {
+ return mProfile < o.mProfile || (mProfile == o.mProfile && mLevel < o.mLevel);
+ }
};
struct Capabilities : public RefBase {
@@ -61,7 +64,9 @@
protected:
Vector<ProfileLevel> mProfileLevels;
+ SortedVector<ProfileLevel> mProfileLevelsSorted;
Vector<uint32_t> mColorFormats;
+ SortedVector<uint32_t> mColorFormatsSorted;
uint32_t mFlags;
sp<AMessage> mDetails;
diff --git a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
index a5e1350..257002d 100644
--- a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
+++ b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
@@ -41,8 +41,9 @@
const KeyedVector<String8, String8> *headers = NULL) = 0;
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
- virtual status_t setDataSource(const sp<DataSource>& source) = 0;
- virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
+ virtual status_t setDataSource(const sp<DataSource>& source, const char *mime) = 0;
+ virtual VideoFrame* getFrameAtTime(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly) = 0;
virtual MediaAlbumArt* extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
@@ -54,7 +55,9 @@
MediaMetadataRetrieverInterface() {}
virtual ~MediaMetadataRetrieverInterface() {}
- virtual VideoFrame* getFrameAtTime(int64_t /*timeUs*/, int /*option*/) { return NULL; }
+ virtual VideoFrame* getFrameAtTime(
+ int64_t /*timeUs*/, int /*option*/, int /*colorFormat*/, bool /*metaOnly*/)
+ { return NULL; }
virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
virtual const char* extractMetadata(int /*keyCode*/) { return NULL; }
};
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index 8ed07ee..65c266b 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -76,8 +76,10 @@
const KeyedVector<String8, String8> *headers = NULL);
status_t setDataSource(int fd, int64_t offset, int64_t length);
- status_t setDataSource(const sp<IDataSource>& dataSource);
- sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
+ status_t setDataSource(
+ const sp<IDataSource>& dataSource, const char *mime = NULL);
+ sp<IMemory> getFrameAtTime(int64_t timeUs, int option,
+ int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
sp<IMemory> extractAlbumArt();
const char* extractMetadata(int keyCode);
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 08a9e6a..7d27d57 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -130,7 +130,7 @@
}
status_t MediaMetadataRetriever::setDataSource(
- const sp<IDataSource>& dataSource)
+ const sp<IDataSource>& dataSource, const char *mime)
{
ALOGV("setDataSource(IDataSource)");
Mutex::Autolock _l(mLock);
@@ -138,18 +138,20 @@
ALOGE("retriever is not initialized");
return INVALID_OPERATION;
}
- return mRetriever->setDataSource(dataSource);
+ return mRetriever->setDataSource(dataSource, mime);
}
-sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
+sp<IMemory> MediaMetadataRetriever::getFrameAtTime(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly)
{
- ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option);
+ ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d) colorFormat(%d) metaOnly(%d)",
+ timeUs, option, colorFormat, metaOnly);
Mutex::Autolock _l(mLock);
if (mRetriever == 0) {
ALOGE("retriever is not initialized");
return NULL;
}
- return mRetriever->getFrameAtTime(timeUs, option);
+ return mRetriever->getFrameAtTime(timeUs, option, colorFormat, metaOnly);
}
const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 43881b3..47a147e 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -120,6 +120,8 @@
// key as part of constructor
dst->mPid = this->mPid;
dst->mUid = this->mUid;
+ dst->mPkgName = this->mPkgName;
+ dst->mPkgVersionCode = this->mPkgVersionCode;
dst->mSessionID = this->mSessionID;
dst->mTimestamp = this->mTimestamp;
dst->mFinalized = this->mFinalized;
@@ -201,6 +203,24 @@
return mUid;
}
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(AString pkgName) {
+ mPkgName = pkgName;
+ return *this;
+}
+
+AString MediaAnalyticsItem::getPkgName() const {
+ return mPkgName;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int32_t pkgVersionCode) {
+ mPkgVersionCode = pkgVersionCode;
+ return *this;
+}
+
+int32_t MediaAnalyticsItem::getPkgVersionCode() const {
+ return mPkgVersionCode;
+}
+
// this key is for the overall record -- "codec", "player", "drm", etc
MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
mKey = key;
@@ -263,11 +283,29 @@
i = mPropCount++;
prop = &mProps[i];
prop->setName(name, len);
+ prop->mType = kTypeNone; // make caller set type info
}
return prop;
}
+// used within the summarizers; return whether property existed
+bool MediaAnalyticsItem::removeProp(const char *name) {
+ size_t len = strlen(name);
+ size_t i = findPropIndex(name, len);
+ if (i < mPropCount) {
+ Prop *prop = &mProps[i];
+ clearProp(prop);
+ if (i != mPropCount-1) {
+ // in the middle, bring last one down to fill gap
+ mProps[i] = mProps[mPropCount-1];
+ }
+ mPropCount--;
+ return true;
+ }
+ return false;
+}
+
// set the values
void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
Prop *prop = allocateProp(name);
@@ -568,6 +606,10 @@
// into 'this' object
// .. we make a copy of the string to put away.
mKey = data.readCString();
+ mPid = data.readInt32();
+ mUid = data.readInt32();
+ mPkgName = data.readCString();
+ mPkgVersionCode = data.readInt32();
mSessionID = data.readInt64();
mFinalized = data.readInt32();
mTimestamp = data.readInt64();
@@ -611,6 +653,10 @@
data->writeCString(mKey.c_str());
+ data->writeInt32(mPid);
+ data->writeInt32(mUid);
+ data->writeCString(mPkgName.c_str());
+ data->writeInt32(mPkgVersionCode);
data->writeInt64(mSessionID);
data->writeInt32(mFinalized);
data->writeInt64(mTimestamp);
@@ -651,21 +697,54 @@
AString MediaAnalyticsItem::toString() {
+ return toString(-1);
+}
- AString result = "(";
+AString MediaAnalyticsItem::toString(int version) {
+
+ // v0 : released with 'o'
+ // v1 : bug fix (missing pid/finalized separator),
+ // adds apk name, apk version code
+
+ if (version <= PROTO_FIRST) {
+ // default to original v0 format, until proper parsers are in place
+ version = PROTO_V0;
+ } else if (version > PROTO_LAST) {
+ version = PROTO_LAST;
+ }
+
+ AString result;
char buffer[512];
+ if (version == PROTO_V0) {
+ result = "(";
+ } else {
+ snprintf(buffer, sizeof(buffer), "[%d:", version);
+ result.append(buffer);
+ }
+
// same order as we spill into the parcel, although not required
// key+session are our primary matching criteria
- //RBE ALOGD("mKey.c_str");
result.append(mKey.c_str());
- //RBE ALOGD("post-mKey.c_str");
result.append(":");
snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
result.append(buffer);
- // we need these internally, but don't want to upload them
- snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
+ snprintf(buffer, sizeof(buffer), "%d:", mUid);
+ result.append(buffer);
+
+ if (version >= PROTO_V1) {
+ result.append(mPkgName);
+ snprintf(buffer, sizeof(buffer), ":%d:", mPkgVersionCode);
+ result.append(buffer);
+ }
+
+ // in 'o' (v1) , the separator between pid and finalized was omitted
+ if (version <= PROTO_V0) {
+ snprintf(buffer, sizeof(buffer), "%d", mPid);
+ } else {
+ snprintf(buffer, sizeof(buffer), "%d:", mPid);
+ }
result.append(buffer);
snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
@@ -713,7 +792,11 @@
result.append(buffer);
}
- result.append(")");
+ if (version == PROTO_V0) {
+ result.append(")");
+ } else {
+ result.append("]");
+ }
return result;
}
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index dc501b2..41b9658 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -75,6 +75,14 @@
typedef const char *Attr;
+ enum {
+ PROTO_V0 = 0,
+ PROTO_FIRST = PROTO_V0,
+ PROTO_V1 = 1,
+ PROTO_LAST = PROTO_V1,
+ };
+
+
public:
// access functions for the class
@@ -161,11 +169,18 @@
MediaAnalyticsItem &setUid(uid_t);
uid_t getUid() const;
+ MediaAnalyticsItem &setPkgName(AString);
+ AString getPkgName() const;
+
+ MediaAnalyticsItem &setPkgVersionCode(int32_t);
+ int32_t getPkgVersionCode() const;
+
// our serialization code for binder calls
int32_t writeToParcel(Parcel *);
int32_t readFromParcel(const Parcel&);
AString toString();
+ AString toString(int version);
// are we collecting analytics data
static bool isEnabled();
@@ -188,6 +203,8 @@
// to help validate that A doesn't mess with B's records
pid_t mPid;
uid_t mUid;
+ AString mPkgName;
+ int32_t mPkgVersionCode;
// let's reuse a binder connection
static sp<IMediaAnalyticsService> sAnalyticsService;
@@ -228,6 +245,7 @@
size_t findPropIndex(const char *name, size_t len);
Prop *findProp(const char *name);
Prop *allocateProp(const char *name);
+ bool removeProp(const char *name);
size_t mPropCount;
size_t mPropSize;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 793f476..5a468f3 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -175,7 +175,7 @@
}
status_t MetadataRetrieverClient::setDataSource(
- const sp<IDataSource>& source)
+ const sp<IDataSource>& source, const char *mime)
{
ALOGV("setDataSource(IDataSource)");
Mutex::Autolock lock(mLock);
@@ -186,16 +186,18 @@
ALOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) return NO_INIT;
- status_t ret = p->setDataSource(dataSource);
+ status_t ret = p->setDataSource(dataSource, mime);
if (ret == NO_ERROR) mRetriever = p;
return ret;
}
Mutex MetadataRetrieverClient::sLock;
-sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option)
+sp<IMemory> MetadataRetrieverClient::getFrameAtTime(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly)
{
- ALOGV("getFrameAtTime: time(%lld us) option(%d)", (long long)timeUs, option);
+ ALOGV("getFrameAtTime: time(%lld us) option(%d) colorFormat(%d), metaOnly(%d)",
+ (long long)timeUs, option, colorFormat, metaOnly);
Mutex::Autolock lock(mLock);
Mutex::Autolock glock(sLock);
mThumbnail.clear();
@@ -203,12 +205,13 @@
ALOGE("retriever is not initialized");
return NULL;
}
- VideoFrame *frame = mRetriever->getFrameAtTime(timeUs, option);
+ VideoFrame *frame = mRetriever->getFrameAtTime(
+ timeUs, option, colorFormat, metaOnly);
if (frame == NULL) {
ALOGE("failed to capture a video frame");
return NULL;
}
- size_t size = sizeof(VideoFrame) + frame->mSize;
+ size_t size = frame->getFlattenedSize();
sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient");
if (heap == NULL) {
ALOGE("failed to create MemoryDealer");
@@ -222,16 +225,7 @@
return NULL;
}
VideoFrame *frameCopy = static_cast<VideoFrame *>(mThumbnail->pointer());
- frameCopy->mWidth = frame->mWidth;
- frameCopy->mHeight = frame->mHeight;
- frameCopy->mDisplayWidth = frame->mDisplayWidth;
- frameCopy->mDisplayHeight = frame->mDisplayHeight;
- frameCopy->mSize = frame->mSize;
- frameCopy->mRotationAngle = frame->mRotationAngle;
- ALOGV("rotation: %d", frameCopy->mRotationAngle);
- frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame);
- memcpy(frameCopy->mData, frame->mData, frame->mSize);
- frameCopy->mData = 0;
+ frameCopy->copyFlattened(*frame);
delete frame; // Fix memory leakage
return mThumbnail;
}
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index fe7547c..c78cd4b 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -49,8 +49,9 @@
const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
- virtual status_t setDataSource(const sp<IDataSource>& source);
- virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
+ virtual status_t setDataSource(const sp<IDataSource>& source, const char *mime);
+ virtual sp<IMemory> getFrameAtTime(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly);
virtual sp<IMemory> extractAlbumArt();
virtual const char* extractMetadata(int keyCode);
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/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8fe255b..ac187cc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -343,7 +343,7 @@
format, mSurface, crypto, 0 /* flags */);
if (err != OK) {
- ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
+ ALOGE("Failed to configure [%s] decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
mCodec.clear();
handleError(err);
@@ -372,7 +372,7 @@
err = mCodec->start();
if (err != OK) {
- ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
+ ALOGE("Failed to start [%s] decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
mCodec.clear();
handleError(err);
@@ -460,6 +460,12 @@
if (notifyComplete) {
mResumePending = true;
}
+
+ if (mCodec == NULL) {
+ ALOGE("[%s] onResume without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return;
+ }
mCodec->start();
}
@@ -481,7 +487,7 @@
}
if (err != OK) {
- ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
+ ALOGE("failed to flush [%s] (err=%d)", mComponentName.c_str(), err);
handleError(err);
// finish with posting kWhatFlushCompleted.
// we attempt to release the buffers even if flush fails.
@@ -530,7 +536,7 @@
releaseAndResetMediaBuffers();
if (err != OK) {
- ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
+ ALOGE("failed to release [%s] (err=%d)", mComponentName.c_str(), err);
handleError(err);
// finish with posting kWhatShutdownCompleted.
}
@@ -631,10 +637,17 @@
return false;
}
+ if (mCodec == NULL) {
+ ALOGE("[%s] handleAnInputBuffer without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
sp<MediaCodecBuffer> buffer;
mCodec->getInputBuffer(index, &buffer);
if (buffer == NULL) {
+ ALOGE("[%s] handleAnInputBuffer, failed to get input buffer", mComponentName.c_str());
handleError(UNKNOWN_ERROR);
return false;
}
@@ -697,11 +710,18 @@
size_t size,
int64_t timeUs,
int32_t flags) {
+ if (mCodec == NULL) {
+ ALOGE("[%s] handleAnOutputBuffer without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
// CHECK_LT(bufferIx, mOutputBuffers.size());
sp<MediaCodecBuffer> buffer;
mCodec->getOutputBuffer(index, &buffer);
if (buffer == NULL) {
+ ALOGE("[%s] handleAnOutputBuffer, failed to get output buffer", mComponentName.c_str());
handleError(UNKNOWN_ERROR);
return false;
}
@@ -949,6 +969,12 @@
}
bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
+ if (mCodec == NULL) {
+ ALOGE("[%s] onInputBufferFetched without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
size_t bufferIx;
CHECK(msg->findSize("buffer-ix", &bufferIx));
CHECK_LT(bufferIx, mInputBuffers.size());
@@ -979,7 +1005,7 @@
}
if (streamErr != ERROR_END_OF_STREAM) {
- ALOGE("Stream error for %s (err=%d), EOS %s queued",
+ ALOGE("Stream error for [%s] (err=%d), EOS %s queued",
mComponentName.c_str(),
streamErr,
err == OK ? "successfully" : "unsuccessfully");
@@ -1073,7 +1099,7 @@
} // no cryptInfo
if (err != OK) {
- ALOGE("onInputBufferFetched: queue%sInputBuffer failed for %s (err=%d, %s)",
+ ALOGE("onInputBufferFetched: queue%sInputBuffer failed for [%s] (err=%d, %s)",
(cryptInfo != NULL ? "Secure" : ""),
mComponentName.c_str(), err, errorDetailMsg.c_str());
handleError(err);
@@ -1102,7 +1128,9 @@
}
}
- if (msg->findInt32("render", &render) && render) {
+ if (mCodec == NULL) {
+ err = NO_INIT;
+ } else if (msg->findInt32("render", &render) && render) {
int64_t timestampNs;
CHECK(msg->findInt64("timestampNs", ×tampNs));
err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
@@ -1111,7 +1139,7 @@
err = mCodec->releaseOutputBuffer(bufferIx);
}
if (err != OK) {
- ALOGE("failed to release output buffer for %s (err=%d)",
+ ALOGE("failed to release output buffer for [%s] (err=%d)",
mComponentName.c_str(), err);
handleError(err);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0fc1aa7..dc29761 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -69,6 +69,7 @@
mPlayer(new NuPlayer(pid)),
mPlayerFlags(0),
mAnalyticsItem(NULL),
+ mClientUid(-1),
mAtEOS(false),
mLooping(false),
mAutoLoop(false) {
@@ -109,6 +110,10 @@
status_t NuPlayerDriver::setUID(uid_t uid) {
mPlayer->setUID(uid);
+ mClientUid = uid;
+ if (mAnalyticsItem) {
+ mAnalyticsItem->setUid(mClientUid);
+ }
return OK;
}
@@ -601,6 +606,7 @@
mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
if (mAnalyticsItem) {
mAnalyticsItem->generateSessionID();
+ mAnalyticsItem->setUid(mClientUid);
}
} else {
ALOGV("did not have anything to record");
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index c5ddcb0..d0cf1dd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -132,11 +132,13 @@
uint32_t mPlayerFlags;
MediaAnalyticsItem *mAnalyticsItem;
+ uid_t mClientUid;
bool mAtEOS;
bool mLooping;
bool mAutoLoop;
+
void updateMetrics(const char *where);
void logMetrics(const char *where);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index d4ec30d..ec48561 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1866,16 +1866,15 @@
mFlags |= kFlagIsGrallocUsageProtected;
mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
}
+ }
+ if (mFlags & kFlagIsSecure) {
+ // use native_handles for secure input buffers
+ err = setPortMode(kPortIndexInput, IOMX::kPortModePresetSecureBuffer);
- if (mFlags & kFlagIsSecure) {
- // use native_handles for secure input buffers
- err = setPortMode(kPortIndexInput, IOMX::kPortModePresetSecureBuffer);
-
- if (err != OK) {
- ALOGI("falling back to non-native_handles");
- setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer);
- err = OK; // ignore error for now
- }
+ if (err != OK) {
+ ALOGI("falling back to non-native_handles");
+ setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer);
+ err = OK; // ignore error for now
}
}
if (haveNativeWindow) {
@@ -4174,11 +4173,12 @@
// static
int /* OMX_VIDEO_AVCLEVELTYPE */ ACodec::getAVCLevelFor(
int width, int height, int rate, int bitrate,
- OMX_VIDEO_AVCPROFILETYPE profile) {
+ OMX_VIDEO_AVCPROFILEEXTTYPE profile) {
// convert bitrate to main/baseline profile kbps equivalent
- switch (profile) {
+ switch ((uint32_t)profile) {
case OMX_VIDEO_AVCProfileHigh10:
bitrate = divUp(bitrate, 3000); break;
+ case OMX_VIDEO_AVCProfileConstrainedHigh:
case OMX_VIDEO_AVCProfileHigh:
bitrate = divUp(bitrate, 1250); break;
default:
@@ -4322,9 +4322,14 @@
h264type.bUseHadamard = OMX_TRUE;
h264type.nRefFrames = 2;
h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1);
+
+ // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames
+ h264type.nRefFrames = 1;
+ h264type.nBFrames = 0;
+
h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
h264type.nAllowedPictureTypes =
- OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB;
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
h264type.nRefIdx10ActiveMinus1 = 0;
h264type.nRefIdx11ActiveMinus1 = 0;
h264type.bEntropyCodingCABAC = OMX_TRUE;
@@ -8263,6 +8268,17 @@
}
builder->addProfileLevel(param.eProfile, param.eLevel);
+ // AVC components may not list the constrained profiles explicitly, but
+ // decoders that support a profile also support its constrained version.
+ // Encoders must explicitly support constrained profiles.
+ if (!isEncoder && mime.equalsIgnoreCase(MEDIA_MIMETYPE_VIDEO_AVC)) {
+ if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
+ builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+ } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+ builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+ }
+ }
+
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking profiles after %u: %x/%x",
name.c_str(), index,
@@ -8276,7 +8292,6 @@
OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
InitOMXParams(&portFormat);
portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
- Vector<uint32_t> supportedColors; // shadow copy to check for duplicates
for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
portFormat.nIndex = index;
status_t err = omxNode->getParameter(
@@ -8290,19 +8305,8 @@
if (IsFlexibleColorFormat(
omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
&flexibleEquivalent)) {
- bool marked = false;
- for (size_t i = 0; i < supportedColors.size(); ++i) {
- if (supportedColors[i] == flexibleEquivalent) {
- marked = true;
- break;
- }
- }
- if (!marked) {
- supportedColors.push(flexibleEquivalent);
- builder->addColorFormat(flexibleEquivalent);
- }
+ builder->addColorFormat(flexibleEquivalent);
}
- supportedColors.push(portFormat.eColorFormat);
builder->addColorFormat(portFormat.eColorFormat);
if (index == kMaxIndicesToCheck) {
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 19973bd..348abf8 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -30,6 +30,7 @@
"FrameRenderTracker.cpp",
"HTTPBase.cpp",
"HevcUtils.cpp",
+ "ItemTable.cpp",
"JPEGSource.cpp",
"MP3Extractor.cpp",
"MPEG2TSWriter.cpp",
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index 4309372..6dfe2de 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -127,10 +127,6 @@
}
ssize_t TinyCacheSource::readAt(off64_t offset, void* data, size_t size) {
- if (size >= kCacheSize) {
- return mSource->readAt(offset, data, size);
- }
-
// Check if the cache satisfies the read.
if (mCachedOffset <= offset
&& offset < (off64_t) (mCachedOffset + mCachedSize)) {
@@ -154,6 +150,9 @@
}
}
+ if (size >= kCacheSize) {
+ return mSource->readAt(offset, data, size);
+ }
// Fill the cache and copy to the caller.
const ssize_t numRead = mSource->readAt(offset, mCache, kCacheSize);
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a5760d1..c22053e 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -92,6 +92,48 @@
return true;
}
+bool DataSource::getUInt16Var(off64_t offset, uint16_t *x, size_t size) {
+ if (size == 2) {
+ return getUInt16(offset, x);
+ }
+ if (size == 1) {
+ uint8_t tmp;
+ if (readAt(offset, &tmp, 1) == 1) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DataSource::getUInt32Var(off64_t offset, uint32_t *x, size_t size) {
+ if (size == 4) {
+ return getUInt32(offset, x);
+ }
+ if (size == 2) {
+ uint16_t tmp;
+ if (getUInt16(offset, &tmp)) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DataSource::getUInt64Var(off64_t offset, uint64_t *x, size_t size) {
+ if (size == 8) {
+ return getUInt64(offset, x);
+ }
+ if (size == 4) {
+ uint32_t tmp;
+ if (getUInt32(offset, &tmp)) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
status_t DataSource::getSize(off64_t *size) {
*size = 0;
diff --git a/media/libstagefright/ItemTable.cpp b/media/libstagefright/ItemTable.cpp
new file mode 100644
index 0000000..3ec416b
--- /dev/null
+++ b/media/libstagefright/ItemTable.cpp
@@ -0,0 +1,1553 @@
+/*
+ * 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 "ItemTable"
+
+#include <include/ItemTable.h>
+#include <media/MediaDefs.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <utils/Log.h>
+
+namespace android {
+
+namespace heif {
+
+/////////////////////////////////////////////////////////////////////
+//
+// struct to keep track of one image item
+//
+
+struct ImageItem {
+ friend struct ItemReference;
+ friend struct ItemProperty;
+
+ ImageItem() : ImageItem(0) {}
+ ImageItem(uint32_t _type) : type(_type),
+ rows(0), columns(0), width(0), height(0), rotation(0),
+ offset(0), size(0), nextTileIndex(0) {}
+
+ bool isGrid() const {
+ return type == FOURCC('g', 'r', 'i', 'd');
+ }
+
+ status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
+ if (reset) {
+ nextTileIndex = 0;
+ }
+ if (nextTileIndex >= dimgRefs.size()) {
+ return ERROR_END_OF_STREAM;
+ }
+ *nextTileItemId = dimgRefs[nextTileIndex++];
+ return OK;
+ }
+
+ uint32_t type;
+ int32_t rows;
+ int32_t columns;
+ int32_t width;
+ int32_t height;
+ int32_t rotation;
+ off64_t offset;
+ size_t size;
+ sp<ABuffer> hvcc;
+ sp<ABuffer> icc;
+
+ Vector<uint32_t> thumbnails;
+ Vector<uint32_t> dimgRefs;
+ size_t nextTileIndex;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// ISO boxes
+//
+
+struct Box {
+protected:
+ Box(const sp<DataSource> source, uint32_t type) :
+ mDataSource(source), mType(type) {}
+
+ virtual ~Box() {}
+
+ virtual status_t onChunkData(
+ uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
+ return OK;
+ }
+
+ inline uint32_t type() const { return mType; }
+
+ inline sp<DataSource> source() const { return mDataSource; }
+
+ status_t parseChunk(off64_t *offset);
+
+ status_t parseChunks(off64_t offset, size_t size);
+
+private:
+ sp<DataSource> mDataSource;
+ uint32_t mType;
+};
+
+status_t Box::parseChunk(off64_t *offset) {
+ if (*offset < 0) {
+ ALOGE("b/23540914");
+ return ERROR_MALFORMED;
+ }
+ uint32_t hdr[2];
+ if (mDataSource->readAt(*offset, hdr, 8) < 8) {
+ return ERROR_IO;
+ }
+ uint64_t chunk_size = ntohl(hdr[0]);
+ int32_t chunk_type = ntohl(hdr[1]);
+ off64_t data_offset = *offset + 8;
+
+ if (chunk_size == 1) {
+ if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ data_offset += 8;
+
+ if (chunk_size < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
+ return ERROR_MALFORMED;
+ }
+ } else if (chunk_size == 0) {
+ // This shouldn't happen since we should never be top level
+ ALOGE("invalid chunk size 0 for non-top level box");
+ return ERROR_MALFORMED;
+ } else if (chunk_size < 8) {
+ // The smallest valid chunk is 8 bytes long.
+ ALOGE("invalid chunk size: %lld", (long long)chunk_size);
+ return ERROR_MALFORMED;
+ }
+
+ char chunk[5];
+ MakeFourCCString(chunk_type, chunk);
+ ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
+
+ off64_t chunk_data_size = chunk_size - (data_offset - *offset);
+ if (chunk_data_size < 0) {
+ ALOGE("b/23540914");
+ return ERROR_MALFORMED;
+ }
+
+ status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+ *offset += chunk_size;
+ return OK;
+}
+
+status_t Box::parseChunks(off64_t offset, size_t size) {
+ off64_t stopOffset = offset + size;
+ while (offset < stopOffset) {
+ status_t err = parseChunk(&offset);
+ if (err != OK) {
+ return err;
+ }
+ }
+ if (offset != stopOffset) {
+ return ERROR_MALFORMED;
+ }
+ return OK;
+}
+
+///////////////////////////////////////////////////////////////////////
+
+struct FullBox : public Box {
+protected:
+ FullBox(const sp<DataSource> source, uint32_t type) :
+ Box(source, type), mVersion(0), mFlags(0) {}
+
+ inline uint8_t version() const { return mVersion; }
+
+ inline uint32_t flags() const { return mFlags; }
+
+ status_t parseFullBoxHeader(off64_t *offset, size_t *size);
+
+private:
+ uint8_t mVersion;
+ uint32_t mFlags;
+};
+
+status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
+ if (*size < 4) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->readAt(*offset, &mVersion, 1)) {
+ return ERROR_IO;
+ }
+ if (!source()->getUInt24(*offset + 1, &mFlags)) {
+ return ERROR_IO;
+ }
+ *offset += 4;
+ *size -= 4;
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// PrimaryImage box
+//
+
+struct PitmBox : public FullBox {
+ PitmBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('p', 'i', 't', 'm')) {}
+
+ status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
+};
+
+status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t itemIdSize = (version() == 0) ? 2 : 4;
+ if (size < itemIdSize) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t itemId;
+ if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
+ return ERROR_IO;
+ }
+
+ ALOGV("primary id %d", itemId);
+ *primaryItemId = itemId;
+
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemLocation related boxes
+//
+
+struct ExtentEntry {
+ uint64_t extentIndex;
+ uint64_t extentOffset;
+ uint64_t extentLength;
+};
+
+struct ItemLoc {
+ ItemLoc() : ItemLoc(0, 0, 0, 0) {}
+ ItemLoc(uint32_t item_id, uint16_t construction_method,
+ uint16_t data_reference_index, uint64_t base_offset) :
+ itemId(item_id),
+ constructionMethod(construction_method),
+ dataReferenceIndex(data_reference_index),
+ baseOffset(base_offset) {}
+
+ void addExtent(const ExtentEntry& extent) {
+ extents.push_back(extent);
+ }
+
+ status_t getLoc(off64_t *offset, size_t *size,
+ off64_t idatOffset, size_t idatSize) const {
+ // TODO: fix extent handling, fix constructionMethod = 2
+ CHECK(extents.size() == 1);
+ if (constructionMethod == 0) {
+ *offset = baseOffset + extents[0].extentOffset;
+ *size = extents[0].extentLength;
+ return OK;
+ } else if (constructionMethod == 1) {
+ if (baseOffset + extents[0].extentOffset + extents[0].extentLength
+ > idatSize) {
+ return ERROR_MALFORMED;
+ }
+ *offset = baseOffset + extents[0].extentOffset + idatOffset;
+ *size = extents[0].extentLength;
+ return OK;
+ }
+ return ERROR_UNSUPPORTED;
+ }
+
+ // parsed info
+ uint32_t itemId;
+ uint16_t constructionMethod;
+ uint16_t dataReferenceIndex;
+ off64_t baseOffset;
+ Vector<ExtentEntry> extents;
+};
+
+struct IlocBox : public FullBox {
+ IlocBox(const sp<DataSource> source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
+ FullBox(source, FOURCC('i', 'l', 'o', 'c')),
+ mItemLocs(itemLocs), mHasConstructMethod1(false) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ bool hasConstructMethod1() { return mHasConstructMethod1; }
+
+private:
+ static bool isSizeFieldValid(uint32_t offset_size) {
+ return offset_size == 0 || offset_size == 4 || offset_size == 8;
+ }
+ KeyedVector<uint32_t, ItemLoc> *mItemLocs;
+ bool mHasConstructMethod1;
+};
+
+status_t IlocBox::parse(off64_t offset, size_t size) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+ if (version() > 2) {
+ ALOGE("%s: invalid version %d", __FUNCTION__, version());
+ return ERROR_MALFORMED;
+ }
+
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+ uint8_t offset_size;
+ if (!source()->readAt(offset++, &offset_size, 1)) {
+ return ERROR_IO;
+ }
+ uint8_t length_size = (offset_size & 0xF);
+ offset_size >>= 4;
+
+ uint8_t base_offset_size;
+ if (!source()->readAt(offset++, &base_offset_size, 1)) {
+ return ERROR_IO;
+ }
+ uint8_t index_size = 0;
+ if (version() == 1 || version() == 2) {
+ index_size = (base_offset_size & 0xF);
+ }
+ base_offset_size >>= 4;
+ size -= 2;
+
+ if (!isSizeFieldValid(offset_size)
+ || !isSizeFieldValid(length_size)
+ || !isSizeFieldValid(base_offset_size)
+ || !isSizeFieldValid((index_size))) {
+ ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
+ offset_size, length_size, base_offset_size, index_size);
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t item_count;
+ size_t itemFieldSize = version() < 2 ? 2 : 4;
+ if (size < itemFieldSize) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
+ return ERROR_IO;
+ }
+
+ ALOGV("item_count %lld", (long long) item_count);
+ offset += itemFieldSize;
+ size -= itemFieldSize;
+
+ for (size_t i = 0; i < item_count; i++) {
+ uint32_t item_id;
+ if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("item[%zu]: id %lld", i, (long long)item_id);
+ offset += itemFieldSize;
+
+ uint8_t construction_method = 0;
+ if (version() == 1 || version() == 2) {
+ uint8_t buf[2];
+ if (!source()->readAt(offset, buf, 2)) {
+ return ERROR_IO;
+ }
+ construction_method = (buf[1] & 0xF);
+ ALOGV("construction_method %d", construction_method);
+ if (construction_method == 1) {
+ mHasConstructMethod1 = true;
+ }
+
+ offset += 2;
+ }
+
+ uint16_t data_reference_index;
+ if (!source()->getUInt16(offset, &data_reference_index)) {
+ return ERROR_IO;
+ }
+ ALOGV("data_reference_index %d", data_reference_index);
+ if (data_reference_index != 0) {
+ // we don't support reference to other files
+ return ERROR_UNSUPPORTED;
+ }
+ offset += 2;
+
+ uint64_t base_offset = 0;
+ if (base_offset_size != 0) {
+ if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
+ return ERROR_IO;
+ }
+ offset += base_offset_size;
+ }
+ ALOGV("base_offset %lld", (long long) base_offset);
+
+ ssize_t index = mItemLocs->add(item_id, ItemLoc(
+ item_id, construction_method, data_reference_index, base_offset));
+ ItemLoc &item = mItemLocs->editValueAt(index);
+
+ uint16_t extent_count;
+ if (!source()->getUInt16(offset, &extent_count)) {
+ return ERROR_IO;
+ }
+ ALOGV("extent_count %d", extent_count);
+
+ if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
+ // if the item is dividec into more than one extents, offset and
+ // length must be present.
+ return ERROR_MALFORMED;
+ }
+ offset += 2;
+
+ for (size_t j = 0; j < extent_count; j++) {
+ uint64_t extent_index = 1; // default=1
+ if ((version() == 1 || version() == 2) && (index_size > 0)) {
+ if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
+ return ERROR_IO;
+ }
+ // TODO: add support for this mode
+ offset += index_size;
+ ALOGV("extent_index %lld", (long long)extent_index);
+ }
+
+ uint64_t extent_offset = 0; // default=0
+ if (offset_size > 0) {
+ if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
+ return ERROR_IO;
+ }
+ offset += offset_size;
+ }
+ ALOGV("extent_offset %lld", (long long)extent_offset);
+
+ uint64_t extent_length = 0; // this indicates full length of file
+ if (length_size > 0) {
+ if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
+ return ERROR_IO;
+ }
+ offset += length_size;
+ }
+ ALOGV("extent_length %lld", (long long)extent_length);
+
+ item.addExtent({ extent_index, extent_offset, extent_length });
+ }
+ }
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemReference related boxes
+//
+
+struct ItemReference : public Box, public RefBase {
+ ItemReference(const sp<DataSource> source, uint32_t type, uint32_t itemIdSize) :
+ Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ uint32_t itemId() { return mItemId; }
+
+ void apply(KeyedVector<uint32_t, ImageItem> &itemIdToImageMap) const {
+ ssize_t imageIndex = itemIdToImageMap.indexOfKey(mItemId);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ return;
+ }
+
+ ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
+
+ if (type() == FOURCC('d', 'i', 'm', 'g')) {
+ ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
+ if (!image.dimgRefs.empty()) {
+ ALOGW("dimgRefs if not clean!");
+ }
+ image.dimgRefs.appendVector(mRefs);
+ } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
+ for (size_t i = 0; i < mRefs.size(); i++) {
+ imageIndex = itemIdToImageMap.indexOfKey(mRefs[i]);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ continue;
+ }
+ ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
+ ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
+ if (!image.thumbnails.empty()) {
+ ALOGW("already has thumbnails!");
+ }
+ image.thumbnails.push_back(mItemId);
+ }
+ } else {
+ ALOGW("ignoring unsupported ref type 0x%x", type());
+ }
+ }
+
+private:
+ uint32_t mItemId;
+ uint32_t mRefIdSize;
+ Vector<uint32_t> mRefs;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
+};
+
+status_t ItemReference::parse(off64_t offset, size_t size) {
+ if (size < mRefIdSize + 2) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
+ return ERROR_IO;
+ }
+ offset += mRefIdSize;
+
+ uint16_t count;
+ if (!source()->getUInt16(offset, &count)) {
+ return ERROR_IO;
+ }
+ offset += 2;
+ size -= (mRefIdSize + 2);
+
+ if (size < count * mRefIdSize) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ uint32_t refItemId;
+ if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
+ return ERROR_IO;
+ }
+ offset += mRefIdSize;
+ mRefs.push_back(refItemId);
+ ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
+ }
+
+ return OK;
+}
+
+struct IrefBox : public FullBox {
+ IrefBox(const sp<DataSource> source, Vector<sp<ItemReference> > *itemRefs) :
+ FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ uint32_t mRefIdSize;
+ Vector<sp<ItemReference> > *mItemRefs;
+};
+
+status_t IrefBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ mRefIdSize = (version() == 0) ? 2 : 4;
+ return parseChunks(offset, size);
+}
+
+status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
+
+ status_t err = itemRef->parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ mItemRefs->push_back(itemRef);
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemProperty related boxes
+//
+
+struct AssociationEntry {
+ uint32_t itemId;
+ bool essential;
+ uint16_t index;
+};
+
+struct ItemProperty : public RefBase {
+ ItemProperty() {}
+
+ virtual void attachTo(ImageItem &/*image*/) const {
+ ALOGW("Unrecognized property");
+ }
+ virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
+ ALOGW("Unrecognized property");
+ return OK;
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
+};
+
+struct IspeBox : public FullBox, public ItemProperty {
+ IspeBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.width = mWidth;
+ image.height = mHeight;
+ }
+
+private:
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+status_t IspeBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (size < 8) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32(offset, &mWidth)
+ || !source()->getUInt32(offset + 4, &mHeight)) {
+ return ERROR_IO;
+ }
+ ALOGV("property ispe: %dx%d", mWidth, mHeight);
+
+ return OK;
+}
+
+struct HvccBox : public Box, public ItemProperty {
+ HvccBox(const sp<DataSource> source) :
+ Box(source, FOURCC('h', 'v', 'c', 'C')) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.hvcc = mHVCC;
+ }
+
+private:
+ sp<ABuffer> mHVCC;
+};
+
+status_t HvccBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ mHVCC = new ABuffer(size);
+
+ if (mHVCC->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ ALOGV("property hvcC");
+
+ return OK;
+}
+
+struct IrotBox : public Box, public ItemProperty {
+ IrotBox(const sp<DataSource> source) :
+ Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.rotation = mAngle * 90;
+ }
+
+private:
+ uint8_t mAngle;
+};
+
+status_t IrotBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ if (size < 1) {
+ return ERROR_MALFORMED;
+ }
+ if (source()->readAt(offset, &mAngle, 1) != 1) {
+ return ERROR_IO;
+ }
+ mAngle &= 0x3;
+ ALOGV("property irot: %d", mAngle);
+
+ return OK;
+}
+
+struct ColrBox : public Box, public ItemProperty {
+ ColrBox(const sp<DataSource> source) :
+ Box(source, FOURCC('c', 'o', 'l', 'r')) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.icc = mICCData;
+ }
+
+private:
+ sp<ABuffer> mICCData;
+};
+
+status_t ColrBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t colour_type;
+ if (!source()->getUInt32(offset, &colour_type)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+ if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
+ return OK;
+ }
+ if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
+ (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
+ return ERROR_MALFORMED;
+ }
+
+ mICCData = new ABuffer(size);
+ if (mICCData->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ ALOGV("property Colr: size %zd", size);
+ return OK;
+}
+
+struct IpmaBox : public FullBox {
+ IpmaBox(const sp<DataSource> source, Vector<AssociationEntry> *associations) :
+ FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
+
+ status_t parse(off64_t offset, size_t size);
+private:
+ Vector<AssociationEntry> *mAssociations;
+};
+
+status_t IpmaBox::parse(off64_t offset, size_t size) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t entryCount;
+ if (!source()->getUInt32(offset, &entryCount)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+
+ for (size_t k = 0; k < entryCount; ++k) {
+ uint32_t itemId = 0;
+ size_t itemIdSize = (version() < 1) ? 2 : 4;
+
+ if (size < itemIdSize + 1) {
+ return ERROR_MALFORMED;
+ }
+
+ if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
+ return ERROR_IO;
+ }
+ offset += itemIdSize;
+ size -= itemIdSize;
+
+ uint8_t associationCount;
+ if (!source()->readAt(offset, &associationCount, 1)) {
+ return ERROR_IO;
+ }
+ offset++;
+ size--;
+
+ for (size_t i = 0; i < associationCount; ++i) {
+ size_t propIndexSize = (flags() & 1) ? 2 : 1;
+ if (size < propIndexSize) {
+ return ERROR_MALFORMED;
+ }
+ uint16_t propIndex;
+ if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
+ return ERROR_IO;
+ }
+ offset += propIndexSize;
+ size -= propIndexSize;
+ uint16_t bitmask = (1 << (8 * propIndexSize - 1));
+ AssociationEntry entry = {
+ .itemId = itemId,
+ .essential = !!(propIndex & bitmask),
+ .index = (uint16_t) (propIndex & ~bitmask)
+ };
+
+ ALOGV("item id %d associated to property %d (essential %d)",
+ itemId, entry.index, entry.essential);
+
+ mAssociations->push_back(entry);
+ }
+ }
+
+ return OK;
+}
+
+struct IpcoBox : public Box {
+ IpcoBox(const sp<DataSource> source, Vector<sp<ItemProperty> > *properties) :
+ Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
+
+ status_t parse(off64_t offset, size_t size);
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<sp<ItemProperty> > *mItemProperties;
+};
+
+status_t IpcoBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+ // push dummy as the index is 1-based
+ mItemProperties->push_back(new ItemProperty());
+ return parseChunks(offset, size);
+}
+
+status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ sp<ItemProperty> itemProperty;
+ switch(type) {
+ case FOURCC('h', 'v', 'c', 'C'):
+ {
+ itemProperty = new HvccBox(source());
+ break;
+ }
+ case FOURCC('i', 's', 'p', 'e'):
+ {
+ itemProperty = new IspeBox(source());
+ break;
+ }
+ case FOURCC('i', 'r', 'o', 't'):
+ {
+ itemProperty = new IrotBox(source());
+ break;
+ }
+ case FOURCC('c', 'o', 'l', 'r'):
+ {
+ itemProperty = new ColrBox(source());
+ break;
+ }
+ default:
+ {
+ // push dummy to maintain correct item property index
+ itemProperty = new ItemProperty();
+ break;
+ }
+ }
+ status_t err = itemProperty->parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ mItemProperties->push_back(itemProperty);
+ return OK;
+}
+
+struct IprpBox : public Box {
+ IprpBox(const sp<DataSource> source,
+ Vector<sp<ItemProperty> > *properties,
+ Vector<AssociationEntry> *associations) :
+ Box(source, FOURCC('i', 'p', 'r', 'p')),
+ mProperties(properties), mAssociations(associations) {}
+
+ status_t parse(off64_t offset, size_t size);
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<sp<ItemProperty> > *mProperties;
+ Vector<AssociationEntry> *mAssociations;
+};
+
+status_t IprpBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseChunks(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ return OK;
+}
+
+status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ switch(type) {
+ case FOURCC('i', 'p', 'c', 'o'):
+ {
+ IpcoBox ipcoBox(source(), mProperties);
+ return ipcoBox.parse(offset, size);
+ }
+ case FOURCC('i', 'p', 'm', 'a'):
+ {
+ IpmaBox ipmaBox(source(), mAssociations);
+ return ipmaBox.parse(offset, size);
+ }
+ default:
+ {
+ ALOGW("Unrecognized box.");
+ break;
+ }
+ }
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemInfo related boxes
+//
+struct ItemInfo {
+ uint32_t itemId;
+ uint32_t itemType;
+};
+
+struct InfeBox : public FullBox {
+ InfeBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
+
+ status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
+
+private:
+ bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
+};
+
+bool InfeBox::parseNullTerminatedString(
+ off64_t *offset, size_t *size, String8 *out) {
+ char tmp[256];
+ size_t len = 0;
+ off64_t newOffset = *offset;
+ off64_t stopOffset = *offset + *size;
+ while (newOffset < stopOffset) {
+ if (!source()->readAt(newOffset++, &tmp[len], 1)) {
+ return false;
+ }
+ if (tmp[len] == 0) {
+ out->append(tmp, len);
+
+ *offset = newOffset;
+ *size = stopOffset - newOffset;
+
+ return true;
+ }
+ if (++len >= sizeof(tmp)) {
+ out->append(tmp, len);
+ len = 0;
+ }
+ }
+ return false;
+}
+
+status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (version() == 0 || version() == 1) {
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint16_t item_id;
+ if (!source()->getUInt16(offset, &item_id)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_id %d", item_id);
+ uint16_t item_protection_index;
+ if (!source()->getUInt16(offset + 2, &item_protection_index)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+
+ String8 item_name;
+ if (!parseNullTerminatedString(&offset, &size, &item_name)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_type;
+ if (!parseNullTerminatedString(&offset, &size, &content_type)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_encoding;
+ if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (version() == 1) {
+ uint32_t extension_type;
+ if (!source()->getUInt32(offset, &extension_type)) {
+ return ERROR_IO;
+ }
+ offset++;
+ size--;
+ // TODO: handle this case
+ }
+ } else { // version >= 2
+ uint32_t item_id;
+ size_t itemIdSize = (version() == 2) ? 2 : 4;
+ if (size < itemIdSize + 6) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_id %d", item_id);
+ offset += itemIdSize;
+ uint16_t item_protection_index;
+ if (!source()->getUInt16(offset, &item_protection_index)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_protection_index %d", item_protection_index);
+ offset += 2;
+ uint32_t item_type;
+ if (!source()->getUInt32(offset, &item_type)) {
+ return ERROR_IO;
+ }
+
+ itemInfo->itemId = item_id;
+ itemInfo->itemType = item_type;
+
+ char itemTypeString[5];
+ MakeFourCCString(item_type, itemTypeString);
+ ALOGV("item_type %s", itemTypeString);
+ offset += 4;
+ size -= itemIdSize + 6;
+
+ String8 item_name;
+ if (!parseNullTerminatedString(&offset, &size, &item_name)) {
+ return ERROR_MALFORMED;
+ }
+ ALOGV("item_name %s", item_name.c_str());
+
+ if (item_type == FOURCC('m', 'i', 'm', 'e')) {
+ String8 content_type;
+ if (!parseNullTerminatedString(&offset, &size, &content_type)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_encoding;
+ if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
+ return ERROR_MALFORMED;
+ }
+ } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
+ String8 item_uri_type;
+ if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
+ return ERROR_MALFORMED;
+ }
+ }
+ }
+ return OK;
+}
+
+struct IinfBox : public FullBox {
+ IinfBox(const sp<DataSource> source, Vector<ItemInfo> *itemInfos) :
+ FullBox(source, FOURCC('i', 'i', 'n', 'f')),
+ mItemInfos(itemInfos), mHasGrids(false) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ bool hasGrids() { return mHasGrids; }
+
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<ItemInfo> *mItemInfos;
+ bool mHasGrids;
+};
+
+status_t IinfBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t entryCountSize = version() == 0 ? 2 : 4;
+ if (size < entryCountSize) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t entry_count;
+ if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("entry_count %d", entry_count);
+
+ off64_t stopOffset = offset + size;
+ offset += entryCountSize;
+ for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
+ ALOGV("entry %zu", i);
+ status_t err = parseChunk(&offset);
+ if (err != OK) {
+ return err;
+ }
+ }
+ if (offset != stopOffset) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ if (type != FOURCC('i', 'n', 'f', 'e')) {
+ return OK;
+ }
+
+ InfeBox infeBox(source());
+ ItemInfo itemInfo;
+ status_t err = infeBox.parse(offset, size, &itemInfo);
+ if (err != OK) {
+ return err;
+ }
+ mItemInfos->push_back(itemInfo);
+ mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
+ return OK;
+}
+
+//////////////////////////////////////////////////////////////////
+
+ItemTable::ItemTable(const sp<DataSource> &source)
+ : mDataSource(source),
+ mPrimaryItemId(0),
+ mIdatOffset(0),
+ mIdatSize(0),
+ mImageItemsValid(false),
+ mCurrentImageIndex(0) {
+ mRequiredBoxes.insert('iprp');
+ mRequiredBoxes.insert('iloc');
+ mRequiredBoxes.insert('pitm');
+ mRequiredBoxes.insert('iinf');
+}
+
+ItemTable::~ItemTable() {}
+
+status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
+ switch(type) {
+ case FOURCC('i', 'l', 'o', 'c'):
+ {
+ return parseIlocBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'i', 'n', 'f'):
+ {
+ return parseIinfBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'p', 'r', 'p'):
+ {
+ return parseIprpBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('p', 'i', 't', 'm'):
+ {
+ return parsePitmBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'd', 'a', 't'):
+ {
+ return parseIdatBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'r', 'e', 'f'):
+ {
+ return parseIrefBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'p', 'r', 'o'):
+ {
+ ALOGW("ipro box not supported!");
+ break;
+ }
+ default:
+ {
+ ALOGW("unrecognized box type: 0x%x", type);
+ break;
+ }
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IlocBox ilocBox(mDataSource, &mItemLocs);
+ status_t err = ilocBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (ilocBox.hasConstructMethod1()) {
+ mRequiredBoxes.insert('idat');
+ }
+
+ return buildImageItemsIfPossible('iloc');
+}
+
+status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IinfBox iinfBox(mDataSource, &mItemInfos);
+ status_t err = iinfBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (iinfBox.hasGrids()) {
+ mRequiredBoxes.insert('iref');
+ }
+
+ return buildImageItemsIfPossible('iinf');
+}
+
+status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ PitmBox pitmBox(mDataSource);
+ status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('pitm');
+}
+
+status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
+ status_t err = iprpBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('iprp');
+}
+
+status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
+ ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ // only remember the offset and size of idat box for later use
+ mIdatOffset = offset;
+ mIdatSize = size;
+
+ return buildImageItemsIfPossible('idat');
+}
+
+status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IrefBox irefBox(mDataSource, &mItemReferences);
+ status_t err = irefBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('iref');
+}
+
+status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
+ if (mImageItemsValid) {
+ return OK;
+ }
+
+ mBoxesSeen.insert(type);
+
+ // need at least 'iprp', 'iloc', 'pitm', 'iinf';
+ // need 'idat' if any items used construction_method of 2;
+ // need 'iref' if there are grids.
+ if (!std::includes(
+ mBoxesSeen.begin(), mBoxesSeen.end(),
+ mRequiredBoxes.begin(), mRequiredBoxes.end())) {
+ return OK;
+ }
+
+ ALOGV("building image table...");
+
+ for (size_t i = 0; i < mItemInfos.size(); i++) {
+ const ItemInfo &info = mItemInfos[i];
+
+
+ // ignore non-image items
+ if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
+ info.itemType != FOURCC('h', 'v', 'c', '1')) {
+ continue;
+ }
+
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(info.itemId);
+ if (imageIndex >= 0) {
+ ALOGW("ignoring duplicate image item id %d", info.itemId);
+ continue;
+ }
+
+ ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
+ if (ilocIndex < 0) {
+ ALOGE("iloc missing for image item id %d", info.itemId);
+ continue;
+ }
+ const ItemLoc &iloc = mItemLocs[ilocIndex];
+
+ off64_t offset;
+ size_t size;
+ if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+ ImageItem image(info.itemType);
+
+ ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
+
+ if (image.isGrid()) {
+ if (size > 12) {
+ return ERROR_MALFORMED;
+ }
+ uint8_t buf[12];
+ if (!mDataSource->readAt(offset, buf, size)) {
+ return ERROR_IO;
+ }
+
+ image.rows = buf[2] + 1;
+ image.columns = buf[3] + 1;
+
+ ALOGV("rows %d, columans %d", image.rows, image.columns);
+ } else {
+ image.offset = offset;
+ image.size = size;
+ }
+ mItemIdToImageMap.add(info.itemId, image);
+ }
+
+ for (size_t i = 0; i < mAssociations.size(); i++) {
+ attachProperty(mAssociations[i]);
+ }
+
+ for (size_t i = 0; i < mItemReferences.size(); i++) {
+ mItemReferences[i]->apply(mItemIdToImageMap);
+ }
+
+ mImageItemsValid = true;
+ return OK;
+}
+
+void ItemTable::attachProperty(const AssociationEntry &association) {
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(association.itemId);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ return;
+ }
+
+ uint16_t propertyIndex = association.index;
+ if (propertyIndex >= mItemProperties.size()) {
+ ALOGW("Ignoring invalid property index %d", propertyIndex);
+ return;
+ }
+
+ ALOGV("attach property %d to item id %d)",
+ propertyIndex, association.itemId);
+
+ mItemProperties[propertyIndex]->attachTo(
+ mItemIdToImageMap.editValueAt(imageIndex));
+}
+
+sp<MetaData> ItemTable::getImageMeta() {
+ if (!mImageItemsValid) {
+ return NULL;
+ }
+
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (imageIndex < 0) {
+ ALOGE("Primary item id %d not found!", mPrimaryItemId);
+ return NULL;
+ }
+
+ ALOGV("primary image index %zu", imageIndex);
+
+ const ImageItem *image = &mItemIdToImageMap[imageIndex];
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+
+ ALOGV("setting image size %dx%d", image->width, image->height);
+ meta->setInt32(kKeyWidth, image->width);
+ meta->setInt32(kKeyHeight, image->height);
+ if (image->rotation != 0) {
+ meta->setInt32(kKeyRotation, image->rotation);
+ }
+ meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+
+ if (!image->thumbnails.empty()) {
+ ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(image->thumbnails[0]);
+ if (thumbnailIndex >= 0) {
+ const ImageItem &thumbnail = mItemIdToImageMap[thumbnailIndex];
+
+ meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
+ meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
+ meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
+ thumbnail.hvcc->data(), thumbnail.hvcc->size());
+ ALOGV("thumbnail meta: %dx%d, index %zd",
+ thumbnail.width, thumbnail.height, thumbnailIndex);
+ } else {
+ ALOGW("Referenced thumbnail does not exist!");
+ }
+ }
+
+ if (image->isGrid()) {
+ ssize_t tileIndex = mItemIdToImageMap.indexOfKey(image->dimgRefs[0]);
+ if (tileIndex < 0) {
+ return NULL;
+ }
+ // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area,
+ // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that.
+ meta->setInt32(kKeyDisplayWidth, image->width);
+ meta->setInt32(kKeyDisplayHeight, image->height);
+ int32_t gridRows = image->rows, gridCols = image->columns;
+
+ // point image to the first tile for grid size and HVCC
+ image = &mItemIdToImageMap.editValueAt(tileIndex);
+ meta->setInt32(kKeyWidth, image->width * gridCols);
+ meta->setInt32(kKeyHeight, image->height * gridRows);
+ meta->setInt32(kKeyGridWidth, image->width);
+ meta->setInt32(kKeyGridHeight, image->height);
+ meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+ }
+
+ if (image->hvcc == NULL) {
+ ALOGE("hvcc is missing!");
+ return NULL;
+ }
+ meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
+
+ if (image->icc != NULL) {
+ meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
+ }
+ return meta;
+}
+
+uint32_t ItemTable::countImages() const {
+ return mImageItemsValid ? mItemIdToImageMap.size() : 0;
+}
+
+status_t ItemTable::findPrimaryImage(uint32_t *imageIndex) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t index = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (index < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ *imageIndex = index;
+ return OK;
+}
+
+status_t ItemTable::findThumbnail(uint32_t *imageIndex) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t primaryIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (primaryIndex < 0) {
+ ALOGE("Primary item id %d not found!", mPrimaryItemId);
+ return ERROR_MALFORMED;
+ }
+
+ const ImageItem &primaryImage = mItemIdToImageMap[primaryIndex];
+ if (primaryImage.thumbnails.empty()) {
+ ALOGW("Using primary in place of thumbnail.");
+ *imageIndex = primaryIndex;
+ return OK;
+ }
+
+ ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(
+ primaryImage.thumbnails[0]);
+ if (thumbnailIndex < 0) {
+ ALOGE("Thumbnail item id %d not found!", primaryImage.thumbnails[0]);
+ return ERROR_MALFORMED;
+ }
+
+ *imageIndex = thumbnailIndex;
+ return OK;
+}
+
+status_t ItemTable::getImageOffsetAndSize(
+ uint32_t *imageIndex, off64_t *offset, size_t *size) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ if (imageIndex != NULL) {
+ if (*imageIndex >= mItemIdToImageMap.size()) {
+ ALOGE("Bad image index!");
+ return BAD_VALUE;
+ }
+ mCurrentImageIndex = *imageIndex;
+ }
+
+ ImageItem &image = mItemIdToImageMap.editValueAt(mCurrentImageIndex);
+ if (image.isGrid()) {
+ uint32_t tileItemId;
+ status_t err = image.getNextTileItemId(&tileItemId, imageIndex != NULL);
+ if (err != OK) {
+ return err;
+ }
+ ssize_t tileImageIndex = mItemIdToImageMap.indexOfKey(tileItemId);
+ if (tileImageIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+ *offset = mItemIdToImageMap[tileImageIndex].offset;
+ *size = mItemIdToImageMap[tileImageIndex].size;
+ } else {
+ if (imageIndex == NULL) {
+ // For single images, we only allow it to be read once, after that
+ // it's EOS. New image index must be requested each time.
+ return ERROR_END_OF_STREAM;
+ }
+ *offset = mItemIdToImageMap[mCurrentImageIndex].offset;
+ *size = mItemIdToImageMap[mCurrentImageIndex].size;
+ }
+
+ return OK;
+}
+
+} // namespace heif
+
+} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 941c759..6f59fac 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -28,6 +28,7 @@
#include "include/MPEG4Extractor.h"
#include "include/SampleTable.h"
+#include "include/ItemTable.h"
#include "include/ESDS.h"
#include <media/stagefright/foundation/ABitReader.h>
@@ -36,6 +37,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
+#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
@@ -72,7 +74,8 @@
const sp<SampleTable> &sampleTable,
Vector<SidxEntry> &sidx,
const Trex *trex,
- off64_t firstMoofOffset);
+ off64_t firstMoofOffset,
+ const sp<ItemTable> &itemTable);
virtual status_t init();
virtual status_t start(MetaData *params = NULL);
@@ -134,6 +137,9 @@
uint8_t *mSrcBuffer;
+ bool mIsHEIF;
+ sp<ItemTable> mItemTable;
+
size_t parseNALSize(const uint8_t *data) const;
status_t parseChunk(off64_t *offset);
status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
@@ -285,45 +291,6 @@
static const bool kUseHexDump = false;
-static void hexdump(const void *_data, size_t size) {
- const uint8_t *data = (const uint8_t *)_data;
- size_t offset = 0;
- while (offset < size) {
- printf("0x%04zx ", offset);
-
- size_t n = size - offset;
- if (n > 16) {
- n = 16;
- }
-
- for (size_t i = 0; i < 16; ++i) {
- if (i == 8) {
- printf(" ");
- }
-
- if (offset + i < size) {
- printf("%02x ", data[offset + i]);
- } else {
- printf(" ");
- }
- }
-
- printf(" ");
-
- for (size_t i = 0; i < n; ++i) {
- if (isprint(data[offset + i])) {
- printf("%c", data[offset + i]);
- } else {
- printf(".");
- }
- }
-
- printf("\n");
-
- offset += 16;
- }
-}
-
static const char *FourCC2MIME(uint32_t fourcc) {
switch (fourcc) {
case FOURCC('m', 'p', '4', 'a'):
@@ -378,6 +345,7 @@
mInitCheck(NO_INIT),
mHeaderTimescale(0),
mIsQT(false),
+ mIsHEIF(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData),
@@ -512,14 +480,6 @@
return track->meta;
}
-static void MakeFourCCString(uint32_t x, char *s) {
- s[0] = x >> 24;
- s[1] = (x >> 16) & 0xff;
- s[2] = (x >> 8) & 0xff;
- s[3] = x & 0xff;
- s[4] = '\0';
-}
-
status_t MPEG4Extractor::readMetaData() {
if (mInitCheck != NO_INIT) {
return mInitCheck;
@@ -529,7 +489,8 @@
status_t err;
bool sawMoovOrSidx = false;
- while (!(sawMoovOrSidx && (mMdatFound || mMoofFound))) {
+ while (!((sawMoovOrSidx && (mMdatFound || mMoofFound)) ||
+ (mIsHEIF && (mItemTable != NULL) && mItemTable->isValid()))) {
off64_t orig_offset = offset;
err = parseChunk(&offset, 0);
@@ -581,6 +542,29 @@
mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
free(buf);
}
+
+ if (mIsHEIF) {
+ sp<MetaData> meta = mItemTable->getImageMeta();
+ if (meta == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ Track *track = mLastTrack;
+ if (track != NULL) {
+ ALOGW("track is set before metadata is fully processed");
+ } else {
+ track = new Track;
+ track->next = NULL;
+ mFirstTrack = mLastTrack = track;
+ }
+
+ track->meta = meta;
+ track->meta->setInt32(kKeyTrackID, 0);
+ track->includes_expensive_metadata = false;
+ track->skipTrack = false;
+ track->timescale = 0;
+ }
+
return mInitCheck;
}
@@ -941,6 +925,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;
@@ -961,8 +951,9 @@
}
}
- if (mLastTrack == NULL)
+ if (mLastTrack == NULL) {
return ERROR_MALFORMED;
+ }
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
@@ -1014,6 +1005,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;
@@ -1033,12 +1030,6 @@
return OK;
}
-
- status_t err = verifyTrack(mLastTrack);
-
- if (err != OK) {
- return err;
- }
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mInitCheck = OK;
@@ -1134,8 +1125,9 @@
original_fourcc = ntohl(original_fourcc);
ALOGV("read original format: %d", original_fourcc);
- if (mLastTrack == NULL)
+ if (mLastTrack == NULL) {
return ERROR_MALFORMED;
+ }
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
uint32_t num_channels = 0;
@@ -1575,8 +1567,9 @@
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
- if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) {
return ERROR_MALFORMED;
+ }
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
@@ -1612,8 +1605,9 @@
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
- if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) {
return ERROR_MALFORMED;
+ }
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
@@ -2029,6 +2023,28 @@
break;
}
+ case FOURCC('i', 'l', 'o', 'c'):
+ case FOURCC('i', 'i', 'n', 'f'):
+ case FOURCC('i', 'p', 'r', 'p'):
+ case FOURCC('p', 'i', 't', 'm'):
+ case FOURCC('i', 'd', 'a', 't'):
+ case FOURCC('i', 'r', 'e', 'f'):
+ case FOURCC('i', 'p', 'r', 'o'):
+ {
+ if (mIsHEIF) {
+ if (mItemTable == NULL) {
+ mItemTable = new ItemTable(mDataSource);
+ }
+ status_t err = mItemTable->parse(
+ chunk_type, data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
+ }
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('m', 'e', 'a', 'n'):
case FOURCC('n', 'a', 'm', 'e'):
case FOURCC('d', 'a', 't', 'a'):
@@ -2376,6 +2392,7 @@
off64_t stop_offset = *offset + chunk_size;
uint32_t numCompatibleBrands = (chunk_data_size - 8) / 4;
+ std::set<uint32_t> brandSet;
for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
if (i == 1) {
// Skip this index, it refers to the minorVersion,
@@ -2389,10 +2406,15 @@
}
brand = ntohl(brand);
- if (brand == FOURCC('q', 't', ' ', ' ')) {
- mIsQT = true;
- break;
- }
+ brandSet.insert(brand);
+ }
+
+ if (brandSet.count(FOURCC('q', 't', ' ', ' ')) > 0) {
+ mIsQT = true;
+ } else if (brandSet.count(FOURCC('m', 'i', 'f', '1')) > 0
+ && brandSet.count(FOURCC('h', 'e', 'i', 'c')) > 0) {
+ mIsHEIF = true;
+ ALOGV("identified HEIF image");
}
*offset = stop_offset;
@@ -3341,7 +3363,7 @@
sp<MPEG4Source> source = new MPEG4Source(this,
track->meta, mDataSource, track->timescale, track->sampleTable,
- mSidxEntries, trex, mMoofOffset);
+ mSidxEntries, trex, mMoofOffset, mItemTable);
if (source->init() != OK) {
return NULL;
}
@@ -3730,7 +3752,8 @@
const sp<SampleTable> &sampleTable,
Vector<SidxEntry> &sidx,
const Trex *trex,
- off64_t firstMoofOffset)
+ off64_t firstMoofOffset,
+ const sp<ItemTable> &itemTable)
: mOwner(owner),
mFormat(format),
mDataSource(dataSource),
@@ -3755,7 +3778,9 @@
mGroup(NULL),
mBuffer(NULL),
mWantsNALFragments(false),
- mSrcBuffer(NULL) {
+ mSrcBuffer(NULL),
+ mIsHEIF(itemTable != NULL),
+ mItemTable(itemTable) {
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
@@ -4530,77 +4555,93 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
- uint32_t findFlags = 0;
- switch (mode) {
- case ReadOptions::SEEK_PREVIOUS_SYNC:
- findFlags = SampleTable::kFlagBefore;
- break;
- case ReadOptions::SEEK_NEXT_SYNC:
- findFlags = SampleTable::kFlagAfter;
- break;
- case ReadOptions::SEEK_CLOSEST_SYNC:
- case ReadOptions::SEEK_CLOSEST:
- findFlags = SampleTable::kFlagClosest;
- break;
- default:
- CHECK(!"Should not be here.");
- break;
- }
+ if (mIsHEIF) {
+ CHECK(mSampleTable == NULL);
+ CHECK(mItemTable != NULL);
- uint32_t sampleIndex;
- status_t err = mSampleTable->findSampleAtTime(
- seekTimeUs, 1000000, mTimescale,
- &sampleIndex, findFlags);
-
- if (mode == ReadOptions::SEEK_CLOSEST) {
- // We found the closest sample already, now we want the sync
- // sample preceding it (or the sample itself of course), even
- // if the subsequent sync sample is closer.
- findFlags = SampleTable::kFlagBefore;
- }
-
- uint32_t syncSampleIndex;
- if (err == OK) {
- err = mSampleTable->findSyncSampleNear(
- sampleIndex, &syncSampleIndex, findFlags);
- }
-
- uint32_t sampleTime;
- if (err == OK) {
- err = mSampleTable->getMetaDataForSample(
- sampleIndex, NULL, NULL, &sampleTime);
- }
-
- if (err != OK) {
- if (err == ERROR_OUT_OF_RANGE) {
- // An attempt to seek past the end of the stream would
- // normally cause this ERROR_OUT_OF_RANGE error. Propagating
- // this all the way to the MediaPlayer would cause abnormal
- // termination. Legacy behaviour appears to be to behave as if
- // we had seeked to the end of stream, ending normally.
- err = ERROR_END_OF_STREAM;
+ status_t err;
+ if (seekTimeUs >= 0) {
+ err = mItemTable->findPrimaryImage(&mCurrentSampleIndex);
+ } else {
+ err = mItemTable->findThumbnail(&mCurrentSampleIndex);
}
- ALOGV("end of stream");
- return err;
- }
+ if (err != OK) {
+ return err;
+ }
+ } else {
+ uint32_t findFlags = 0;
+ switch (mode) {
+ case ReadOptions::SEEK_PREVIOUS_SYNC:
+ findFlags = SampleTable::kFlagBefore;
+ break;
+ case ReadOptions::SEEK_NEXT_SYNC:
+ findFlags = SampleTable::kFlagAfter;
+ break;
+ case ReadOptions::SEEK_CLOSEST_SYNC:
+ case ReadOptions::SEEK_CLOSEST:
+ findFlags = SampleTable::kFlagClosest;
+ break;
+ default:
+ CHECK(!"Should not be here.");
+ break;
+ }
- if (mode == ReadOptions::SEEK_CLOSEST) {
- targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
- }
+ uint32_t sampleIndex;
+ status_t err = mSampleTable->findSampleAtTime(
+ seekTimeUs, 1000000, mTimescale,
+ &sampleIndex, findFlags);
+
+ if (mode == ReadOptions::SEEK_CLOSEST) {
+ // We found the closest sample already, now we want the sync
+ // sample preceding it (or the sample itself of course), even
+ // if the subsequent sync sample is closer.
+ findFlags = SampleTable::kFlagBefore;
+ }
+
+ uint32_t syncSampleIndex;
+ if (err == OK) {
+ err = mSampleTable->findSyncSampleNear(
+ sampleIndex, &syncSampleIndex, findFlags);
+ }
+
+ uint32_t sampleTime;
+ if (err == OK) {
+ err = mSampleTable->getMetaDataForSample(
+ sampleIndex, NULL, NULL, &sampleTime);
+ }
+
+ if (err != OK) {
+ if (err == ERROR_OUT_OF_RANGE) {
+ // An attempt to seek past the end of the stream would
+ // normally cause this ERROR_OUT_OF_RANGE error. Propagating
+ // this all the way to the MediaPlayer would cause abnormal
+ // termination. Legacy behaviour appears to be to behave as if
+ // we had seeked to the end of stream, ending normally.
+ err = ERROR_END_OF_STREAM;
+ }
+ ALOGV("end of stream");
+ return err;
+ }
+
+ if (mode == ReadOptions::SEEK_CLOSEST) {
+ targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
+ }
#if 0
- uint32_t syncSampleTime;
- CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
- syncSampleIndex, NULL, NULL, &syncSampleTime));
+ uint32_t syncSampleTime;
+ CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
+ syncSampleIndex, NULL, NULL, &syncSampleTime));
- ALOGI("seek to time %lld us => sample at time %lld us, "
- "sync sample at time %lld us",
- seekTimeUs,
- sampleTime * 1000000ll / mTimescale,
- syncSampleTime * 1000000ll / mTimescale);
+ ALOGI("seek to time %lld us => sample at time %lld us, "
+ "sync sample at time %lld us",
+ seekTimeUs,
+ sampleTime * 1000000ll / mTimescale,
+ syncSampleTime * 1000000ll / mTimescale);
#endif
- mCurrentSampleIndex = syncSampleIndex;
+ mCurrentSampleIndex = syncSampleIndex;
+ }
+
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
@@ -4617,9 +4658,19 @@
if (mBuffer == NULL) {
newBuffer = true;
- status_t err =
- mSampleTable->getMetaDataForSample(
+ status_t err;
+ if (!mIsHEIF) {
+ err = mSampleTable->getMetaDataForSample(
mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
+ } else {
+ err = mItemTable->getImageOffsetAndSize(
+ options && options->getSeekTo(&seekTimeUs, &mode) ?
+ &mCurrentSampleIndex : NULL, &offset, &size);
+
+ cts = stts = 0;
+ isSyncSample = 0;
+ ALOGV("image offset %lld, size %zu", (long long)offset, size);
+ }
if (err != OK) {
return err;
@@ -5173,6 +5224,7 @@
void MPEG4Extractor::populateMetrics() {
ALOGV("MPEG4Extractor::populateMetrics");
+ // write into mAnalyticsItem
}
static bool LegacySniffMPEG4(
@@ -5189,7 +5241,8 @@
|| !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
|| !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
|| !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
- || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
+ || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
+ || !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)) {
*mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.4;
@@ -5218,6 +5271,8 @@
FOURCC('3', 'g', '2', 'a'), // 3GPP2
FOURCC('3', 'g', '2', 'b'),
+ FOURCC('m', 'i', 'f', '1'), // HEIF image
+ FOURCC('h', 'e', 'i', 'c'), // HEIF image
};
for (size_t i = 0;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 92399f1..7786c4d 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -432,7 +432,7 @@
};
MPEG4Writer::MPEG4Writer(int fd) {
- initInternal(fd);
+ initInternal(fd, true /*isFirstSession*/);
}
MPEG4Writer::~MPEG4Writer() {
@@ -451,19 +451,26 @@
}
}
-void MPEG4Writer::initInternal(int fd) {
+void MPEG4Writer::initInternal(int fd, bool isFirstSession) {
ALOGV("initInternal");
mFd = dup(fd);
mNextFd = -1;
mInitCheck = mFd < 0? NO_INIT: OK;
- mIsRealTimeRecording = true;
- mUse4ByteNalLength = true;
- mUse32BitOffset = true;
- mIsFileSizeLimitExplicitlyRequested = false;
+
+ mInterleaveDurationUs = 1000000;
+
+ mStartTimestampUs = -1ll;
+ mStartTimeOffsetMs = -1;
mPaused = false;
mStarted = false;
mWriterThreadStarted = false;
mSendNotify = false;
+
+ // Reset following variables for all the sessions and they will be
+ // initialized in start(MetaData *param).
+ mIsRealTimeRecording = true;
+ mUse4ByteNalLength = true;
+ mUse32BitOffset = true;
mOffset = 0;
mMdatOffset = 0;
mMoovBoxBuffer = NULL;
@@ -472,17 +479,21 @@
mFreeBoxOffset = 0;
mStreamableFile = false;
mEstimatedMoovBoxSize = 0;
- mMoovExtraSize = 0;
- mInterleaveDurationUs = 1000000;
mTimeScale = -1;
- mStartTimestampUs = -1ll;
- mLatitudex10000 = 0;
- mLongitudex10000 = 0;
- mAreGeoTagsAvailable = false;
- mStartTimeOffsetMs = -1;
- mSwitchPending = false;
- mMetaKeys = new AMessage();
- addDeviceMeta();
+
+ // Following variables only need to be set for the first recording session.
+ // And they will stay the same for all the recording sessions.
+ if (isFirstSession) {
+ mMoovExtraSize = 0;
+ mMetaKeys = new AMessage();
+ addDeviceMeta();
+ mLatitudex10000 = 0;
+ mLongitudex10000 = 0;
+ mAreGeoTagsAvailable = false;
+ mSwitchPending = false;
+ mIsFileSizeLimitExplicitlyRequested = false;
+ }
+
// Verify mFd is seekable
off64_t off = lseek64(mFd, 0, SEEK_SET);
if (off < 0) {
@@ -873,19 +884,8 @@
}
status_t MPEG4Writer::pause() {
- if (mInitCheck != OK) {
- return OK;
- }
- mPaused = true;
- status_t err = OK;
- for (List<Track *>::iterator it = mTracks.begin();
- it != mTracks.end(); ++it) {
- status_t status = (*it)->pause();
- if (status != OK) {
- err = status;
- }
- }
- return err;
+ ALOGW("MPEG4Writer: pause is not supported");
+ return ERROR_UNSUPPORTED;
}
void MPEG4Writer::stopWriterThread() {
@@ -1833,7 +1833,7 @@
int fd = mNextFd;
mNextFd = -1;
mLock.unlock();
- initInternal(fd);
+ initInternal(fd, false /*isFirstSession*/);
start(mStartMeta.get());
mSwitchPending = false;
notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED, 0);
@@ -1860,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) {
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index e31c37c..810b0d6 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/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index f0c27ac..4ff2bfe 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -85,7 +85,8 @@
status_t status;
if (fd < 0) {
// couldn't open it locally, maybe the media server can?
- status = mRetriever->setDataSource(NULL /* httpService */, path);
+ sp<IMediaHTTPService> nullService;
+ status = mRetriever->setDataSource(nullService, path);
} else {
status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
close(fd);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 03dc9df..a53897f 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -121,12 +121,12 @@
}
status_t StagefrightMetadataRetriever::setDataSource(
- const sp<DataSource>& source) {
+ const sp<DataSource>& source, const char *mime) {
ALOGV("setDataSource(DataSource)");
clearMetadata();
mSource = source;
- mExtractor = MediaExtractor::Create(mSource);
+ mExtractor = MediaExtractor::Create(mSource, mime);
if (mExtractor == NULL) {
ALOGE("Failed to instantiate a MediaExtractor.");
@@ -137,17 +137,173 @@
return OK;
}
+static VideoFrame *allocVideoFrame(
+ const sp<MetaData> &trackMeta, int32_t width, int32_t height, int32_t bpp, bool metaOnly) {
+ int32_t rotationAngle;
+ if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
+ rotationAngle = 0; // By default, no rotation
+ }
+
+ uint32_t type;
+ const void *iccData;
+ size_t iccSize;
+ if (!trackMeta->findData(kKeyIccProfile, &type, &iccData, &iccSize)){
+ iccData = NULL;
+ iccSize = 0;
+ }
+
+ int32_t sarWidth, sarHeight;
+ int32_t displayWidth, displayHeight;
+ if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
+ && trackMeta->findInt32(kKeySARHeight, &sarHeight)
+ && sarHeight != 0) {
+ displayWidth = (width * sarWidth) / sarHeight;
+ displayHeight = height;
+ } else if (trackMeta->findInt32(kKeyDisplayWidth, &displayWidth)
+ && trackMeta->findInt32(kKeyDisplayHeight, &displayHeight)
+ && displayWidth > 0 && displayHeight > 0
+ && width > 0 && height > 0) {
+ ALOGV("found display size %dx%d", displayWidth, displayHeight);
+ } else {
+ displayWidth = width;
+ displayHeight = height;
+ }
+
+ return new VideoFrame(width, height, displayWidth, displayHeight,
+ rotationAngle, bpp, !metaOnly, iccData, iccSize);
+}
+
+static bool getDstColorFormat(android_pixel_format_t colorFormat,
+ OMX_COLOR_FORMATTYPE *omxColorFormat, int32_t *bpp) {
+ switch (colorFormat) {
+ case HAL_PIXEL_FORMAT_RGB_565:
+ {
+ *omxColorFormat = OMX_COLOR_Format16bitRGB565;
+ *bpp = 2;
+ return true;
+ }
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ {
+ *omxColorFormat = OMX_COLOR_Format32BitRGBA8888;
+ *bpp = 4;
+ return true;
+ }
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ {
+ *omxColorFormat = OMX_COLOR_Format32bitBGRA8888;
+ *bpp = 4;
+ return true;
+ }
+ default:
+ {
+ ALOGE("Unsupported color format: %d", colorFormat);
+ break;
+ }
+ }
+ return false;
+}
+
static VideoFrame *extractVideoFrame(
const AString &componentName,
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source,
int64_t frameTimeUs,
- int seekMode) {
-
+ int seekMode,
+ int colorFormat,
+ bool metaOnly) {
sp<MetaData> format = source->getFormat();
+ MediaSource::ReadOptions::SeekMode mode =
+ static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
+ if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
+ seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
+ ALOGE("Unknown seek mode: %d", seekMode);
+ return NULL;
+ }
+
+ int32_t dstBpp;
+ OMX_COLOR_FORMATTYPE dstFormat;
+ if (!getDstColorFormat(
+ (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
+ return NULL;
+ }
+
+ if (metaOnly) {
+ int32_t width, height;
+ CHECK(trackMeta->findInt32(kKeyWidth, &width));
+ CHECK(trackMeta->findInt32(kKeyHeight, &height));
+ return allocVideoFrame(trackMeta, width, height, dstBpp, true);
+ }
+
+ MediaSource::ReadOptions options;
+ sp<MetaData> overrideMeta;
+ if (frameTimeUs < 0) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ int64_t thumbNailTime;
+ int32_t thumbnailWidth, thumbnailHeight;
+
+ // if we have a stand-alone thumbnail, set up the override meta,
+ // and set seekTo time to -1.
+ if (trackMeta->findInt32(kKeyThumbnailWidth, &thumbnailWidth)
+ && trackMeta->findInt32(kKeyThumbnailHeight, &thumbnailHeight)
+ && trackMeta->findData(kKeyThumbnailHVCC, &type, &data, &size)){
+ overrideMeta = new MetaData(*trackMeta);
+ overrideMeta->remove(kKeyDisplayWidth);
+ overrideMeta->remove(kKeyDisplayHeight);
+ overrideMeta->setInt32(kKeyWidth, thumbnailWidth);
+ overrideMeta->setInt32(kKeyHeight, thumbnailHeight);
+ overrideMeta->setData(kKeyHVCC, type, data, size);
+ thumbNailTime = -1ll;
+ ALOGV("thumbnail: %dx%d", thumbnailWidth, thumbnailHeight);
+ } else if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
+ || thumbNailTime < 0) {
+ thumbNailTime = 0;
+ }
+
+ options.setSeekTo(thumbNailTime, mode);
+ } else {
+ options.setSeekTo(frameTimeUs, mode);
+ }
+
+ int32_t gridRows = 1, gridCols = 1;
+ if (overrideMeta == NULL) {
+ // check if we're dealing with a tiled heif
+ int32_t gridWidth, gridHeight;
+ if (trackMeta->findInt32(kKeyGridWidth, &gridWidth) && gridWidth > 0
+ && trackMeta->findInt32(kKeyGridHeight, &gridHeight) && gridHeight > 0) {
+ int32_t width, height, displayWidth, displayHeight;
+ CHECK(trackMeta->findInt32(kKeyWidth, &width));
+ CHECK(trackMeta->findInt32(kKeyHeight, &height));
+ CHECK(trackMeta->findInt32(kKeyDisplayWidth, &displayWidth));
+ CHECK(trackMeta->findInt32(kKeyDisplayHeight, &displayHeight));
+
+ if (width >= displayWidth && height >= displayHeight
+ && (width % gridWidth == 0) && (height % gridHeight == 0)) {
+ ALOGV("grid config: %dx%d, display %dx%d, grid %dx%d",
+ width, height, displayWidth, displayHeight, gridWidth, gridHeight);
+
+ overrideMeta = new MetaData(*trackMeta);
+ overrideMeta->remove(kKeyDisplayWidth);
+ overrideMeta->remove(kKeyDisplayHeight);
+ overrideMeta->setInt32(kKeyWidth, gridWidth);
+ overrideMeta->setInt32(kKeyHeight, gridHeight);
+ gridCols = width / gridWidth;
+ gridRows = height / gridHeight;
+ } else {
+ ALOGE("Bad grid config: %dx%d, display %dx%d, grid %dx%d",
+ width, height, displayWidth, displayHeight, gridWidth, gridHeight);
+ }
+ }
+ if (overrideMeta == NULL) {
+ overrideMeta = trackMeta;
+ }
+ }
+ int32_t numTiles = gridRows * gridCols;
+
sp<AMessage> videoFormat;
- if (convertMetaDataToMessage(trackMeta, &videoFormat) != OK) {
+ if (convertMetaDataToMessage(overrideMeta, &videoFormat) != OK) {
ALOGE("b/23680780");
ALOGW("Failed to convert meta data to message");
return NULL;
@@ -160,7 +316,8 @@
// input and output ports, if seeking to a sync frame. NOTE: This request may
// fail if component requires more than that for decoding.
bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST);
- if (!isSeekingClosest) {
+ bool decodeSingleFrame = !isSeekingClosest && (numTiles == 1);
+ if (decodeSingleFrame) {
videoFormat->setInt32("android._num-input-buffers", 1);
videoFormat->setInt32("android._num-output-buffers", 1);
}
@@ -190,30 +347,6 @@
return NULL;
}
- MediaSource::ReadOptions options;
- if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
- seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
-
- ALOGE("Unknown seek mode: %d", seekMode);
- decoder->release();
- return NULL;
- }
-
- MediaSource::ReadOptions::SeekMode mode =
- static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
-
- int64_t thumbNailTime;
- if (frameTimeUs < 0) {
- if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
- || thumbNailTime < 0) {
- thumbNailTime = 0;
- }
- options.setSeekTo(thumbNailTime, mode);
- } else {
- thumbNailTime = -1;
- options.setSeekTo(frameTimeUs, mode);
- }
-
err = source->start();
if (err != OK) {
ALOGW("source failed to start: %d (%s)", err, asString(err));
@@ -258,6 +391,9 @@
bool firstSample = true;
int64_t targetTimeUs = -1ll;
+ VideoFrame *frame = NULL;
+ int32_t tilesDecoded = 0;
+
do {
size_t inputIndex = -1;
int64_t ptsUs = 0ll;
@@ -282,6 +418,9 @@
if (err != OK) {
ALOGW("Input Error or EOS");
haveMoreInputs = false;
+ if (err == ERROR_END_OF_STREAM) {
+ err = OK;
+ }
break;
}
if (firstSample && isSeekingClosest) {
@@ -293,6 +432,7 @@
if (mediaBuffer->range_length() > codecBuffer->capacity()) {
ALOGE("buffer size (%zu) too large for codec input size (%zu)",
mediaBuffer->range_length(), codecBuffer->capacity());
+ haveMoreInputs = false;
err = BAD_VALUE;
} else {
codecBuffer->setRange(0, mediaBuffer->range_length());
@@ -301,19 +441,20 @@
memcpy(codecBuffer->data(),
(const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
mediaBuffer->range_length());
- if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) {
- // Only need to decode one IDR frame, unless we're seeking with CLOSEST
- // option, in which case we need to actually decode to targetTimeUs.
- haveMoreInputs = false;
- flags |= MediaCodec::BUFFER_FLAG_EOS;
- }
}
mediaBuffer->release();
break;
}
- if (err == OK && inputIndex < inputBuffers.size()) {
+ if (haveMoreInputs && inputIndex < inputBuffers.size()) {
+ if (isAvcOrHevc && IsIDR(codecBuffer) && decodeSingleFrame) {
+ // Only need to decode one IDR frame, unless we're seeking with CLOSEST
+ // option, in which case we need to actually decode to targetTimeUs.
+ haveMoreInputs = false;
+ flags |= MediaCodec::BUFFER_FLAG_EOS;
+ }
+
ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
codecBuffer->size(), ptsUs, flags);
err = decoder->queueInputBuffer(
@@ -352,11 +493,70 @@
} else if (err == OK) {
// If we're seeking with CLOSEST option and obtained a valid targetTimeUs
// from the extractor, decode to the specified frame. Otherwise we're done.
- done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
- if (!done) {
- err = decoder->releaseOutputBuffer(index);
+ sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
+
+ int32_t width, height;
+ CHECK(outputFormat != NULL);
+ CHECK(outputFormat->findInt32("width", &width));
+ CHECK(outputFormat->findInt32("height", &height));
+
+ int32_t crop_left, crop_top, crop_right, crop_bottom;
+ if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
+ crop_left = crop_top = 0;
+ crop_right = width - 1;
+ crop_bottom = height - 1;
}
+
+ if (frame == NULL) {
+ frame = allocVideoFrame(
+ trackMeta,
+ (crop_right - crop_left + 1) * gridCols,
+ (crop_bottom - crop_top + 1) * gridRows,
+ dstBpp,
+ false /*metaOnly*/);
+ }
+
+ int32_t srcFormat;
+ CHECK(outputFormat->findInt32("color-format", &srcFormat));
+
+ ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat);
+
+ int32_t dstLeft, dstTop, dstRight, dstBottom;
+ if (numTiles == 1) {
+ dstLeft = crop_left;
+ dstTop = crop_top;
+ dstRight = crop_right;
+ dstBottom = crop_bottom;
+ } else {
+ dstLeft = tilesDecoded % gridCols * width;
+ dstTop = tilesDecoded / gridCols * height;
+ dstRight = dstLeft + width - 1;
+ dstBottom = dstTop + height - 1;
+ }
+
+ if (converter.isValid()) {
+ err = converter.convert(
+ (const uint8_t *)videoFrameBuffer->data(),
+ width, height,
+ crop_left, crop_top, crop_right, crop_bottom,
+ frame->mData,
+ frame->mWidth,
+ frame->mHeight,
+ dstLeft, dstTop, dstRight, dstBottom);
+ } else {
+ ALOGE("Unable to convert from format 0x%08x to 0x%08x",
+ srcFormat, dstFormat);
+
+ err = ERROR_UNSUPPORTED;
+ }
+
+ done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
+ if (numTiles > 1) {
+ tilesDecoded++;
+ done &= (tilesDecoded >= numTiles);
+ }
+ err = decoder->releaseOutputBuffer(index);
} else {
ALOGW("Received error %d (%s) instead of output", err, asString(err));
done = true;
@@ -366,102 +566,11 @@
}
} while (err == OK && !done);
- if (err != OK || size <= 0 || outputFormat == NULL) {
- ALOGE("Failed to decode thumbnail frame");
- source->stop();
- decoder->release();
- return NULL;
- }
-
- ALOGV("successfully decoded video frame.");
- sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
-
- if (thumbNailTime >= 0) {
- if (timeUs != thumbNailTime) {
- AString mime;
- CHECK(outputFormat->findString("mime", &mime));
-
- ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
- (long long)thumbNailTime, (long long)timeUs, mime.c_str());
- }
- }
-
- int32_t width, height;
- CHECK(outputFormat->findInt32("width", &width));
- CHECK(outputFormat->findInt32("height", &height));
-
- int32_t crop_left, crop_top, crop_right, crop_bottom;
- if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
- crop_left = crop_top = 0;
- crop_right = width - 1;
- crop_bottom = height - 1;
- }
-
- int32_t rotationAngle;
- if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
- rotationAngle = 0; // By default, no rotation
- }
-
- VideoFrame *frame = new VideoFrame;
- frame->mWidth = crop_right - crop_left + 1;
- frame->mHeight = crop_bottom - crop_top + 1;
- frame->mDisplayWidth = frame->mWidth;
- frame->mDisplayHeight = frame->mHeight;
- frame->mSize = frame->mWidth * frame->mHeight * 2;
- frame->mData = new uint8_t[frame->mSize];
- frame->mRotationAngle = rotationAngle;
-
- int32_t sarWidth, sarHeight;
- if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
- && trackMeta->findInt32(kKeySARHeight, &sarHeight)
- && sarHeight != 0) {
- frame->mDisplayWidth = (frame->mDisplayWidth * sarWidth) / sarHeight;
- } else {
- int32_t width, height;
- if (trackMeta->findInt32(kKeyDisplayWidth, &width)
- && trackMeta->findInt32(kKeyDisplayHeight, &height)
- && frame->mDisplayWidth > 0 && frame->mDisplayHeight > 0
- && width > 0 && height > 0) {
- if (frame->mDisplayHeight * (int64_t)width / height > (int64_t)frame->mDisplayWidth) {
- frame->mDisplayHeight =
- (int32_t)(height * (int64_t)frame->mDisplayWidth / width);
- } else {
- frame->mDisplayWidth =
- (int32_t)(frame->mDisplayHeight * (int64_t)width / height);
- }
- ALOGV("thumbNail width and height are overridden to %d x %d",
- frame->mDisplayWidth, frame->mDisplayHeight);
- }
- }
-
- int32_t srcFormat;
- CHECK(outputFormat->findInt32("color-format", &srcFormat));
-
- ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
-
- if (converter.isValid()) {
- err = converter.convert(
- (const uint8_t *)videoFrameBuffer->data(),
- width, height,
- crop_left, crop_top, crop_right, crop_bottom,
- frame->mData,
- frame->mWidth,
- frame->mHeight,
- 0, 0, frame->mWidth - 1, frame->mHeight - 1);
- } else {
- ALOGE("Unable to convert from format 0x%08x to RGB565", srcFormat);
-
- err = ERROR_UNSUPPORTED;
- }
-
- videoFrameBuffer.clear();
source->stop();
- decoder->releaseOutputBuffer(index);
decoder->release();
if (err != OK) {
- ALOGE("Colorconverter failed to convert frame.");
-
+ ALOGE("failed to get video frame (err %d)", err);
delete frame;
frame = NULL;
}
@@ -470,9 +579,10 @@
}
VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
- int64_t timeUs, int option) {
+ int64_t timeUs, int option, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option);
+ ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
+ timeUs, option, colorFormat, metaOnly);
if (mExtractor.get() == NULL) {
ALOGV("no extractor.");
@@ -540,8 +650,8 @@
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const AString &componentName = matchingCodecs[i];
- VideoFrame *frame =
- extractVideoFrame(componentName, trackMeta, source, timeUs, option);
+ VideoFrame *frame = extractVideoFrame(
+ componentName, trackMeta, source, timeUs, option, colorFormat, metaOnly);
if (frame != NULL) {
return frame;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 0aea8e1..3ef8f2a 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -237,6 +237,11 @@
OMX_VIDEO_AVCPROFILETYPE codecProfile;
OMX_VIDEO_AVCLEVELTYPE codecLevel;
if (profiles.map(profile, &codecProfile)) {
+ if (profile == 66 && (constraints & 0x40)) {
+ codecProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedBaseline;
+ } else if (profile == 100 && (constraints & 0x0C) == 0x0C) {
+ codecProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedHigh;
+ }
format->setInt32("profile", codecProfile);
if (levels.map(level, &codecLevel)) {
// for 9 && 11 decide level based on profile and constraint_set3 flag
@@ -1874,5 +1879,13 @@
return result;
}
+void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
} // namespace android
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index 6e04c1e..21c00a1 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_aacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftAAC2.cpp",
@@ -33,4 +37,5 @@
"libcutils",
"liblog",
],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 1a7ffca..fb368c2 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_aacenc",
+ vendor_available: true,
srcs: [
"basic_op/basicop2.c",
@@ -111,6 +112,10 @@
cc_library_shared {
name: "libstagefright_soft_aacenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAACEncoder2.cpp"],
@@ -140,6 +145,7 @@
"libutils",
"liblog",
],
+ compile_multilib: "32",
}
cc_library_shared {
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index c5ac558..5177593 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_amrnb_common",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"src/add.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index 996183b..d266dc2 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrnbdec",
+ vendor_available: true,
srcs: [
"src/a_refl.cpp",
@@ -55,13 +56,20 @@
// ],
//},
- shared_libs: ["libstagefright_amrnb_common"],
+ shared_libs: [
+ "libstagefright_amrnb_common",
+ "liblog",
+ ],
}
//###############################################################################
cc_library_shared {
name: "libstagefright_soft_amrdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMR.cpp"],
@@ -96,6 +104,7 @@
"liblog",
"libstagefright_amrnb_common",
],
+ compile_multilib: "32",
}
//###############################################################################
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index af0f8c2..6dc2dc1 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrnbenc",
+ vendor_available: true,
srcs: [
"src/amrencode.cpp",
@@ -83,6 +84,10 @@
cc_library_shared {
name: "libstagefright_soft_amrnbenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMRNBEncoder.cpp"],
@@ -110,6 +115,7 @@
"liblog",
"libstagefright_amrnb_common",
],
+ compile_multilib: "32",
}
//###############################################################################
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index e261c04..b932ccc 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrwbdec",
+ vendor_available: true,
srcs: [
"src/agc2_amr_wb.cpp",
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 5c5a122..8968991 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrwbenc",
+ vendor_available: true,
srcs: [
"src/autocorr.c",
@@ -144,6 +145,10 @@
cc_library_shared {
name: "libstagefright_soft_amrwbenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMRWBEncoder.cpp"],
@@ -173,6 +178,7 @@
"liblog",
"libstagefright_enc_common",
],
+ compile_multilib: "32",
}
//###############################################################################
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 6b996a7..1f43803 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_avcdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libavcdec"],
srcs: ["SoftAVCDec.cpp"],
@@ -12,7 +16,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -30,4 +34,5 @@
},
ldflags: ["-Wl,-Bsymbolic"],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index 248ab6d..c342b6c 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -48,10 +48,14 @@
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
static const CodecProfileLevel kProfileLevels[] = {
+ { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel52 },
+
{ OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
{ OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 },
+ { OMX_VIDEO_AVCProfileConstrainedHigh, OMX_VIDEO_AVCLevel52 },
+
{ OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 },
};
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index 49021a9..66507a7 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_avcenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libavcenc"],
srcs: ["SoftAVCEnc.cpp"],
@@ -13,7 +17,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
@@ -30,4 +34,5 @@
},
ldflags: ["-Wl,-Bsymbolic"],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index b1af17b..358c743 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -27,7 +27,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
#include <OMX_IndexExt.h>
#include <OMX_VideoExt.h>
@@ -75,33 +74,11 @@
};
static const CodecProfileLevel kProfileLevels[] = {
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
+ { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel41 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
};
static size_t GetCPUCoreCount() {
@@ -629,8 +606,10 @@
level = 30;
} else if (displaySizeY > (352 * 288)) {
level = 21;
- } else {
+ } else if (displaySizeY > (176 * 144)) {
level = 20;
+ } else {
+ level = 10;
}
mAVCEncLevel = MAX(level, mAVCEncLevel);
@@ -963,7 +942,8 @@
return OMX_ErrorUndefined;
}
- avcParams->eProfile = OMX_VIDEO_AVCProfileBaseline;
+ // TODO: maintain profile
+ avcParams->eProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedBaseline;
avcParams->eLevel = omxLevel;
avcParams->nRefFrames = 1;
avcParams->bUseHadamard = OMX_TRUE;
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index 021e6af..3726922 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -1,5 +1,9 @@
cc_library {
name: "libstagefright_enc_common",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["cmnMemory.c"],
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 6ac264d..595cfdb 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_flacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftFlacDecoder.cpp",
@@ -33,4 +37,5 @@
"libstagefright_foundation",
"libutils",
],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index d1413f6..066917b 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -22,7 +22,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -32,5 +32,10 @@
static_libs: ["libFLAC"],
name: "libstagefright_soft_flacenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp
index b78b689..fff72a8 100644
--- a/media/libstagefright/codecs/g711/dec/Android.bp
+++ b/media/libstagefright/codecs/g711/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_g711dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftG711.cpp"],
@@ -9,7 +13,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
@@ -27,4 +31,5 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp
index 8e86ad6..753eeef 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.bp
+++ b/media/libstagefright/codecs/gsm/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_gsmdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftGSM.cpp"],
@@ -23,11 +27,12 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
],
static_libs: ["libgsm"],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp
index cd75c97..7fa74d4 100644
--- a/media/libstagefright/codecs/hevcdec/Android.bp
+++ b/media/libstagefright/codecs/hevcdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_hevcdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libhevcdec"],
srcs: ["SoftHEVC.cpp"],
@@ -22,7 +26,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -33,4 +37,5 @@
// requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
// Bug: 16853291
ldflags: ["-Wl,-Bsymbolic"],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 04ea075..1216ae5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -1,5 +1,7 @@
cc_library_static {
name: "libstagefright_m4vh263dec",
+ vendor_available: true,
+ shared_libs: ["liblog"],
srcs: [
"src/adaptive_smooth_no_mmx.cpp",
@@ -66,6 +68,10 @@
cc_library_shared {
name: "libstagefright_soft_mpeg4dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMPEG4.cpp"],
@@ -87,7 +93,7 @@
static_libs: ["libstagefright_m4vh263dec"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -103,4 +109,5 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 411a251..39b67ab 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -31,20 +31,12 @@
namespace android {
static const CodecProfileLevel kM4VProfileLevels[] = {
- { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0 },
- { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0b },
- { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level1 },
- { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level2 },
{ OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level3 },
};
static const CodecProfileLevel kH263ProfileLevels[] = {
- { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level10 },
- { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level20 },
{ OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level30 },
{ OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 },
- { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level10 },
- { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level20 },
{ OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level30 },
{ OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level45 },
};
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index da5b162..640718d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_m4vh263enc",
+ vendor_available: true,
srcs: [
"src/bitstream_io.cpp",
@@ -52,6 +53,10 @@
cc_library_shared {
name: "libstagefright_soft_mpeg4enc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMPEG4Encoder.cpp"],
@@ -75,7 +80,7 @@
static_libs: ["libstagefright_m4vh263enc"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
@@ -90,6 +95,7 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
//###############################################################################
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index 6d4cb69..7b90a01 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -29,7 +29,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
#include "SoftMPEG4Encoder.h"
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index 0d0a2c6..273fa31 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_mp3dec",
+ vendor_available: true,
srcs: [
"src/pvmp3_normalize.cpp",
@@ -77,6 +78,10 @@
cc_library_shared {
name: "libstagefright_soft_mp3dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMP3.cpp"],
@@ -102,7 +107,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -110,6 +115,7 @@
],
static_libs: ["libstagefright_mp3dec"],
+ compile_multilib: "32",
}
//###############################################################################
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index 0144581..15fdde7 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_mpeg2dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libmpeg2dec"],
srcs: ["SoftMPEG2.cpp"],
@@ -12,7 +16,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -30,4 +34,5 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index c4242c2..59c1f5d 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vpxdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftVPX.cpp"],
@@ -11,7 +15,7 @@
static_libs: ["libvpx"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -30,4 +34,5 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index 114c1be..741774c 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vpxenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftVPXEncoder.cpp",
@@ -26,10 +30,11 @@
static_libs: ["libvpx"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
"liblog",
],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index 5d9c4c8..88d6ec4 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_opusdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftOpus.cpp"],
@@ -10,13 +14,15 @@
shared_libs: [
"libopus",
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
"liblog",
],
+ cflags: ["-Werror"],
+
sanitize: {
misc_undefined: [
"signed-integer-overflow",
@@ -27,4 +33,5 @@
cfi: true,
},
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
index d1f5e59..813004b 100644
--- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
+++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
@@ -62,6 +62,7 @@
mSeekPreRoll(0),
mAnchorTimeUs(0),
mNumFramesOutput(0),
+ mHaveEOS(false),
mOutputPortSettingsChange(NONE) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
@@ -384,7 +385,31 @@
return static_cast<double>(ns) * kRate / 1000000000;
}
-void SoftOpus::onQueueFilled(OMX_U32 portIndex) {
+void SoftOpus::handleEOS() {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+ CHECK(!inQueue.empty() && !outQueue.empty());
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ mHaveEOS = true;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+
+ ++mInputBufferCount;
+}
+
+void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) {
List<BufferInfo *> &inQueue = getPortQueue(0);
List<BufferInfo *> &outQueue = getPortQueue(1);
@@ -392,104 +417,108 @@
return;
}
- if (portIndex == 0 && mInputBufferCount < 3) {
- BufferInfo *info = *inQueue.begin();
- OMX_BUFFERHEADERTYPE *header = info->mHeader;
-
- const uint8_t *data = header->pBuffer + header->nOffset;
- size_t size = header->nFilledLen;
-
- if (mInputBufferCount == 0) {
- CHECK(mHeader == NULL);
- mHeader = new OpusHeader();
- memset(mHeader, 0, sizeof(*mHeader));
- if (!ParseOpusHeader(data, size, mHeader)) {
- ALOGV("Parsing Opus Header failed.");
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
-
- uint8_t channel_mapping[kMaxChannels] = {0};
- if (mHeader->channels <= kMaxChannelsWithDefaultLayout) {
- memcpy(&channel_mapping,
- kDefaultOpusChannelLayout,
- kMaxChannelsWithDefaultLayout);
- } else {
- memcpy(&channel_mapping,
- mHeader->stream_map,
- mHeader->channels);
- }
-
- int status = OPUS_INVALID_STATE;
- mDecoder = opus_multistream_decoder_create(kRate,
- mHeader->channels,
- mHeader->num_streams,
- mHeader->num_coupled,
- channel_mapping,
- &status);
- if (!mDecoder || status != OPUS_OK) {
- ALOGV("opus_multistream_decoder_create failed status=%s",
- opus_strerror(status));
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
- status =
- opus_multistream_decoder_ctl(mDecoder,
- OPUS_SET_GAIN(mHeader->gain_db));
- if (status != OPUS_OK) {
- ALOGV("Failed to set OPUS header gain; status=%s",
- opus_strerror(status));
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
- } else if (mInputBufferCount == 1) {
- mCodecDelay = ns_to_samples(
- *(reinterpret_cast<int64_t*>(header->pBuffer +
- header->nOffset)),
- kRate);
- mSamplesToDiscard = mCodecDelay;
- } else {
- mSeekPreRoll = ns_to_samples(
- *(reinterpret_cast<int64_t*>(header->pBuffer +
- header->nOffset)),
- kRate);
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- }
-
- inQueue.erase(inQueue.begin());
- info->mOwnedByUs = false;
- notifyEmptyBufferDone(header);
- ++mInputBufferCount;
- return;
- }
-
- while (!inQueue.empty() && !outQueue.empty()) {
+ while (!mHaveEOS && !inQueue.empty() && !outQueue.empty()) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- // Ignore CSD re-submissions.
- if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ if (mInputBufferCount < 3) {
+ const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+ size_t size = inHeader->nFilledLen;
+
+ if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && size == 0) {
+ handleEOS();
+ return;
+ }
+
+ if (mInputBufferCount == 0) {
+ CHECK(mHeader == NULL);
+ mHeader = new OpusHeader();
+ memset(mHeader, 0, sizeof(*mHeader));
+ if (!ParseOpusHeader(data, size, mHeader)) {
+ ALOGV("Parsing Opus Header failed.");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ uint8_t channel_mapping[kMaxChannels] = {0};
+ if (mHeader->channels <= kMaxChannelsWithDefaultLayout) {
+ memcpy(&channel_mapping,
+ kDefaultOpusChannelLayout,
+ kMaxChannelsWithDefaultLayout);
+ } else {
+ memcpy(&channel_mapping,
+ mHeader->stream_map,
+ mHeader->channels);
+ }
+
+ int status = OPUS_INVALID_STATE;
+ mDecoder = opus_multistream_decoder_create(kRate,
+ mHeader->channels,
+ mHeader->num_streams,
+ mHeader->num_coupled,
+ channel_mapping,
+ &status);
+ if (!mDecoder || status != OPUS_OK) {
+ ALOGV("opus_multistream_decoder_create failed status=%s",
+ opus_strerror(status));
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+ status =
+ opus_multistream_decoder_ctl(mDecoder,
+ OPUS_SET_GAIN(mHeader->gain_db));
+ if (status != OPUS_OK) {
+ ALOGV("Failed to set OPUS header gain; status=%s",
+ opus_strerror(status));
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+ } else if (mInputBufferCount == 1) {
+ mCodecDelay = ns_to_samples(
+ *(reinterpret_cast<int64_t*>(inHeader->pBuffer +
+ inHeader->nOffset)),
+ kRate);
+ mSamplesToDiscard = mCodecDelay;
+ } else {
+ mSeekPreRoll = ns_to_samples(
+ *(reinterpret_cast<int64_t*>(inHeader->pBuffer +
+ inHeader->nOffset)),
+ kRate);
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ handleEOS();
+ return;
+ }
+
inQueue.erase(inQueue.begin());
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
- return;
+ ++mInputBufferCount;
+
+ continue;
+ }
+
+ // Ignore CSD re-submissions.
+ if (mInputBufferCount >= 3 && (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ handleEOS();
+ return;
+ }
+
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ continue;
}
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) {
- inQueue.erase(inQueue.begin());
- inInfo->mOwnedByUs = false;
- notifyEmptyBufferDone(inHeader);
-
- outHeader->nFilledLen = 0;
- outHeader->nFlags = OMX_BUFFERFLAG_EOS;
-
- outQueue.erase(outQueue.begin());
- outInfo->mOwnedByUs = false;
- notifyFillBufferDone(outHeader);
+ handleEOS();
return;
}
@@ -539,7 +568,6 @@
}
outHeader->nFilledLen = numFrames * sizeof(int16_t) * mHeader->channels;
- outHeader->nFlags = 0;
outHeader->nTimeStamp = mAnchorTimeUs +
(mNumFramesOutput * 1000000ll) /
@@ -548,22 +576,20 @@
mNumFramesOutput += numFrames;
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
- inHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ mHaveEOS = true;
} else {
- inInfo->mOwnedByUs = false;
- inQueue.erase(inQueue.begin());
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
+ outHeader->nFlags = 0;
}
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ notifyEmptyBufferDone(inHeader);
+ ++mInputBufferCount;
+
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
- outInfo = NULL;
notifyFillBufferDone(outHeader);
- outHeader = NULL;
-
- ++mInputBufferCount;
}
}
@@ -575,6 +601,7 @@
opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
mAnchorTimeUs = 0;
mSamplesToDiscard = mSeekPreRoll;
+ mHaveEOS = false;
}
}
@@ -591,6 +618,7 @@
}
mOutputPortSettingsChange = NONE;
+ mHaveEOS = false;
}
void SoftOpus::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.h b/media/libstagefright/codecs/opus/dec/SoftOpus.h
index fab925d..91cafa1 100644
--- a/media/libstagefright/codecs/opus/dec/SoftOpus.h
+++ b/media/libstagefright/codecs/opus/dec/SoftOpus.h
@@ -75,6 +75,7 @@
int64_t mSamplesToDiscard;
int64_t mAnchorTimeUs;
int64_t mNumFramesOutput;
+ bool mHaveEOS;
enum {
NONE,
@@ -85,6 +86,7 @@
void initPorts();
status_t initDecoder();
bool isConfigured() const;
+ void handleEOS();
DISALLOW_EVIL_CONSTRUCTORS(SoftOpus);
};
diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp
index c64027b..f21d46f 100644
--- a/media/libstagefright/codecs/raw/Android.bp
+++ b/media/libstagefright/codecs/raw/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_rawdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftRaw.cpp"],
@@ -27,4 +31,5 @@
"libutils",
"liblog",
],
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp
index 1a4de60..628b36c 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.bp
+++ b/media/libstagefright/codecs/vorbis/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vorbisdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftVorbis.cpp"],
@@ -10,7 +14,7 @@
shared_libs: [
"libvorbisidec",
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -25,4 +29,5 @@
"unsigned-integer-overflow",
],
},
+ compile_multilib: "32",
}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index 96e01b6..8912f8a 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -309,7 +309,33 @@
oggpack_readinit(bits, ref);
}
-void SoftVorbis::onQueueFilled(OMX_U32 portIndex) {
+void SoftVorbis::handleEOS() {
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ CHECK(!inQueue.empty() && !outQueue.empty());
+
+ mSawInputEos = true;
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+ mSignalledOutputEos = true;
+
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ ++mInputBufferCount;
+}
+
+void SoftVorbis::onQueueFilled(OMX_U32 /* portIndex */) {
List<BufferInfo *> &inQueue = getPortQueue(0);
List<BufferInfo *> &outQueue = getPortQueue(1);
@@ -317,69 +343,7 @@
return;
}
- if (portIndex == 0 && mInputBufferCount < 2) {
- BufferInfo *info = *inQueue.begin();
- OMX_BUFFERHEADERTYPE *header = info->mHeader;
-
- const uint8_t *data = header->pBuffer + header->nOffset;
- size_t size = header->nFilledLen;
- if (size < 7) {
- ALOGE("Too small input buffer: %zu bytes", size);
- android_errorWriteLog(0x534e4554, "27833616");
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- mSignalledError = true;
- return;
- }
-
- ogg_buffer buf;
- ogg_reference ref;
- oggpack_buffer bits;
-
- makeBitReader(
- (const uint8_t *)data + 7, size - 7,
- &buf, &ref, &bits);
-
- if (mInputBufferCount == 0) {
- CHECK(mVi == NULL);
- mVi = new vorbis_info;
- vorbis_info_init(mVi);
-
- int ret = _vorbis_unpack_info(mVi, &bits);
- if (ret != 0) {
- notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL);
- mSignalledError = true;
- return;
- }
- } else {
- int ret = _vorbis_unpack_books(mVi, &bits);
- if (ret != 0) {
- notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL);
- mSignalledError = true;
- return;
- }
-
- CHECK(mState == NULL);
- mState = new vorbis_dsp_state;
- CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
-
- 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());
- info->mOwnedByUs = false;
- notifyEmptyBufferDone(header);
-
- ++mInputBufferCount;
-
- return;
- }
-
- while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) {
+ while (!mSignalledOutputEos && (!inQueue.empty() || mSawInputEos) && !outQueue.empty()) {
BufferInfo *inInfo = NULL;
OMX_BUFFERHEADERTYPE *inHeader = NULL;
if (!inQueue.empty()) {
@@ -393,6 +357,73 @@
int32_t numPageSamples = 0;
if (inHeader) {
+ if (mInputBufferCount < 2) {
+ const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+ size_t size = inHeader->nFilledLen;
+
+ if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && size == 0) {
+ handleEOS();
+ return;
+ }
+
+ if (size < 7) {
+ ALOGE("Too small input buffer: %zu bytes", size);
+ android_errorWriteLog(0x534e4554, "27833616");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits);
+
+ if (mInputBufferCount == 0) {
+ CHECK(mVi == NULL);
+ mVi = new vorbis_info;
+ vorbis_info_init(mVi);
+
+ int ret = _vorbis_unpack_info(mVi, &bits);
+ if (ret != 0) {
+ notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL);
+ mSignalledError = true;
+ return;
+ }
+ } else {
+ int ret = _vorbis_unpack_books(mVi, &bits);
+ if (ret != 0) {
+ notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ CHECK(mState == NULL);
+ mState = new vorbis_dsp_state;
+ CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
+
+ 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;
+ }
+ }
+
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ handleEOS();
+ return;
+ }
+
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ ++mInputBufferCount;
+
+ continue;
+ }
+
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
mSawInputEos = true;
}
@@ -406,8 +437,7 @@
return;
}
memcpy(&numPageSamples,
- inHeader->pBuffer
- + inHeader->nOffset + inHeader->nFilledLen - 4,
+ inHeader->pBuffer + inHeader->nOffset + inHeader->nFilledLen - 4,
sizeof(numPageSamples));
if (inHeader->nOffset == 0) {
@@ -446,6 +476,14 @@
int numFrames = 0;
outHeader->nFlags = 0;
+
+ if (mState == nullptr || mVi == nullptr) {
+ notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL);
+ mSignalledError = true;
+ ALOGE("onQueueFilled, input does not have CSD");
+ return;
+ }
+
int err = vorbis_dsp_synthesis(mState, &pack, 1);
if (err != 0) {
// FIXME temporary workaround for log spam
@@ -495,18 +533,13 @@
if (inHeader) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
- inInfo = NULL;
notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
+ ++mInputBufferCount;
}
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
- outInfo = NULL;
notifyFillBufferDone(outHeader);
- outHeader = NULL;
-
- ++mInputBufferCount;
}
}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
index 52d1632..5ff8ea4 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
@@ -72,6 +72,7 @@
void initPorts();
status_t initDecoder();
bool isConfigured() const;
+ void handleEOS();
DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis);
};
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 3ca7cc0..0982006 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/MediaErrors.h>
#include "libyuv/convert_from.h"
+#include "libyuv/video_common.h"
#define USE_LIBYUV
@@ -41,17 +42,17 @@
}
bool ColorConverter::isValid() const {
- if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
- return false;
- }
-
switch (mSrcFormat) {
case OMX_COLOR_FormatYUV420Planar:
+ return mDstFormat == OMX_COLOR_Format16bitRGB565
+ || mDstFormat == OMX_COLOR_Format32BitRGBA8888
+ || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
+
case OMX_COLOR_FormatCbYCrY:
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
case OMX_COLOR_FormatYUV420SemiPlanar:
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
- return true;
+ return mDstFormat == OMX_COLOR_Format16bitRGB565;
default:
return false;
@@ -62,14 +63,43 @@
void *bits,
size_t width, size_t height,
size_t cropLeft, size_t cropTop,
- size_t cropRight, size_t cropBottom)
+ size_t cropRight, size_t cropBottom,
+ OMX_COLOR_FORMATTYPE colorFromat)
: mBits(bits),
+ mColorFormat(colorFromat),
mWidth(width),
mHeight(height),
mCropLeft(cropLeft),
mCropTop(cropTop),
mCropRight(cropRight),
mCropBottom(cropBottom) {
+ switch(mColorFormat) {
+ case OMX_COLOR_Format16bitRGB565:
+ mBpp = 2;
+ mStride = 2 * mWidth;
+ break;
+
+ case OMX_COLOR_Format32bitBGRA8888:
+ case OMX_COLOR_Format32BitRGBA8888:
+ mBpp = 4;
+ mStride = 4 * mWidth;
+ break;
+
+ case OMX_COLOR_FormatYUV420Planar:
+ case OMX_COLOR_FormatCbYCrY:
+ case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ case OMX_COLOR_FormatYUV420SemiPlanar:
+ case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
+ mBpp = 1;
+ mStride = mWidth;
+ break;
+
+ default:
+ ALOGE("Unsupported color format %d", mColorFormat);
+ mBpp = 1;
+ mStride = mWidth;
+ break;
+ }
}
size_t ColorConverter::BitmapParams::cropWidth() const {
@@ -89,19 +119,15 @@
size_t dstWidth, size_t dstHeight,
size_t dstCropLeft, size_t dstCropTop,
size_t dstCropRight, size_t dstCropBottom) {
- if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
- return ERROR_UNSUPPORTED;
- }
-
BitmapParams src(
const_cast<void *>(srcBits),
srcWidth, srcHeight,
- srcCropLeft, srcCropTop, srcCropRight, srcCropBottom);
+ srcCropLeft, srcCropTop, srcCropRight, srcCropBottom, mSrcFormat);
BitmapParams dst(
dstBits,
dstWidth, dstHeight,
- dstCropLeft, dstCropTop, dstCropRight, dstCropBottom);
+ dstCropLeft, dstCropTop, dstCropRight, dstCropBottom, mDstFormat);
status_t err;
@@ -212,26 +238,104 @@
return ERROR_UNSUPPORTED;
}
- uint16_t *dst_ptr = (uint16_t *)dst.mBits
- + dst.mCropTop * dst.mWidth + dst.mCropLeft;
+ uint8_t *dst_ptr = (uint8_t *)dst.mBits
+ + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
+ (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
const uint8_t *src_u =
- (const uint8_t *)src_y + src.mWidth * src.mHeight
- + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
+ (const uint8_t *)src.mBits + src.mStride * src.mHeight
+ + (src.mCropTop / 2) * (src.mStride / 2) + (src.mCropLeft / 2);
const uint8_t *src_v =
- src_u + (src.mWidth / 2) * (src.mHeight / 2);
+ src_u + (src.mStride / 2) * (src.mHeight / 2);
+ switch (mDstFormat) {
+ case OMX_COLOR_Format16bitRGB565:
+ libyuv::I420ToRGB565(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
+ (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ break;
- libyuv::I420ToRGB565(src_y, src.mWidth, src_u, src.mWidth / 2, src_v, src.mWidth / 2,
- (uint8 *)dst_ptr, dst.mWidth * 2, dst.mWidth, dst.mHeight);
+ case OMX_COLOR_Format32BitRGBA8888:
+ libyuv::ConvertFromI420(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
+ (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight(), libyuv::FOURCC_ABGR);
+ break;
+
+ case OMX_COLOR_Format32bitBGRA8888:
+ libyuv::ConvertFromI420(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
+ (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight(), libyuv::FOURCC_ARGB);
+ break;
+
+ default:
+ return ERROR_UNSUPPORTED;
+ }
return OK;
}
+void ColorConverter::writeToDst(
+ void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
+ signed r1, signed g1, signed b1,
+ signed r2, signed g2, signed b2) {
+ switch (mDstFormat) {
+ case OMX_COLOR_Format16bitRGB565:
+ {
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ if (uncropped) {
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
+ } else {
+ *(uint16_t *)dst_ptr = rgb1;
+ }
+ break;
+ }
+ case OMX_COLOR_Format32BitRGBA8888:
+ {
+ ((uint32_t *)dst_ptr)[0] =
+ (kAdjustedClip[r1])
+ | (kAdjustedClip[g1] << 8)
+ | (kAdjustedClip[b1] << 16)
+ | (0xFF << 24);
+
+ if (uncropped) {
+ ((uint32_t *)dst_ptr)[1] =
+ (kAdjustedClip[r2])
+ | (kAdjustedClip[g2] << 8)
+ | (kAdjustedClip[b2] << 16)
+ | (0xFF << 24);
+ }
+ break;
+ }
+ case OMX_COLOR_Format32bitBGRA8888:
+ {
+ ((uint32_t *)dst_ptr)[0] =
+ (kAdjustedClip[b1])
+ | (kAdjustedClip[g1] << 8)
+ | (kAdjustedClip[r1] << 16)
+ | (0xFF << 24);
+
+ if (uncropped) {
+ ((uint32_t *)dst_ptr)[1] =
+ (kAdjustedClip[b2])
+ | (kAdjustedClip[g2] << 8)
+ | (kAdjustedClip[r2] << 16)
+ | (0xFF << 24);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
status_t ColorConverter::convertYUV420Planar(
const BitmapParams &src, const BitmapParams &dst) {
if (!((src.mCropLeft & 1) == 0
@@ -242,18 +346,18 @@
uint8_t *kAdjustedClip = initClip();
- uint16_t *dst_ptr = (uint16_t *)dst.mBits
- + dst.mCropTop * dst.mWidth + dst.mCropLeft;
+ uint8_t *dst_ptr = (uint8_t *)dst.mBits
+ + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
+ (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
const uint8_t *src_u =
- (const uint8_t *)src_y + src.mWidth * src.mHeight
- + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
+ (const uint8_t *)src.mBits + src.mStride * src.mHeight
+ + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2;
const uint8_t *src_v =
- src_u + (src.mWidth / 2) * (src.mHeight / 2);
+ src_u + (src.mStride / 2) * (src.mHeight / 2);
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
@@ -296,31 +400,19 @@
signed g2 = (tmp2 + v_g + u_g) / 256;
signed r2 = (tmp2 + v_r) / 256;
- uint32_t rgb1 =
- ((kAdjustedClip[r1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[b1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[r2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[b2] >> 3);
-
- if (x + 1 < src.cropWidth()) {
- *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
- } else {
- dst_ptr[x] = rgb1;
- }
+ bool uncropped = x + 1 < src.cropWidth();
+ (void)writeToDst(dst_ptr + x * dst.mBpp,
+ kAdjustedClip, uncropped, r1, g1, b1, r2, g2, b2);
}
- src_y += src.mWidth;
+ src_y += src.mStride;
if (y & 1) {
- src_u += src.mWidth / 2;
- src_v += src.mWidth / 2;
+ src_u += src.mStride / 2;
+ src_v += src.mStride / 2;
}
- dst_ptr += dst.mWidth;
+ dst_ptr += dst.mStride;
}
return OK;
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
index ce164a2..a127843 100644
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -34,20 +34,21 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.h264.decoder" type="video/avc">
- <!-- profiles and levels: ProfileHigh : Level41 -->
- <Limit name="size" min="16x16" max="1920x1088" />
+ <!-- profiles and levels: ProfileHigh : Level52 -->
+ <Limit name="size" min="2x2" max="4096x4096" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="blocks-per-second" range="1-244800" />
- <Limit name="bitrate" range="1-12000000" />
+ <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 -->
+ <Limit name="blocks-per-second" range="1-1966080" />
+ <Limit name="bitrate" range="1-48000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
<!-- profiles and levels: ProfileMain : MainTierLevel51 -->
- <Limit name="size" min="2x2" max="2048x2048" />
+ <Limit name="size" min="2x2" max="4096x4096" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="8x8" />
- <Limit name="block-count" range="1-139264" />
+ <Limit name="block-count" range="1-196608" /> <!-- max 4096x3072 -->
<Limit name="blocks-per-second" range="1-2000000" />
<Limit name="bitrate" range="1-10000000" />
<Feature name="adaptive-playback" />
@@ -56,6 +57,7 @@
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
+ <Limit name="block-count" range="1-16384" />
<Limit name="blocks-per-second" range="1-1000000" />
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
@@ -64,6 +66,7 @@
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
+ <Limit name="block-count" range="1-16384" />
<Limit name="blocks-per-second" range="1-500000" />
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
@@ -79,10 +82,11 @@
</MediaCodec>
<MediaCodec name="OMX.google.h264.encoder" type="video/avc">
<!-- profiles and levels: ProfileBaseline : Level41 -->
- <Limit name="size" min="16x16" max="1920x1088" />
+ <Limit name="size" min="16x16" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="blocks-per-second" range="1-244800" />
+ <Limit name="block-count" range="1-8192" /> <!-- max 2048x1024 -->
+ <Limit name="blocks-per-second" range="1-245760" />
<Limit name="bitrate" range="1-12000000" />
<Feature name="intra-refresh" />
</MediaCodec>
@@ -98,6 +102,9 @@
<!-- profiles and levels: ProfileMain : Level_Version0-3 -->
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
+ <Limit name="block-size" value="16x16" />
+ <!-- 2016 devices can encode at about 10fps at this block count -->
+ <Limit name="block-count" range="1-16384" />
<Limit name="bitrate" range="1-40000000" />
<Feature name="bitrate-modes" value="VBR,CBR" />
</MediaCodec>
@@ -105,6 +112,9 @@
<!-- profiles and levels: ProfileMain : Level_Version0-3 -->
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
+ <Limit name="block-size" value="16x16" />
+ <!-- 2016 devices can encode at about 8fps at this block count -->
+ <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
<Limit name="bitrate" range="1-40000000" />
<Feature name="bitrate-modes" value="VBR,CBR" />
</MediaCodec>
diff --git a/media/libstagefright/data/media_codecs_google_video_le.xml b/media/libstagefright/data/media_codecs_google_video_le.xml
index 034a038..d7c6570 100644
--- a/media/libstagefright/data/media_codecs_google_video_le.xml
+++ b/media/libstagefright/data/media_codecs_google_video_le.xml
@@ -34,22 +34,22 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.h264.decoder" type="video/avc">
- <!-- profiles and levels: ProfileBaseline : Level51 -->
+ <!-- profiles and levels: ProfileHigh : Level51 -->
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="block-count" range="1-8160" />
- <Limit name="blocks-per-second" range="1-489600" />
+ <Limit name="block-count" range="1-16384" />
+ <Limit name="blocks-per-second" range="1-491520" />
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
<!-- profiles and levels: ProfileMain : MainTierLevel51 -->
- <Limit name="size" min="2x2" max="1280x1280" />
+ <Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="8x8" />
- <Limit name="block-count" range="1-139264" />
- <Limit name="blocks-per-second" range="1-432000" />
+ <Limit name="block-count" range="1-65536" />
+ <Limit name="blocks-per-second" range="1-491520" />
<Limit name="bitrate" range="1-5000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
@@ -57,7 +57,7 @@
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="block-count" range="1-8160" />
+ <Limit name="block-count" range="1-8192" /> <!-- max 2048x1024 -->
<Limit name="blocks-per-second" range="1-500000" />
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
@@ -66,7 +66,7 @@
<Limit name="size" min="2x2" max="1280x1280" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="block-count" range="1-3600" />
+ <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
<Limit name="blocks-per-second" range="1-108000" />
<Limit name="bitrate" range="1-5000000" />
<Feature name="adaptive-playback" />
@@ -81,12 +81,14 @@
<Limit name="bitrate" range="1-128000" />
</MediaCodec>
<MediaCodec name="OMX.google.h264.encoder" type="video/avc">
- <!-- profiles and levels: ProfileBaseline : Level2 -->
- <Limit name="size" min="16x16" max="896x896" />
- <Limit name="alignment" value="16x16" />
+ <!-- profiles and levels: ProfileBaseline : Level3 -->
+ <Limit name="size" min="16x16" max="1808x1808" />
+ <Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="blocks-per-second" range="1-11880" />
+ <Limit name="block-count" range="1-1620" />
+ <Limit name="blocks-per-second" range="1-40500" />
<Limit name="bitrate" range="1-2000000" />
+ <Feature name="intra-refresh" />
</MediaCodec>
<MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es">
<!-- profiles and levels: ProfileCore : Level2 -->
@@ -100,7 +102,8 @@
<!-- profiles and levels: ProfileMain : Level_Version0-3 -->
<Limit name="size" min="2x2" max="1280x1280" />
<Limit name="alignment" value="2x2" />
- <Limit name="block-count" range="1-3600" />
+ <Limit name="block-size" value="16x16" />
+ <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
<Limit name="bitrate" range="1-20000000" />
<Feature name="bitrate-modes" value="VBR,CBR" />
</MediaCodec>
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 284c25f..1b9fe0f 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_flacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"FLACDecoder.cpp",
@@ -31,4 +35,5 @@
"libstagefright_foundation",
"libutils",
],
+ header_libs: ["libmedia_headers"],
}
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 9108ce1..221af1d 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -7,6 +7,9 @@
cc_library_shared {
name: "libstagefright_foundation",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
include_dirs: [
"frameworks/av/include",
"frameworks/native/include",
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index 7da7db9..8f32582 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -23,6 +23,7 @@
sp<ABuffer> decodeBase64(const AString &s) {
size_t n = s.size();
+
if ((n % 4) != 0) {
return NULL;
}
@@ -45,7 +46,6 @@
size_t outLen = (n / 4) * 3 - padding;
sp<ABuffer> buffer = new ABuffer(outLen);
-
uint8_t *out = buffer->data();
if (out == NULL || buffer->size() < outLen) {
return NULL;
@@ -61,9 +61,9 @@
value = 26 + c - 'a';
} else if (c >= '0' && c <= '9') {
value = 52 + c - '0';
- } else if (c == '+') {
+ } else if (c == '+' || c == '-') {
value = 62;
- } else if (c == '/') {
+ } else if (c == '/' || c == '_') {
value = 63;
} else if (c != '=') {
return NULL;
@@ -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; }
@@ -145,4 +144,26 @@
}
}
+void encodeBase64Url(
+ const void *_data, size_t size, AString *out) {
+ encodeBase64(_data, size, out);
+
+ if ((-1 != out->find("+")) || (-1 != out->find("/"))) {
+ size_t outLen = out->size();
+ char *base64url = new char[outLen];
+ for (size_t i = 0; i < outLen; ++i) {
+ if (out->c_str()[i] == '+')
+ base64url[i] = '-';
+ else if (out->c_str()[i] == '/')
+ base64url[i] = '_';
+ else
+ base64url[i] = out->c_str()[i];
+ }
+
+ out->setTo(base64url, outLen);
+ delete[] base64url;
+ }
+}
+
+
} // namespace android
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
index e340b89..abc95e0 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
@@ -28,6 +28,8 @@
sp<ABuffer> decodeBase64(const AString &s);
void encodeBase64(const void *data, size_t size, AString *out);
+void encodeBase64Url(const void *data, size_t size, AString *out);
+
} // namespace android
#endif // BASE_64_H_
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
index d741c6f..a9e3c76 100644
--- a/media/libstagefright/foundation/tests/Android.mk
+++ b/media/libstagefright/foundation/tests/Android.mk
@@ -9,11 +9,13 @@
LOCAL_SRC_FILES := \
AData_test.cpp \
+ Base64_test.cpp \
Flagged_test.cpp \
TypeTraits_test.cpp \
Utils_test.cpp \
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libstagefright_foundation \
libutils \
diff --git a/media/libstagefright/foundation/tests/Base64_test.cpp b/media/libstagefright/foundation/tests/Base64_test.cpp
new file mode 100644
index 0000000..7a4289e
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Base64_test.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.
+ */
+#include <utils/Log.h>
+
+#include "gtest/gtest.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AStringUtils.h>
+#include <media/stagefright/foundation/base64.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace {
+const android::String8 kBase64Padding("=");
+};
+
+namespace android {
+
+class Base64Test : public ::testing::Test {
+};
+
+void verifyDecode(const AString* expected, const AString* in) {
+ size_t numTests = 0;
+ while (!expected[numTests].empty())
+ ++numTests;
+
+ for (size_t i = 0; i < numTests; ++i) {
+ // Since android::decodeBase64() requires padding characters,
+ // add them so length of encoded text is exactly a multiple of 4.
+ int remainder = in[i].size() % 4;
+ String8 paddedText(in[i].c_str());
+ if (remainder > 0) {
+ for (int i = 0; i < 4 - remainder; ++i) {
+ paddedText.append(kBase64Padding);
+ }
+ }
+ sp<ABuffer> result = decodeBase64(AString(paddedText.string()));
+
+ ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(),
+ reinterpret_cast<char*>(result->data()),
+ expected[i].size(), false), 0);
+ }
+}
+
+void verifyEncode(const AString* expected, const AString* in) {
+ size_t numTests = 0;
+ while (!expected[numTests].empty())
+ ++numTests;
+
+ AString out = AString("");
+ for (size_t i = 0; i < numTests; ++i) {
+ encodeBase64Url(in[i].c_str(), in[i].size(), &out);
+
+ ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(), out.c_str(),
+ expected[i].size(), false), 0);
+ }
+}
+
+TEST_F(Base64Test, TestDecodeBase64) {
+ const AString base64[] = {
+ AString("SGVsbG8gRnJpZW5kIQ"),
+ AString("R29vZCBkYXkh"),
+ AString("") // string to signal end of array
+ };
+
+ const AString clearText[] = {
+ AString("Hello Friend!"),
+ AString("Good day!"),
+ AString("")
+ };
+
+ verifyDecode(clearText, base64);
+}
+
+TEST_F(Base64Test, TestDecodeBase64Url) {
+ const AString base64Url[] = {
+ AString("SGVsbG8gRnJpZW5kICE-Pw"),
+ AString("SGVsbG8gRnJpZW5kICE_"),
+ AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+ AString("")
+ };
+
+ const AString clearText[] = {
+ AString("Hello Friend !>?"),
+ AString("Hello Friend !?"),
+ AString("Hello ?>> Friend ?>"),
+ AString("")
+ };
+
+ verifyDecode(clearText, base64Url);
+}
+
+TEST_F(Base64Test, TestDecodeMalformedBase64) {
+ const AString base64Url[] = {
+ AString("1?GawgguFyGrWKav7AX4VKUg"), // fail on parsing
+ AString("GawgguFyGrWKav7AX4V???"), // fail on length not multiple of 4
+ AString("GawgguFyGrWKav7AX4VKUg"), // ditto
+ };
+
+ for (size_t i = 0; i < 3; ++i) {
+ sp<ABuffer> result = decodeBase64(AString(base64Url[i]));
+ EXPECT_TRUE(result == nullptr);
+ }
+}
+
+TEST_F(Base64Test, TestEncodeBase64) {
+ const AString clearText[] = {
+ AString("Hello Friend!"),
+ AString("Good day!"),
+ AString("")
+ };
+
+ const AString base64[] = {
+ AString("SGVsbG8gRnJpZW5kIQ=="),
+ AString("R29vZCBkYXkh"),
+ AString("")
+ };
+
+ verifyEncode(base64, clearText);
+}
+
+TEST_F(Base64Test, TestEncodeBase64Url) {
+ const AString clearText[] = {
+ AString("Hello Friend !>?"),
+ AString("Hello Friend !?"),
+ AString("Hello ?>> Friend ?>"),
+ AString("")
+ };
+
+ const AString base64Url[] = {
+ AString("SGVsbG8gRnJpZW5kICE-Pw=="),
+ AString("SGVsbG8gRnJpZW5kICE_"),
+ AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+ AString("")
+ };
+
+ verifyEncode(base64Url, clearText);
+}
+
+} // namespace android
diff --git a/media/libstagefright/include/ItemTable.h b/media/libstagefright/include/ItemTable.h
new file mode 100644
index 0000000..5a6af5e
--- /dev/null
+++ b/media/libstagefright/include/ItemTable.h
@@ -0,0 +1,98 @@
+/*
+ * 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 ITEM_TABLE_H_
+#define ITEM_TABLE_H_
+
+#include <set>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MetaData;
+
+namespace heif {
+
+struct AssociationEntry;
+struct ImageItem;
+struct ItemLoc;
+struct ItemInfo;
+struct ItemProperty;
+struct ItemReference;
+
+/*
+ * ItemTable keeps track of all image items (including coded images, grids and
+ * tiles) inside a HEIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
+ */
+
+class ItemTable : public RefBase {
+public:
+ explicit ItemTable(const sp<DataSource> &source);
+
+ status_t parse(uint32_t type, off64_t offset, size_t size);
+
+ bool isValid() { return mImageItemsValid; }
+ sp<MetaData> getImageMeta();
+ uint32_t countImages() const;
+ status_t findPrimaryImage(uint32_t *imageIndex);
+ status_t findThumbnail(uint32_t *thumbnailIndex);
+ status_t getImageOffsetAndSize(
+ uint32_t *imageIndex, off64_t *offset, size_t *size);
+
+protected:
+ ~ItemTable();
+
+private:
+ sp<DataSource> mDataSource;
+
+ KeyedVector<uint32_t, ItemLoc> mItemLocs;
+ Vector<ItemInfo> mItemInfos;
+ Vector<AssociationEntry> mAssociations;
+ Vector<sp<ItemProperty> > mItemProperties;
+ Vector<sp<ItemReference> > mItemReferences;
+
+ uint32_t mPrimaryItemId;
+ off64_t mIdatOffset;
+ size_t mIdatSize;
+
+ std::set<uint32_t> mRequiredBoxes;
+ std::set<uint32_t> mBoxesSeen;
+
+ bool mImageItemsValid;
+ uint32_t mCurrentImageIndex;
+ KeyedVector<uint32_t, ImageItem> mItemIdToImageMap;
+
+ status_t parseIlocBox(off64_t offset, size_t size);
+ status_t parseIinfBox(off64_t offset, size_t size);
+ status_t parsePitmBox(off64_t offset, size_t size);
+ status_t parseIprpBox(off64_t offset, size_t size);
+ status_t parseIdatBox(off64_t offset, size_t size);
+ status_t parseIrefBox(off64_t offset, size_t size);
+
+ void attachProperty(const AssociationEntry &association);
+ status_t buildImageItemsIfPossible(uint32_t type);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ItemTable);
+};
+
+} // namespace heif
+} // namespace android
+
+#endif // ITEM_TABLE_H_
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index f847119..4a4c538 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -28,11 +28,14 @@
#include <utils/String8.h>
namespace android {
-
struct AMessage;
class DataSource;
class SampleTable;
class String8;
+namespace heif {
+class ItemTable;
+}
+using heif::ItemTable;
struct SidxEntry {
size_t mSize;
@@ -97,6 +100,7 @@
status_t mInitCheck;
uint32_t mHeaderTimescale;
bool mIsQT;
+ bool mIsHEIF;
Track *mFirstTrack, *mLastTrack;
@@ -134,6 +138,8 @@
SINF *mFirstSINF;
bool mIsDrm;
+ sp<ItemTable> mItemTable;
+
status_t parseDrmSINF(off64_t *offset, off64_t data_offset);
status_t parseTrackHeader(off64_t data_offset, off64_t data_size);
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index b7ac718..277eb3e 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -38,9 +38,9 @@
const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
- virtual status_t setDataSource(const sp<DataSource>& source);
+ virtual status_t setDataSource(const sp<DataSource>& source, const char *mime);
- virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
+ virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option, int colorFormat, bool metaOnly);
virtual MediaAlbumArt *extractAlbumArt();
virtual const char *extractMetadata(int keyCode);
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index d049df5..b9f48c4 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -94,7 +94,8 @@
// some OMX components as auto level, and by others as invalid level.
static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
int width, int height, int rate, int bitrate,
- OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
+ OMX_VIDEO_AVCPROFILEEXTTYPE profile =
+ (OMX_VIDEO_AVCPROFILEEXTTYPE)OMX_VIDEO_AVCProfileBaseline);
// Quirk still supported, even though deprecated
enum Quirks {
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index 270c809..7ac9b37 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -49,14 +49,17 @@
void *bits,
size_t width, size_t height,
size_t cropLeft, size_t cropTop,
- size_t cropRight, size_t cropBottom);
+ size_t cropRight, size_t cropBottom,
+ OMX_COLOR_FORMATTYPE colorFromat);
size_t cropWidth() const;
size_t cropHeight() const;
void *mBits;
+ OMX_COLOR_FORMATTYPE mColorFormat;
size_t mWidth, mHeight;
size_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+ size_t mBpp, mStride;
};
OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat;
@@ -82,6 +85,10 @@
status_t convertTIYUV420PackedSemiPlanar(
const BitmapParams &src, const BitmapParams &dst);
+ void writeToDst(void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
+ signed r1, signed g1, signed b1,
+ signed r2, signed g2, signed b2);
+
ColorConverter(const ColorConverter &);
ColorConverter &operator=(const ColorConverter &);
};
diff --git a/media/libstagefright/include/media/stagefright/DataSource.h b/media/libstagefright/include/media/stagefright/DataSource.h
index 63eccea..bd863ba 100644
--- a/media/libstagefright/include/media/stagefright/DataSource.h
+++ b/media/libstagefright/include/media/stagefright/DataSource.h
@@ -73,6 +73,11 @@
bool getUInt32(off64_t offset, uint32_t *x);
bool getUInt64(off64_t offset, uint64_t *x);
+ // read either int<N> or int<2N> into a uint<2N>_t, size is the int size in bytes.
+ bool getUInt16Var(off64_t offset, uint16_t *x, size_t size);
+ bool getUInt32Var(off64_t offset, uint32_t *x, size_t size);
+ bool getUInt64Var(off64_t offset, uint64_t *x, size_t size);
+
// Reads in "count" entries of type T into vector *x.
// Returns true if "count" entries can be read.
// If fewer than "count" entries can be read, return false. In this case,
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index dd357cc..1c7b4a6 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -204,7 +204,10 @@
void lock();
void unlock();
- void initInternal(int fd);
+ // Init all the internal variables for each recording session. Some variables
+ // will only need to be set for the first recording session and they will stay
+ // the same across all the recording sessions.
+ void initInternal(int fd, bool isFirstSession);
// Acquire lock before calling these methods
off64_t addSample_l(MediaBuffer *buffer);
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 9676b97..6cfde9c 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -38,6 +38,8 @@
kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation
kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width
kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height
+ kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width
+ kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height
// a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
kKeyCropRect = 'crop',
@@ -58,6 +60,7 @@
kKeyAACProfile = 'aacp', // int32_t
kKeyAVCC = 'avcc', // raw data
kKeyHVCC = 'hvcc', // raw data
+ kKeyThumbnailHVCC = 'thvc', // raw data
kKeyD263 = 'd263', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
@@ -209,6 +212,10 @@
// color Matrix, value defined by ColorAspects.MatrixCoeffs.
kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer)
kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded
+
+ kKeyGridWidth = 'grdW', // int32_t, HEIF grid width
+ kKeyGridHeight = 'grdH', // int32_t, HEIF grid height
+ kKeyIccProfile = 'prof', // raw data, ICC prifile data
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 88a416a..77cbd4c 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -95,7 +95,7 @@
void readFromAMessage(const sp<AMessage> &msg, BufferingSettings *buffering /* nonnull */);
AString nameForFd(int fd);
-
+void MakeFourCCString(uint32_t x, char *s);
} // namespace android
#endif // UTILS_H_
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index 35ba4dc..c4499dc 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -66,10 +66,9 @@
sp<Fence> fence;
::android::FrameEventHistoryDelta outTimestamps;
status_t status = mBase->dequeueBuffer(
- &slot, &fence,
- width, height,
- static_cast<::android::PixelFormat>(format), usage,
- getFrameTimestamps ? &outTimestamps : nullptr);
+ &slot, &fence, width, height,
+ static_cast<::android::PixelFormat>(format), usage, nullptr,
+ getFrameTimestamps ? &outTimestamps : nullptr);
hidl_handle tFence;
FrameEventHistoryDelta tOutTimestamps;
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 3027cdd..bb05740 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_omx",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"FrameDropper.cpp",
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index caf3ac8..109460d 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -733,7 +733,7 @@
} else {
// snap to nearest capture point
int64_t nFrames = std::llround(
- (timeUs - mPrevCaptureUs) * mCaptureFps);
+ (timeUs - mPrevCaptureUs) * mCaptureFps / 1000000);
if (nFrames <= 0) {
// skip this frame as it's too close to previous capture
ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
@@ -741,9 +741,9 @@
}
mFrameCount += nFrames;
mPrevCaptureUs = mBaseCaptureUs + std::llround(
- mFrameCount / mCaptureFps);
+ mFrameCount * 1000000 / mCaptureFps);
mPrevFrameUs = mBaseFrameUs + std::llround(
- mFrameCount / mFps);
+ mFrameCount * 1000000 / mFps);
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 09e6d75..87c2411 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -213,6 +213,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;
@@ -235,8 +242,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/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 24ed981..cb811a0 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -182,11 +182,11 @@
uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() {
- return mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
+ return max(mIsAdaptive ? mAdaptiveMaxWidth : 0, mWidth);
}
uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() {
- return mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
+ return max(mIsAdaptive ? mAdaptiveMaxHeight : 0, mHeight);
}
void SoftVideoDecoderOMXComponent::handlePortSettingsChange(
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index 8e22f9e..ab893de 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_xmlparser",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"MediaCodecsXmlParser.cpp",
diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp
index 1c04bcf..d2ede1c 100644
--- a/media/mtp/MtpDebug.cpp
+++ b/media/mtp/MtpDebug.cpp
@@ -102,6 +102,7 @@
{ "MTP_FORMAT_JP2", 0x380F },
{ "MTP_FORMAT_JPX", 0x3810 },
{ "MTP_FORMAT_DNG", 0x3811 },
+ { "MTP_FORMAT_HEIF", 0x3812 },
{ "MTP_FORMAT_UNDEFINED_FIRMWARE", 0xB802 },
{ "MTP_FORMAT_WINDOWS_IMAGE_FORMAT", 0xB881 },
{ "MTP_FORMAT_UNDEFINED_AUDIO", 0xB900 },
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index c50af2f..4132fed 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -719,9 +719,22 @@
}
int MtpFfsHandle::sendEvent(mtp_event me) {
+ // Mimic the behavior of f_mtp by sending the event async.
+ // Events aren't critical to the connection, so we don't need to check the return value.
+ char *temp = new char[me.length];
+ memcpy(temp, me.data, me.length);
+ me.data = temp;
+ std::thread t([this, me]() { return this->doSendEvent(me); });
+ t.detach();
+ return 0;
+}
+
+void MtpFfsHandle::doSendEvent(mtp_event me) {
unsigned length = me.length;
- int ret = writeHandle(mIntr, me.data, length);
- return static_cast<unsigned>(ret) == length ? 0 : -1;
+ int ret = ::write(mIntr, me.data, length);
+ if (static_cast<unsigned>(ret) != length)
+ PLOG(ERROR) << "Mtp error sending event thread!";
+ delete[] reinterpret_cast<char*>(me.data);
}
} // namespace android
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index 98669ff..b637d65 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -33,6 +33,7 @@
bool initFunctionfs();
void closeConfig();
void closeEndpoints();
+ void doSendEvent(mtp_event me);
bool mPtp;
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index adfb102..13cc859 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -94,6 +94,7 @@
#define MTP_FORMAT_JP2 0x380F // JPEG2000 Baseline File Format
#define MTP_FORMAT_JPX 0x3810 // JPEG2000 Extended File Format
#define MTP_FORMAT_DNG 0x3811 // Digital Negative
+#define MTP_FORMAT_HEIF 0x3812 // HEIF images
#define MTP_FORMAT_UNDEFINED_FIRMWARE 0xB802
#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881
#define MTP_FORMAT_UNDEFINED_AUDIO 0xB900
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 40974f3..0d48de1 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -90,3 +90,9 @@
},
},
}
+
+llndk_library {
+ name: "libmediandk",
+ symbol_file: "libmediandk.map.txt",
+ export_include_dirs: ["include"],
+}
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 5d1a20b..a450dd3 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -44,7 +44,11 @@
const char* AImageReader::kGraphicBufferKey = "GraphicBuffer";
bool
-AImageReader::isSupportedFormat(int32_t format) {
+AImageReader::isSupportedFormatAndUsage(int32_t format, uint64_t usage) {
+ // Check whether usage has either CPU_READ_OFTEN or CPU_READ set. Note that check against
+ // AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN (0x6) is sufficient as it implies
+ // AHARDWAREBUFFER_USAGE_CPU_READ (0x2).
+ bool hasCpuUsage = usage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
switch (format) {
case AIMAGE_FORMAT_RGBA_8888:
case AIMAGE_FORMAT_RGBX_8888:
@@ -60,6 +64,9 @@
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
return true;
+ case AIMAGE_FORMAT_PRIVATE:
+ // For private format, cpu usage is prohibited.
+ return !hasCpuUsage;
default:
return false;
}
@@ -83,6 +90,8 @@
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
return 1;
+ case AIMAGE_FORMAT_PRIVATE:
+ return 0;
default:
return -1;
}
@@ -606,9 +615,9 @@
return AMEDIA_ERROR_INVALID_PARAMETER;
}
- if (!AImageReader::isSupportedFormat(format)) {
- ALOGE("%s: format %d is not supported by AImageReader",
- __FUNCTION__, format);
+ if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
+ ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
+ __FUNCTION__, format, usage);
return AMEDIA_ERROR_INVALID_PARAMETER;
}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 989c1fd..989b937 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -49,7 +49,7 @@
struct AImageReader : public RefBase {
public:
- static bool isSupportedFormat(int32_t format);
+ static bool isSupportedFormatAndUsage(int32_t format, uint64_t usage0);
static int getNumPlanesForFormat(int32_t format);
AImageReader(int32_t width,
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index d7443be..1931496 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -495,7 +495,13 @@
/**
* Android private opaque image format.
*
- * <p>This format is not currently supported by {@link AImageReader}.</p>
+ * <p>The choices of the actual format and pixel data layout are entirely up to the
+ * device-specific and framework internal implementations, and may vary depending on use cases
+ * even for the same device. Also note that the contents of these buffers are not directly
+ * accessible to the application.</p>
+ *
+ * <p>When an {@link AImage} of this format is obtained from an {@link AImageReader} or
+ * {@link AImage_getNumberOfPlanes()} method will return zero.</p>
*/
AIMAGE_FORMAT_PRIVATE = 0x22
};
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index 59ae507..7a0c17b 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -70,7 +70,9 @@
* @param height The default height in pixels of the Images that this reader will produce.
* @param format The format of the Image that this reader will produce. This must be one of the
* AIMAGE_FORMAT_* enum value defined in {@link AIMAGE_FORMATS}. Note that not all
- * formats are supported, like {@link AIMAGE_FORMAT_PRIVATE}.
+ * formats are supported. One example is {@link AIMAGE_FORMAT_PRIVATE}, as it is not
+ * intended to be read by applications directly. That format is supported by
+ * {@link AImageReader_newWithUsage} introduced in API 26.
* @param maxImages The maximum number of images the user will want to access simultaneously. This
* should be as small as possible to limit memory use. Once maxImages Images are obtained
* by the user, one of them has to be released before a new {@link AImage} will become
@@ -307,6 +309,28 @@
* for the consumer usage. All other parameters and the return values are identical to those passed
* to {@line AImageReader_new}.
*
+ * <p>If the {@code format} is {@link AIMAGE_FORMAT_PRIVATE}, the created {@link AImageReader}
+ * will produce images whose contents are not directly accessible by the application. The application can
+ * still acquire images from this {@link AImageReader} and access {@link AHardwareBuffer} via
+ * {@link AImage_getHardwareBuffer()}. The {@link AHardwareBuffer} gained this way can then
+ * be passed back to hardware (such as GPU or hardware encoder if supported) for future processing.
+ * For example, you can obtain an {@link EGLClientBuffer} from the {@link AHardwareBuffer} by using
+ * {@link eglGetNativeClientBufferANDROID} extension and pass that {@link EGLClientBuffer} to {@link
+ * eglCreateImageKHR} to create an {@link EGLImage} resource type, which may then be bound to a
+ * texture via {@link glEGLImageTargetTexture2DOES} on supported devices. This can be useful for
+ * transporting textures that may be shared cross-process.</p>
+ * <p>In general, when software access to image data is not necessary, an {@link AImageReader}
+ * created with {@link AIMAGE_FORMAT_PRIVATE} format is more efficient, compared with {@link
+ * AImageReader}s using other format such as {@link AIMAGE_FORMAT_YUV_420_888}.</p>
+ *
+ * <p>Note that not all format and usage flag combination is supported by the {@link AImageReader},
+ * especially if {@code format} is {@link AIMAGE_FORMAT_PRIVATE}, {@code usage} must not include either
+ * {@link AHARDWAREBUFFER_USAGE_READ_RARELY} or {@link AHARDWAREBUFFER_USAGE_READ_OFTEN}</p>
+ *
+ * @param width The default width in pixels of the Images that this reader will produce.
+ * @param height The default height in pixels of the Images that this reader will produce.
+ * @param format The format of the Image that this reader will produce. This must be one of the
+ * AIMAGE_FORMAT_* enum value defined in {@link AIMAGE_FORMATS}.
* @param usage specifies how the consumer will access the AImage, using combination of the
* AHARDWAREBUFFER_USAGE flags described in {@link hardware_buffer.h}.
* Passing {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN} is equivalent to calling
@@ -331,6 +355,11 @@
* {@link AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE}, or combined</td>
* </tr>
* </table>
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL, or one or more of width,
+ * height, format, maxImages, or usage arguments is not supported.</li>
+ * <li>{@link AMEDIA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
*
* @see AImage
* @see AImageReader_new
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0df9a39..13522f5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -326,7 +326,7 @@
sp<MmapThread> thread = mMmapThreads.valueFor(io);
if (thread != 0) {
interface = new MmapThreadHandle(thread);
- thread->configure(attr, streamType, sessionId, callback, portId);
+ thread->configure(attr, streamType, sessionId, callback, *deviceId, portId);
*handle = portId;
} else {
ret = NO_INIT;
@@ -2603,7 +2603,7 @@
while (ec->mEffects.size()) {
sp<EffectModule> effect = ec->mEffects[0];
effect->unPin();
- t->removeEffect_l(effect);
+ t->removeEffect_l(effect, /*release*/ true);
if (effect->purgeHandles()) {
t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9023b2d..63898a0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -604,7 +604,7 @@
virtual status_t standby();
private:
- sp<MmapThread> mThread;
+ const sp<MmapThread> mThread;
};
ThreadBase *checkThread_l(audio_io_handle_t ioHandle) const;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f2c1c4f..bd5f146 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
+#include <media/AudioEffect.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -109,7 +110,10 @@
{
ALOGV("Destructor %p", this);
if (mEffectInterface != 0) {
- ALOGW("EffectModule %p destructor called with unreleased interface", this);
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+ this, uuidStr);
release_l();
}
@@ -1081,18 +1085,12 @@
result.append(buffer);
result.append("\t\tDescriptor:\n");
- snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
- mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
- mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],
- mDescriptor.uuid.node[2],
- mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ snprintf(buffer, SIZE, "\t\t- UUID: %s\n", uuidStr);
result.append(buffer);
- snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
- mDescriptor.type.timeLow, mDescriptor.type.timeMid,
- mDescriptor.type.timeHiAndVersion,
- mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],
- mDescriptor.type.node[2],
- mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+ AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+ snprintf(buffer, SIZE, "\t\t- TYPE: %s\n", uuidStr);
result.append(buffer);
snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
mDescriptor.apiVersion,
@@ -1306,11 +1304,10 @@
if (thread != 0) {
thread->disconnectEffectHandle(this, unpinIfLast);
} else {
- ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
// try to cleanup as much as we can
sp<EffectModule> effect = mEffect.promote();
- if (effect != 0) {
- effect->disconnectHandle(this, unpinIfLast);
+ if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) {
+ ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
}
}
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 3c73543..c1044ef 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -113,10 +113,15 @@
return ok;
}
-bool captureHotwordAllowed() {
- static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
- // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
- bool ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed);
+bool captureHotwordAllowed(pid_t pid, uid_t uid) {
+ // CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission
+ bool ok = recordingAllowed(String16(""), pid, uid);
+
+ if (ok) {
+ static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+ ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed);
+ }
if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
return ok;
}
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 8b1bc00..04cb9cd 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -22,7 +22,7 @@
bool isTrustedCallingUid(uid_t uid);
bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
-bool captureHotwordAllowed();
+bool captureHotwordAllowed(pid_t pid, uid_t uid);
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 459e4fb..297608c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7503,34 +7503,22 @@
AudioFlinger::MmapThreadHandle::MmapThreadHandle(const sp<MmapThread>& thread)
: mThread(thread)
{
+ assert(thread != 0); // thread must start non-null and stay non-null
}
AudioFlinger::MmapThreadHandle::~MmapThreadHandle()
{
- MmapThread *thread = mThread.get();
- // clear our strong reference before disconnecting the thread: the last strong reference
- // will be removed when closeInput/closeOutput is executed upon call from audio policy manager
- // and the thread removed from mMMapThreads list causing the thread destruction.
- mThread.clear();
- if (thread != nullptr) {
- thread->disconnect();
- }
+ mThread->disconnect();
}
status_t AudioFlinger::MmapThreadHandle::createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info)
{
- if (mThread == 0) {
- return NO_INIT;
- }
return mThread->createMmapBuffer(minSizeFrames, info);
}
status_t AudioFlinger::MmapThreadHandle::getMmapPosition(struct audio_mmap_position *position)
{
- if (mThread == 0) {
- return NO_INIT;
- }
return mThread->getMmapPosition(position);
}
@@ -7538,25 +7526,16 @@
audio_port_handle_t *handle)
{
- if (mThread == 0) {
- return NO_INIT;
- }
return mThread->start(client, handle);
}
status_t AudioFlinger::MmapThreadHandle::stop(audio_port_handle_t handle)
{
- if (mThread == 0) {
- return NO_INIT;
- }
return mThread->stop(handle);
}
status_t AudioFlinger::MmapThreadHandle::standby()
{
- if (mThread == 0) {
- return NO_INIT;
- }
return mThread->standby();
}
@@ -7566,6 +7545,8 @@
AudioHwDevice *hwDev, sp<StreamHalInterface> stream,
audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady)
: ThreadBase(audioFlinger, id, outDevice, inDevice, MMAP, systemReady),
+ mSessionId(AUDIO_SESSION_NONE),
+ mDeviceId(AUDIO_PORT_HANDLE_NONE), mPortId(AUDIO_PORT_HANDLE_NONE),
mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev),
mActiveTracks(&this->mLocalLog)
{
@@ -7588,7 +7569,7 @@
for (const sp<MmapTrack> &t : mActiveTracks) {
stop(t->portId());
}
- // this will cause the destruction of this thread.
+ // This will decrement references and may cause the destruction of this thread.
if (isOutput()) {
AudioSystem::releaseOutput(mId, streamType(), mSessionId);
} else {
@@ -7601,11 +7582,13 @@
audio_stream_type_t streamType __unused,
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
+ audio_port_handle_t deviceId,
audio_port_handle_t portId)
{
mAttr = *attr;
mSessionId = sessionId;
mCallback = callback;
+ mDeviceId = deviceId;
mPortId = portId;
}
@@ -7650,6 +7633,10 @@
return NO_ERROR;
}
+ if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
+ return PERMISSION_DENIED;
+ }
+
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io = mId;
@@ -7661,7 +7648,7 @@
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;
+ audio_port_handle_t deviceId = mDeviceId;
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
@@ -7675,7 +7662,7 @@
config.sample_rate = mSampleRate;
config.channel_mask = mChannelMask;
config.format = mFormat;
- audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_port_handle_t deviceId = mDeviceId;
ret = AudioSystem::getInputForAttr(&mAttr, &io,
mSessionId,
client.clientPid,
@@ -8012,17 +7999,19 @@
mPrevOutDevice = type;
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
sp<MmapStreamCallback> callback = mCallback.promote();
- if (callback != 0) {
+ if (mDeviceId != deviceId && callback != 0) {
callback->onRoutingChanged(deviceId);
}
+ mDeviceId = deviceId;
}
if (!isOutput() && mPrevInDevice != mInDevice) {
mPrevInDevice = type;
sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
sp<MmapStreamCallback> callback = mCallback.promote();
- if (callback != 0) {
+ if (mDeviceId != deviceId && callback != 0) {
callback->onRoutingChanged(deviceId);
}
+ mDeviceId = deviceId;
}
return status;
}
@@ -8263,9 +8252,10 @@
audio_stream_type_t streamType,
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
+ audio_port_handle_t deviceId,
audio_port_handle_t portId)
{
- MmapThread::configure(attr, streamType, sessionId, callback, portId);
+ MmapThread::configure(attr, streamType, sessionId, callback, deviceId, portId);
mStreamType = streamType;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 32f2bc4..8d7bb1a 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1479,6 +1479,7 @@
audio_stream_type_t streamType,
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
+ audio_port_handle_t deviceId,
audio_port_handle_t portId);
void disconnect();
@@ -1540,6 +1541,7 @@
audio_attributes_t mAttr;
audio_session_t mSessionId;
+ audio_port_handle_t mDeviceId;
audio_port_handle_t mPortId;
wp<MmapStreamCallback> mCallback;
@@ -1562,6 +1564,7 @@
audio_stream_type_t streamType,
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
+ audio_port_handle_t deviceId,
audio_port_handle_t portId);
AudioStreamOut* clearOutput();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 1a7db26..e6ee6c9 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
// ----------------------------------------------------------------------------
@@ -1315,9 +1320,9 @@
// apply volume rules for current stream and device if necessary
checkAndSetVolume(stream,
- mVolumeCurves->getVolumeIndex(stream, device),
+ mVolumeCurves->getVolumeIndex(stream, outputDesc->device()),
outputDesc,
- device);
+ outputDesc->device());
// update the outputs if starting an output with a stream that can affect notification
// routing
@@ -4716,10 +4721,10 @@
// use device for strategy enforced audible
// 2: we are in call or the strategy phone is active on the output:
// use device for strategy phone
- // 3: the strategy for enforced audible is active but not enforced on the output:
- // use the device for strategy enforced audible
- // 4: the strategy sonification is active on the output:
+ // 3: the strategy sonification is active on the output:
// use device for strategy sonification
+ // 4: the strategy for enforced audible is active but not enforced on the output:
+ // use the device for strategy enforced audible
// 5: the strategy accessibility is active on the output:
// use device for strategy accessibility
// 6: the strategy "respectful" sonification is active on the output:
@@ -4736,10 +4741,10 @@
} else if (isInCall() ||
isStrategyActive(outputDesc, STRATEGY_PHONE)) {
device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
- } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
- device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
} else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION)) {
device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+ } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
+ device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
} else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) {
device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache);
} else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) {
@@ -5348,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/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7d7cd93..1d4386c 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -289,13 +289,6 @@
return BAD_VALUE;
}
- if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
- return BAD_VALUE;
- }
- sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status;
- AudioPolicyInterface::input_type_t inputType;
-
bool updatePid = (pid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
if (!isTrustedCallingUid(callingUid)) {
@@ -313,7 +306,15 @@
pid = callingPid;
}
+ if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
+ return BAD_VALUE;
+ }
+
+ sp<AudioPolicyEffects>audioPolicyEffects;
{
+ status_t status;
+ AudioPolicyInterface::input_type_t inputType;
+
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 0429e7f..6fd9263 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -313,11 +313,13 @@
binder::Status CameraDeviceClient::beginConfigure() {
// TODO: Implement this.
+ ATRACE_CALL();
ALOGV("%s: Not implemented yet.", __FUNCTION__);
return binder::Status::ok();
}
binder::Status CameraDeviceClient::endConfigure(int operatingMode) {
+ ATRACE_CALL();
ALOGV("%s: ending configure (%d input stream, %zu output surfaces)",
__FUNCTION__, mInputStream.configured ? 1 : 0,
mStreamMap.size());
@@ -568,7 +570,7 @@
/*out*/
int* newStreamId) {
int width, height, format, surfaceType;
- int32_t consumerUsage;
+ uint64_t consumerUsage;
android_dataspace dataSpace;
status_t err;
binder::Status res;
@@ -764,24 +766,23 @@
// Query consumer usage bits to set async operation mode for
// GLConsumer using controlledByApp parameter.
bool useAsync = false;
- int32_t consumerUsage;
+ uint64_t consumerUsage = 0;
status_t err;
- if ((err = gbp->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS,
- &consumerUsage)) != OK) {
+ if ((err = gbp->getConsumerUsage(&consumerUsage)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface consumer usage: %s (%d)",
mCameraIdStr.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
- ALOGW("%s: Camera %s with consumer usage flag: 0x%x: Forcing asynchronous mode for stream",
+ ALOGW("%s: Camera %s with consumer usage flag: %" PRIu64 ": Forcing asynchronous mode for stream",
__FUNCTION__, mCameraIdStr.string(), consumerUsage);
useAsync = true;
}
- int32_t disallowedFlags = GraphicBuffer::USAGE_HW_VIDEO_ENCODER |
+ uint64_t disallowedFlags = GraphicBuffer::USAGE_HW_VIDEO_ENCODER |
GRALLOC_USAGE_RENDERSCRIPT;
- int32_t allowedFlags = GraphicBuffer::USAGE_SW_READ_MASK |
+ uint64_t allowedFlags = GraphicBuffer::USAGE_SW_READ_MASK |
GraphicBuffer::USAGE_HW_TEXTURE |
GraphicBuffer::USAGE_HW_COMPOSER;
bool flexibleConsumer = (consumerUsage & disallowedFlags) == 0 &&
@@ -874,7 +875,7 @@
//surface class type. Use usage flag to approximate the comparison.
if (consumerUsage != streamInfo.consumerUsage) {
String8 msg = String8::format(
- "Camera %s:Surface usage flag doesn't match 0x%x vs 0x%x",
+ "Camera %s:Surface usage flag doesn't match %" PRIu64 " vs %" PRIu64 "",
mCameraIdStr.string(), consumerUsage, streamInfo.consumerUsage);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index e8fc080..50661cb 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -213,13 +213,13 @@
int height;
int format;
android_dataspace dataSpace;
- int32_t consumerUsage;
+ uint64_t consumerUsage;
bool finalized = false;
OutputStreamInfo() :
width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
consumerUsage(0) {}
OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
- int32_t _consumerUsage) :
+ uint64_t _consumerUsage) :
width(_width), height(_height), format(_format),
dataSpace(_dataSpace), consumerUsage(_consumerUsage) {}
};
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index d9059f3..54fcb0a 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -119,7 +119,7 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint32_t consumerUsage = 0) = 0;
+ bool isShared = false, uint64_t consumerUsage = 0) = 0;
/**
* Create an output stream of the requested size, format, rotation and
@@ -132,7 +132,7 @@
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint32_t consumerUsage = 0) = 0;
+ bool isShared = false, uint64_t consumerUsage = 0) = 0;
/**
* Create an input stream of width, height, and format.
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 469c86c..991b50f 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -29,11 +29,6 @@
CameraHardwareInterface::~CameraHardwareInterface()
{
ALOGI("Destroying camera %s", mName.string());
- if (mDevice) {
- int rc = mDevice->common.close(&mDevice->common);
- if (rc != OK)
- ALOGE("Could not close camera %s: %d", mName.string(), rc);
- }
if (mHidlDevice != nullptr) {
mHidlDevice->close();
mHidlDevice.clear();
@@ -42,12 +37,6 @@
}
status_t CameraHardwareInterface::initialize(sp<CameraProviderManager> manager) {
- if (mDevice) {
- ALOGE("%s: camera hardware interface has been initialized to libhardware path!",
- __FUNCTION__);
- return INVALID_OPERATION;
- }
-
ALOGI("Opening camera %s", mName.string());
status_t ret = manager->openSession(mName.string(), this, &mHidlDevice);
@@ -372,7 +361,7 @@
ALOGE("%s: preview window is null", __FUNCTION__);
return s;
}
- mPreviewUsage = (int) usage;
+ mPreviewUsage = static_cast<uint64_t> (usage);
int rc = native_window_set_usage(a, mPreviewUsage);
if (rc == OK) {
cleanupCirculatingBuffers();
@@ -444,23 +433,6 @@
}
return CameraProviderManager::mapToStatusT(
mHidlDevice->setPreviewWindow(buf.get() ? this : nullptr));
- } else if (mDevice) {
- if (mDevice->ops->set_preview_window) {
- mPreviewWindow = buf;
- if (buf != nullptr) {
- if (mPreviewScalingMode != NOT_SET) {
- setPreviewScalingMode(mPreviewScalingMode);
- }
- if (mPreviewTransform != NOT_SET) {
- setPreviewTransform(mPreviewTransform);
- }
- }
- mHalPreviewWindow.user = this;
- ALOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p",__FUNCTION__,
- &mHalPreviewWindow, mHalPreviewWindow.user);
- return mDevice->ops->set_preview_window(mDevice,
- buf.get() ? &mHalPreviewWindow.nw : 0);
- }
}
return INVALID_OPERATION;
}
@@ -478,15 +450,6 @@
mCbUser = user;
ALOGV("%s(%s)", __FUNCTION__, mName.string());
-
- if (mDevice && mDevice->ops->set_callbacks) {
- mDevice->ops->set_callbacks(mDevice,
- sNotifyCb,
- sDataCb,
- sDataCbTimestamp,
- sGetMemory,
- this);
- }
}
void CameraHardwareInterface::enableMsgType(int32_t msgType)
@@ -494,8 +457,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
mHidlDevice->enableMsgType(msgType);
- } else if (mDevice && mDevice->ops->enable_msg_type) {
- mDevice->ops->enable_msg_type(mDevice, msgType);
}
}
@@ -504,8 +465,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
mHidlDevice->disableMsgType(msgType);
- } else if (mDevice && mDevice->ops->disable_msg_type) {
- mDevice->ops->disable_msg_type(mDevice, msgType);
}
}
@@ -514,8 +473,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
return mHidlDevice->msgTypeEnabled(msgType);
- } else if (mDevice && mDevice->ops->msg_type_enabled) {
- return mDevice->ops->msg_type_enabled(mDevice, msgType);
}
return false;
}
@@ -526,8 +483,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->startPreview());
- } else if (mDevice && mDevice->ops->start_preview) {
- return mDevice->ops->start_preview(mDevice);
}
return INVALID_OPERATION;
}
@@ -537,8 +492,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
mHidlDevice->stopPreview();
- } else if (mDevice && mDevice->ops->stop_preview) {
- mDevice->ops->stop_preview(mDevice);
}
}
@@ -547,8 +500,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
return mHidlDevice->previewEnabled();
- } else if (mDevice && mDevice->ops->preview_enabled) {
- return mDevice->ops->preview_enabled(mDevice);
}
return false;
}
@@ -559,8 +510,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->storeMetaDataInBuffers(enable));
- } else if (mDevice && mDevice->ops->store_meta_data_in_buffers) {
- return mDevice->ops->store_meta_data_in_buffers(mDevice, enable);
}
return enable ? INVALID_OPERATION: OK;
}
@@ -571,8 +520,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->startRecording());
- } else if (mDevice && mDevice->ops->start_recording) {
- return mDevice->ops->start_recording(mDevice);
}
return INVALID_OPERATION;
}
@@ -585,8 +532,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
mHidlDevice->stopRecording();
- } else if (mDevice && mDevice->ops->stop_recording) {
- mDevice->ops->stop_recording(mDevice);
}
}
@@ -598,8 +543,6 @@
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
return mHidlDevice->recordingEnabled();
- } else if (mDevice && mDevice->ops->recording_enabled) {
- return mDevice->ops->recording_enabled(mDevice);
}
return false;
}
@@ -624,9 +567,6 @@
} else {
mHidlDevice->releaseRecordingFrame(heapId, bufferIndex);
}
- } else if (mDevice && mDevice->ops->release_recording_frame) {
- void *data = ((uint8_t *)heap->base()) + offset;
- return mDevice->ops->release_recording_frame(mDevice, data);
}
}
@@ -653,9 +593,6 @@
ALOGE("%s only supports VideoNativeHandleMetadata mode", __FUNCTION__);
return;
}
- } else {
- ALOGE("Non HIDL mode do not support %s", __FUNCTION__);
- return;
}
}
@@ -674,8 +611,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->autoFocus());
- } else if (mDevice && mDevice->ops->auto_focus) {
- return mDevice->ops->auto_focus(mDevice);
}
return INVALID_OPERATION;
}
@@ -686,8 +621,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->cancelAutoFocus());
- } else if (mDevice && mDevice->ops->cancel_auto_focus) {
- return mDevice->ops->cancel_auto_focus(mDevice);
}
return INVALID_OPERATION;
}
@@ -698,8 +631,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->takePicture());
- } else if (mDevice && mDevice->ops->take_picture) {
- return mDevice->ops->take_picture(mDevice);
}
return INVALID_OPERATION;
}
@@ -710,8 +641,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->cancelPicture());
- } else if (mDevice && mDevice->ops->cancel_picture) {
- return mDevice->ops->cancel_picture(mDevice);
}
return INVALID_OPERATION;
}
@@ -722,8 +651,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->setParameters(params.flatten().string()));
- } else if (mDevice && mDevice->ops->set_parameters) {
- return mDevice->ops->set_parameters(mDevice, params.flatten().string());
}
return INVALID_OPERATION;
}
@@ -740,14 +667,6 @@
});
String8 tmp(outParam.c_str());
parms.unflatten(tmp);
- } else if (mDevice && mDevice->ops->get_parameters) {
- char *temp = mDevice->ops->get_parameters(mDevice);
- String8 str_parms(temp);
- if (mDevice->ops->put_parameters)
- mDevice->ops->put_parameters(mDevice, temp);
- else
- free(temp);
- parms.unflatten(str_parms);
}
return parms;
}
@@ -758,8 +677,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->sendCommand((CommandType) cmd, arg1, arg2));
- } else if (mDevice && mDevice->ops->send_command) {
- return mDevice->ops->send_command(mDevice, cmd, arg1, arg2);
}
return INVALID_OPERATION;
}
@@ -773,8 +690,6 @@
if (CC_LIKELY(mHidlDevice != nullptr)) {
mHidlDevice->close();
mHidlDevice.clear();
- } else if (mDevice && mDevice->ops->release) {
- mDevice->ops->release(mDevice);
}
}
@@ -790,15 +705,10 @@
Status s = mHidlDevice->dumpState(handle);
native_handle_delete(handle);
return CameraProviderManager::mapToStatusT(s);
- } else if (mDevice && mDevice->ops->dump) {
- return mDevice->ops->dump(mDevice, fd);
}
return OK; // It's fine if the HAL doesn't implement dump()
}
-/**
- * Methods for legacy (non-HIDL) path follows
- */
void CameraHardwareInterface::sNotifyCb(int32_t msg_type, int32_t ext1,
int32_t ext2, void *user)
{
@@ -868,177 +778,4 @@
mem->decStrong(mem);
}
-ANativeWindow* CameraHardwareInterface::sToAnw(void *user)
-{
- CameraHardwareInterface *object =
- reinterpret_cast<CameraHardwareInterface *>(user);
- return object->mPreviewWindow.get();
-}
-#define anw(n) sToAnw(((struct camera_preview_window *)(n))->user)
-#define hwi(n) reinterpret_cast<CameraHardwareInterface *>(\
- ((struct camera_preview_window *)(n))->user)
-
-int CameraHardwareInterface::sDequeueBuffer(struct preview_stream_ops* w,
- buffer_handle_t** buffer, int *stride)
-{
- int rc;
- ANativeWindow *a = anw(w);
- ANativeWindowBuffer* anb;
- rc = native_window_dequeue_buffer_and_wait(a, &anb);
- if (rc == OK) {
- *buffer = &anb->handle;
- *stride = anb->stride;
- }
- return rc;
-}
-
-#ifndef container_of
-#define container_of(ptr, type, member) ({ \
- const __typeof__(((type *) 0)->member) *__mptr = (ptr); \
- (type *) ((char *) __mptr - (char *)(&((type *)0)->member)); })
-#endif
-
-int CameraHardwareInterface::sLockBuffer(struct preview_stream_ops* w,
- buffer_handle_t* /*buffer*/)
-{
- ANativeWindow *a = anw(w);
- (void)a;
- return 0;
-}
-
-int CameraHardwareInterface::sEnqueueBuffer(struct preview_stream_ops* w,
- buffer_handle_t* buffer)
-{
- ANativeWindow *a = anw(w);
- return a->queueBuffer(a,
- container_of(buffer, ANativeWindowBuffer, handle), -1);
-}
-
-int CameraHardwareInterface::sCancelBuffer(struct preview_stream_ops* w,
- buffer_handle_t* buffer)
-{
- ANativeWindow *a = anw(w);
- return a->cancelBuffer(a,
- container_of(buffer, ANativeWindowBuffer, handle), -1);
-}
-
-int CameraHardwareInterface::sSetBufferCount(struct preview_stream_ops* w, int count)
-{
- ANativeWindow *a = anw(w);
-
- if (a != nullptr) {
- // Workaround for b/27039775
- // Previously, setting the buffer count would reset the buffer
- // queue's flag that allows for all buffers to be dequeued on the
- // producer side, instead of just the producer's declared max count,
- // if no filled buffers have yet been queued by the producer. This
- // reset no longer happens, but some HALs depend on this behavior,
- // so it needs to be maintained for HAL backwards compatibility.
- // Simulate the prior behavior by disconnecting/reconnecting to the
- // window and setting the values again. This has the drawback of
- // actually causing memory reallocation, which may not have happened
- // in the past.
- CameraHardwareInterface *hw = hwi(w);
- native_window_api_disconnect(a, NATIVE_WINDOW_API_CAMERA);
- native_window_api_connect(a, NATIVE_WINDOW_API_CAMERA);
- if (hw->mPreviewScalingMode != NOT_SET) {
- native_window_set_scaling_mode(a, hw->mPreviewScalingMode);
- }
- if (hw->mPreviewTransform != NOT_SET) {
- native_window_set_buffers_transform(a, hw->mPreviewTransform);
- }
- if (hw->mPreviewWidth != NOT_SET) {
- native_window_set_buffers_dimensions(a,
- hw->mPreviewWidth, hw->mPreviewHeight);
- native_window_set_buffers_format(a, hw->mPreviewFormat);
- }
- if (hw->mPreviewUsage != 0) {
- native_window_set_usage(a, hw->mPreviewUsage);
- }
- if (hw->mPreviewSwapInterval != NOT_SET) {
- a->setSwapInterval(a, hw->mPreviewSwapInterval);
- }
- if (hw->mPreviewCrop.left != NOT_SET) {
- native_window_set_crop(a, &(hw->mPreviewCrop));
- }
- }
-
- return native_window_set_buffer_count(a, count);
-}
-
-int CameraHardwareInterface::sSetBuffersGeometry(struct preview_stream_ops* w,
- int width, int height, int format)
-{
- int rc;
- ANativeWindow *a = anw(w);
- CameraHardwareInterface *hw = hwi(w);
- hw->mPreviewWidth = width;
- hw->mPreviewHeight = height;
- hw->mPreviewFormat = format;
- rc = native_window_set_buffers_dimensions(a, width, height);
- if (rc == OK) {
- rc = native_window_set_buffers_format(a, format);
- }
- return rc;
-}
-
-int CameraHardwareInterface::sSetCrop(struct preview_stream_ops *w,
- int left, int top, int right, int bottom)
-{
- ANativeWindow *a = anw(w);
- CameraHardwareInterface *hw = hwi(w);
- hw->mPreviewCrop.left = left;
- hw->mPreviewCrop.top = top;
- hw->mPreviewCrop.right = right;
- hw->mPreviewCrop.bottom = bottom;
- return native_window_set_crop(a, &(hw->mPreviewCrop));
-}
-
-int CameraHardwareInterface::sSetTimestamp(struct preview_stream_ops *w,
- int64_t timestamp) {
- ANativeWindow *a = anw(w);
- return native_window_set_buffers_timestamp(a, timestamp);
-}
-
-int CameraHardwareInterface::sSetUsage(struct preview_stream_ops* w, int usage)
-{
- ANativeWindow *a = anw(w);
- CameraHardwareInterface *hw = hwi(w);
- hw->mPreviewUsage = usage;
- return native_window_set_usage(a, usage);
-}
-
-int CameraHardwareInterface::sSetSwapInterval(struct preview_stream_ops *w, int interval)
-{
- ANativeWindow *a = anw(w);
- CameraHardwareInterface *hw = hwi(w);
- hw->mPreviewSwapInterval = interval;
- return a->setSwapInterval(a, interval);
-}
-
-int CameraHardwareInterface::sGetMinUndequeuedBufferCount(
- const struct preview_stream_ops *w,
- int *count)
-{
- ANativeWindow *a = anw(w);
- return a->query(a, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, count);
-}
-
-void CameraHardwareInterface::initHalPreviewWindow()
-{
- mHalPreviewWindow.nw.cancel_buffer = sCancelBuffer;
- mHalPreviewWindow.nw.lock_buffer = sLockBuffer;
- mHalPreviewWindow.nw.dequeue_buffer = sDequeueBuffer;
- mHalPreviewWindow.nw.enqueue_buffer = sEnqueueBuffer;
- mHalPreviewWindow.nw.set_buffer_count = sSetBufferCount;
- mHalPreviewWindow.nw.set_buffers_geometry = sSetBuffersGeometry;
- mHalPreviewWindow.nw.set_crop = sSetCrop;
- mHalPreviewWindow.nw.set_timestamp = sSetTimestamp;
- mHalPreviewWindow.nw.set_usage = sSetUsage;
- mHalPreviewWindow.nw.set_swap_interval = sSetSwapInterval;
-
- mHalPreviewWindow.nw.get_min_undequeued_buffer_count =
- sGetMinUndequeuedBufferCount;
-}
-
}; // namespace android
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 1c38d00..6a1b4fb 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -90,7 +90,6 @@
public:
explicit CameraHardwareInterface(const char *name):
- mDevice(nullptr),
mHidlDevice(nullptr),
mName(name),
mPreviewScalingMode(NOT_SET),
@@ -299,7 +298,6 @@
status_t dump(int fd, const Vector<String16>& /*args*/) const;
private:
- camera_device_t *mDevice;
sp<hardware::camera::device::V1_0::ICameraDevice> mHidlDevice;
String8 mName;
@@ -369,41 +367,6 @@
static void sPutMemory(camera_memory_t *data);
- static ANativeWindow *sToAnw(void *user);
-
- static int sDequeueBuffer(struct preview_stream_ops* w,
- buffer_handle_t** buffer, int *stride);
-
- static int sLockBuffer(struct preview_stream_ops* w,
- buffer_handle_t* /*buffer*/);
-
- static int sEnqueueBuffer(struct preview_stream_ops* w,
- buffer_handle_t* buffer);
-
- static int sCancelBuffer(struct preview_stream_ops* w,
- buffer_handle_t* buffer);
-
- static int sSetBufferCount(struct preview_stream_ops* w, int count);
-
- static int sSetBuffersGeometry(struct preview_stream_ops* w,
- int width, int height, int format);
-
- static int sSetCrop(struct preview_stream_ops *w,
- int left, int top, int right, int bottom);
-
- static int sSetTimestamp(struct preview_stream_ops *w,
- int64_t timestamp);
-
- static int sSetUsage(struct preview_stream_ops* w, int usage);
-
- static int sSetSwapInterval(struct preview_stream_ops *w, int interval);
-
- static int sGetMinUndequeuedBufferCount(
- const struct preview_stream_ops *w,
- int *count);
-
- void initHalPreviewWindow();
-
std::pair<bool, uint64_t> getBufferId(ANativeWindowBuffer* anb);
void cleanupCirculatingBuffers();
@@ -459,13 +422,6 @@
sp<ANativeWindow> mPreviewWindow;
- struct camera_preview_window {
- struct preview_stream_ops nw;
- void *user;
- };
-
- struct camera_preview_window mHalPreviewWindow;
-
notify_callback mNotifyCb;
data_callback mDataCb;
data_callback_timestamp mDataCbTimestamp;
@@ -479,7 +435,7 @@
int mPreviewWidth;
int mPreviewHeight;
int mPreviewFormat;
- int mPreviewUsage;
+ uint64_t mPreviewUsage;
int mPreviewSwapInterval;
android_native_rect_t mPreviewCrop;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 69b1d7d..94e8f3b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -407,7 +407,7 @@
}
BufferUsageFlags Camera3Device::mapToConsumerUsage(
- uint32_t usage) {
+ uint64_t usage) {
return usage;
}
@@ -460,12 +460,12 @@
return static_cast<uint32_t>(pixelFormat);
}
-uint32_t Camera3Device::mapConsumerToFrameworkUsage(
+uint64_t Camera3Device::mapConsumerToFrameworkUsage(
BufferUsageFlags usage) {
return usage;
}
-uint32_t Camera3Device::mapProducerToFrameworkUsage(
+uint64_t Camera3Device::mapProducerToFrameworkUsage(
BufferUsageFlags usage) {
return usage;
}
@@ -1208,7 +1208,7 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId, bool isShared, uint32_t consumerUsage) {
+ int streamSetId, bool isShared, uint64_t consumerUsage) {
ATRACE_CALL();
if (consumer == nullptr) {
@@ -1226,13 +1226,13 @@
status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId, bool isShared, uint32_t consumerUsage) {
+ int streamSetId, bool isShared, uint64_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,
+ " consumer usage %" PRIu64 ", isShared %d", mId.string(), mNextStreamId, width, height, format,
dataSpace, rotation, consumerUsage, isShared);
status_t res;
@@ -1478,6 +1478,7 @@
status_t Camera3Device::getInputBufferProducer(
sp<IGraphicBufferProducer> *producer) {
+ ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -1691,6 +1692,7 @@
}
status_t Camera3Device::waitForNextFrame(nsecs_t timeout) {
+ ATRACE_CALL();
status_t res;
Mutex::Autolock l(mOutputLock);
@@ -1884,6 +1886,7 @@
*/
void Camera3Device::notifyStatus(bool idle) {
+ ATRACE_CALL();
{
// Need mLock to safely update state and synchronize to current
// state of methods in flight.
@@ -2317,6 +2320,7 @@
}
void Camera3Device::setErrorState(const char *fmt, ...) {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
va_list args;
va_start(args, fmt);
@@ -2327,6 +2331,7 @@
}
void Camera3Device::setErrorStateV(const char *fmt, va_list args) {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
setErrorStateLockedV(fmt, args);
}
@@ -2411,6 +2416,7 @@
}
void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+ ATRACE_CALL();
nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
mInFlightMap.removeItemsAt(idx, 1);
@@ -2433,25 +2439,6 @@
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
@@ -2459,7 +2446,7 @@
// In the case of a unsuccessful request:
// all input and output buffers arrived.
if (request.numBuffersLeft == 0 &&
- (skipResultMetadata ||
+ (request.skipResultMetadata ||
(request.haveResultMetadata && shutterTimestamp != 0))) {
ATRACE_ASYNC_END("frame capture", frameNumber);
@@ -2495,6 +2482,7 @@
}
void Camera3Device::flushInflightRequests() {
+ ATRACE_CALL();
{ // First return buffers cached in mInFlightMap
Mutex::Autolock l(mInFlightLock);
for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
@@ -2621,6 +2609,7 @@
void Camera3Device::sendPartialCaptureResult(const camera_metadata_t * partialResult,
const CaptureResultExtras &resultExtras, uint32_t frameNumber) {
+ ATRACE_CALL();
Mutex::Autolock l(mOutputLock);
CaptureResult captureResult;
@@ -2636,6 +2625,7 @@
CameraMetadata &collectedPartialResult,
uint32_t frameNumber,
bool reprocess) {
+ ATRACE_CALL();
if (pendingMetadata.isEmpty())
return;
@@ -2884,7 +2874,7 @@
void Camera3Device::notifyError(const camera3_error_msg_t &msg,
sp<NotificationListener> listener) {
-
+ ATRACE_CALL();
// Map camera HAL error codes to ICameraDeviceCallback error codes
// Index into this with the HAL error code
static const int32_t halErrorMap[CAMERA3_MSG_NUM_ERRORS] = {
@@ -2932,6 +2922,11 @@
InFlightRequest &r = mInFlightMap.editValueAt(idx);
r.requestStatus = msg.error_code;
resultExtras = r.resultExtras;
+ if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == errorCode
+ || hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
+ errorCode) {
+ r.skipResultMetadata = true;
+ }
if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
errorCode) {
// In case of missing result check whether the buffers
@@ -2962,6 +2957,7 @@
void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg,
sp<NotificationListener> listener) {
+ ATRACE_CALL();
ssize_t idx;
// Set timestamp for the request in the in-flight tracking
@@ -3048,24 +3044,20 @@
Camera3Device::HalInterface::HalInterface(
sp<ICameraDeviceSession> &session,
std::shared_ptr<RequestMetadataQueue> queue) :
- mHal3Device(nullptr),
mHidlSession(session),
mRequestMetadataQueue(queue) {}
-Camera3Device::HalInterface::HalInterface() :
- mHal3Device(nullptr) {}
+Camera3Device::HalInterface::HalInterface() {}
Camera3Device::HalInterface::HalInterface(const HalInterface& other) :
- mHal3Device(other.mHal3Device),
mHidlSession(other.mHidlSession),
mRequestMetadataQueue(other.mRequestMetadataQueue) {}
bool Camera3Device::HalInterface::valid() {
- return (mHal3Device != nullptr) || (mHidlSession != nullptr);
+ return (mHidlSession != nullptr);
}
void Camera3Device::HalInterface::clear() {
- mHal3Device = nullptr;
mHidlSession.clear();
}
@@ -3080,72 +3072,60 @@
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
- if (mHal3Device != nullptr) {
- const camera_metadata *r;
- r = mHal3Device->ops->construct_default_request_settings(
- mHal3Device, templateId);
- if (r == nullptr) return BAD_VALUE;
- *requestTemplate = clone_camera_metadata(r);
- if (requestTemplate == nullptr) {
- ALOGE("%s: Unable to clone camera metadata received from HAL",
- __FUNCTION__);
- return INVALID_OPERATION;
- }
- } else {
- common::V1_0::Status status;
- RequestTemplate id;
- switch (templateId) {
- case CAMERA3_TEMPLATE_PREVIEW:
- id = RequestTemplate::PREVIEW;
- break;
- case CAMERA3_TEMPLATE_STILL_CAPTURE:
- id = RequestTemplate::STILL_CAPTURE;
- break;
- case CAMERA3_TEMPLATE_VIDEO_RECORD:
- id = RequestTemplate::VIDEO_RECORD;
- break;
- case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
- id = RequestTemplate::VIDEO_SNAPSHOT;
- break;
- case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
- id = RequestTemplate::ZERO_SHUTTER_LAG;
- break;
- case CAMERA3_TEMPLATE_MANUAL:
- id = RequestTemplate::MANUAL;
- break;
- default:
- // Unknown template ID
- return BAD_VALUE;
- }
- auto err = mHidlSession->constructDefaultRequestSettings(id,
- [&status, &requestTemplate]
- (common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
- status = s;
- if (status == common::V1_0::Status::OK) {
- const camera_metadata *r =
- reinterpret_cast<const camera_metadata_t*>(request.data());
- size_t expectedSize = request.size();
- int ret = validate_camera_metadata_structure(r, &expectedSize);
- if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
- *requestTemplate = clone_camera_metadata(r);
- if (*requestTemplate == nullptr) {
- ALOGE("%s: Unable to clone camera metadata received from HAL",
- __FUNCTION__);
- status = common::V1_0::Status::INTERNAL_ERROR;
- }
- } else {
- ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+ common::V1_0::Status status;
+ RequestTemplate id;
+ switch (templateId) {
+ case CAMERA3_TEMPLATE_PREVIEW:
+ id = RequestTemplate::PREVIEW;
+ break;
+ case CAMERA3_TEMPLATE_STILL_CAPTURE:
+ id = RequestTemplate::STILL_CAPTURE;
+ break;
+ case CAMERA3_TEMPLATE_VIDEO_RECORD:
+ id = RequestTemplate::VIDEO_RECORD;
+ break;
+ case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+ id = RequestTemplate::VIDEO_SNAPSHOT;
+ break;
+ case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+ id = RequestTemplate::ZERO_SHUTTER_LAG;
+ break;
+ case CAMERA3_TEMPLATE_MANUAL:
+ id = RequestTemplate::MANUAL;
+ break;
+ default:
+ // Unknown template ID
+ return BAD_VALUE;
+ }
+ auto err = mHidlSession->constructDefaultRequestSettings(id,
+ [&status, &requestTemplate]
+ (common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
+ status = s;
+ if (status == common::V1_0::Status::OK) {
+ const camera_metadata *r =
+ reinterpret_cast<const camera_metadata_t*>(request.data());
+ size_t expectedSize = request.size();
+ int ret = validate_camera_metadata_structure(r, &expectedSize);
+ if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
+ *requestTemplate = clone_camera_metadata(r);
+ if (*requestTemplate == nullptr) {
+ ALOGE("%s: Unable to clone camera metadata received from HAL",
+ __FUNCTION__);
status = common::V1_0::Status::INTERNAL_ERROR;
}
+ } else {
+ ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+ status = common::V1_0::Status::INTERNAL_ERROR;
}
- });
- if (!err.isOk()) {
- ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
- res = DEAD_OBJECT;
- } else {
- res = CameraProviderManager::mapToStatusT(status);
- }
+ }
+ });
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ res = DEAD_OBJECT;
+ } else {
+ res = CameraProviderManager::mapToStatusT(status);
}
+
return res;
}
@@ -3154,145 +3134,144 @@
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
- if (mHal3Device != nullptr) {
- res = mHal3Device->ops->configure_streams(mHal3Device, config);
- } else {
- // Convert stream config to HIDL
- std::set<int> activeStreams;
- StreamConfiguration requestedConfiguration;
- requestedConfiguration.streams.resize(config->num_streams);
- for (size_t i = 0; i < config->num_streams; i++) {
- Stream &dst = requestedConfiguration.streams[i];
- camera3_stream_t *src = config->streams[i];
+ // Convert stream config to HIDL
+ std::set<int> activeStreams;
+ StreamConfiguration requestedConfiguration;
+ requestedConfiguration.streams.resize(config->num_streams);
+ for (size_t i = 0; i < config->num_streams; i++) {
+ Stream &dst = requestedConfiguration.streams[i];
+ camera3_stream_t *src = config->streams[i];
- Camera3Stream* cam3stream = Camera3Stream::cast(src);
- cam3stream->setBufferFreedListener(this);
- int streamId = cam3stream->getId();
- StreamType streamType;
- switch (src->stream_type) {
- case CAMERA3_STREAM_OUTPUT:
- streamType = StreamType::OUTPUT;
- break;
- case CAMERA3_STREAM_INPUT:
- streamType = StreamType::INPUT;
- break;
- default:
- ALOGE("%s: Stream %d: Unsupported stream type %d",
- __FUNCTION__, streamId, config->streams[i]->stream_type);
- return BAD_VALUE;
+ Camera3Stream* cam3stream = Camera3Stream::cast(src);
+ cam3stream->setBufferFreedListener(this);
+ int streamId = cam3stream->getId();
+ StreamType streamType;
+ switch (src->stream_type) {
+ case CAMERA3_STREAM_OUTPUT:
+ streamType = StreamType::OUTPUT;
+ break;
+ case CAMERA3_STREAM_INPUT:
+ streamType = StreamType::INPUT;
+ break;
+ default:
+ ALOGE("%s: Stream %d: Unsupported stream type %d",
+ __FUNCTION__, streamId, config->streams[i]->stream_type);
+ return BAD_VALUE;
+ }
+ dst.id = streamId;
+ dst.streamType = streamType;
+ dst.width = src->width;
+ dst.height = src->height;
+ dst.format = mapToPixelFormat(src->format);
+ dst.usage = mapToConsumerUsage(cam3stream->getUsage());
+ dst.dataSpace = mapToHidlDataspace(src->data_space);
+ dst.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
+
+ activeStreams.insert(streamId);
+ // Create Buffer ID map if necessary
+ if (mBufferIdMaps.count(streamId) == 0) {
+ mBufferIdMaps.emplace(streamId, BufferIdMap{});
+ }
+ }
+ // remove BufferIdMap for deleted streams
+ for(auto it = mBufferIdMaps.begin(); it != mBufferIdMaps.end();) {
+ int streamId = it->first;
+ bool active = activeStreams.count(streamId) > 0;
+ if (!active) {
+ it = mBufferIdMaps.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ res = mapToStreamConfigurationMode(
+ (camera3_stream_configuration_mode_t) config->operation_mode,
+ /*out*/ &requestedConfiguration.operationMode);
+ if (res != OK) {
+ return res;
+ }
+
+ // Invoke configureStreams
+
+ HalStreamConfiguration finalConfiguration;
+ common::V1_0::Status status;
+ auto err = mHidlSession->configureStreams(requestedConfiguration,
+ [&status, &finalConfiguration]
+ (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
+ finalConfiguration = halConfiguration;
+ status = s;
+ });
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+
+ if (status != common::V1_0::Status::OK ) {
+ return CameraProviderManager::mapToStatusT(status);
+ }
+
+ // And convert output stream configuration from HIDL
+
+ for (size_t i = 0; i < config->num_streams; i++) {
+ camera3_stream_t *dst = config->streams[i];
+ int streamId = Camera3Stream::cast(dst)->getId();
+
+ // Start scan at i, with the assumption that the stream order matches
+ size_t realIdx = i;
+ bool found = false;
+ for (size_t idx = 0; idx < finalConfiguration.streams.size(); idx++) {
+ if (finalConfiguration.streams[realIdx].id == streamId) {
+ found = true;
+ break;
}
- dst.id = streamId;
- dst.streamType = streamType;
- dst.width = src->width;
- dst.height = src->height;
- dst.format = mapToPixelFormat(src->format);
- dst.usage = mapToConsumerUsage(src->usage);
- dst.dataSpace = mapToHidlDataspace(src->data_space);
- dst.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
+ realIdx = (realIdx >= finalConfiguration.streams.size()) ? 0 : realIdx + 1;
+ }
+ if (!found) {
+ ALOGE("%s: Stream %d not found in stream configuration response from HAL",
+ __FUNCTION__, streamId);
+ return INVALID_OPERATION;
+ }
+ HalStream &src = finalConfiguration.streams[realIdx];
- activeStreams.insert(streamId);
- // Create Buffer ID map if necessary
- if (mBufferIdMaps.count(streamId) == 0) {
- mBufferIdMaps.emplace(streamId, BufferIdMap{});
+ int overrideFormat = mapToFrameworkFormat(src.overrideFormat);
+ if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ if (dst->format != overrideFormat) {
+ ALOGE("%s: Stream %d: Format override not allowed for format 0x%x", __FUNCTION__,
+ streamId, dst->format);
}
- }
- // remove BufferIdMap for deleted streams
- for(auto it = mBufferIdMaps.begin(); it != mBufferIdMaps.end();) {
- int streamId = it->first;
- bool active = activeStreams.count(streamId) > 0;
- if (!active) {
- it = mBufferIdMaps.erase(it);
- } else {
- ++it;
- }
+ } else {
+ // Override allowed with IMPLEMENTATION_DEFINED
+ dst->format = overrideFormat;
}
- res = mapToStreamConfigurationMode(
- (camera3_stream_configuration_mode_t) config->operation_mode,
- /*out*/ &requestedConfiguration.operationMode);
- if (res != OK) {
- return res;
- }
-
- // Invoke configureStreams
-
- HalStreamConfiguration finalConfiguration;
- common::V1_0::Status status;
- auto err = mHidlSession->configureStreams(requestedConfiguration,
- [&status, &finalConfiguration]
- (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
- finalConfiguration = halConfiguration;
- status = s;
- });
- if (!err.isOk()) {
- ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
- return DEAD_OBJECT;
- }
-
- if (status != common::V1_0::Status::OK ) {
- return CameraProviderManager::mapToStatusT(status);
- }
-
- // And convert output stream configuration from HIDL
-
- for (size_t i = 0; i < config->num_streams; i++) {
- camera3_stream_t *dst = config->streams[i];
- int streamId = Camera3Stream::cast(dst)->getId();
-
- // Start scan at i, with the assumption that the stream order matches
- size_t realIdx = i;
- bool found = false;
- for (size_t idx = 0; idx < finalConfiguration.streams.size(); idx++) {
- if (finalConfiguration.streams[realIdx].id == streamId) {
- found = true;
- break;
- }
- realIdx = (realIdx >= finalConfiguration.streams.size()) ? 0 : realIdx + 1;
- }
- if (!found) {
- ALOGE("%s: Stream %d not found in stream configuration response from HAL",
+ if (dst->stream_type == CAMERA3_STREAM_INPUT) {
+ if (src.producerUsage != 0) {
+ ALOGE("%s: Stream %d: INPUT streams must have 0 for producer usage",
__FUNCTION__, streamId);
return INVALID_OPERATION;
}
- HalStream &src = finalConfiguration.streams[realIdx];
-
- int overrideFormat = mapToFrameworkFormat(src.overrideFormat);
- if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- if (dst->format != overrideFormat) {
- ALOGE("%s: Stream %d: Format override not allowed for format 0x%x", __FUNCTION__,
- streamId, dst->format);
- }
- } else {
- // Override allowed with IMPLEMENTATION_DEFINED
- dst->format = overrideFormat;
+ Camera3Stream::cast(dst)->setUsage(
+ mapConsumerToFrameworkUsage(src.consumerUsage));
+ } else {
+ // OUTPUT
+ if (src.consumerUsage != 0) {
+ ALOGE("%s: Stream %d: OUTPUT streams must have 0 for consumer usage",
+ __FUNCTION__, streamId);
+ return INVALID_OPERATION;
}
-
- if (dst->stream_type == CAMERA3_STREAM_INPUT) {
- if (src.producerUsage != 0) {
- ALOGE("%s: Stream %d: INPUT streams must have 0 for producer usage",
- __FUNCTION__, streamId);
- return INVALID_OPERATION;
- }
- dst->usage = mapConsumerToFrameworkUsage(src.consumerUsage);
- } else {
- // OUTPUT
- if (src.consumerUsage != 0) {
- ALOGE("%s: Stream %d: OUTPUT streams must have 0 for consumer usage",
- __FUNCTION__, streamId);
- return INVALID_OPERATION;
- }
- dst->usage = mapProducerToFrameworkUsage(src.producerUsage);
- }
- dst->max_buffers = src.maxBuffers;
+ Camera3Stream::cast(dst)->setUsage(
+ mapProducerToFrameworkUsage(src.producerUsage));
}
+ dst->max_buffers = src.maxBuffers;
}
+
return res;
}
void Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
/*out*/device::V3_2::CaptureRequest* captureRequest,
/*out*/std::vector<native_handle_t*>* handlesCreated) {
-
+ ATRACE_CALL();
if (captureRequest == nullptr || handlesCreated == nullptr) {
ALOGE("%s: captureRequest (%p) and handlesCreated (%p) must not be null",
__FUNCTION__, captureRequest, handlesCreated);
@@ -3441,14 +3420,11 @@
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
- if (mHal3Device != nullptr) {
- res = mHal3Device->ops->process_capture_request(mHal3Device, request);
- } else {
- uint32_t numRequestProcessed = 0;
- std::vector<camera3_capture_request_t*> requests(1);
- requests[0] = request;
- res = processBatchCaptureRequests(requests, &numRequestProcessed);
- }
+ uint32_t numRequestProcessed = 0;
+ std::vector<camera3_capture_request_t*> requests(1);
+ requests[0] = request;
+ res = processBatchCaptureRequests(requests, &numRequestProcessed);
+
return res;
}
@@ -3457,31 +3433,24 @@
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
- if (mHal3Device != nullptr) {
- res = mHal3Device->ops->flush(mHal3Device);
+ auto err = mHidlSession->flush();
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ res = DEAD_OBJECT;
} else {
- auto err = mHidlSession->flush();
- if (!err.isOk()) {
- ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
- res = DEAD_OBJECT;
- } else {
- res = CameraProviderManager::mapToStatusT(err);
- }
+ res = CameraProviderManager::mapToStatusT(err);
}
+
return res;
}
-status_t Camera3Device::HalInterface::dump(int fd) {
+status_t Camera3Device::HalInterface::dump(int /*fd*/) {
ATRACE_NAME("CameraHal::dump");
if (!valid()) return INVALID_OPERATION;
- status_t res = OK;
- if (mHal3Device != nullptr) {
- mHal3Device->ops->dump(mHal3Device, fd);
- } else {
- // Handled by CameraProviderManager::dump
- }
- return res;
+ // Handled by CameraProviderManager::dump
+
+ return OK;
}
status_t Camera3Device::HalInterface::close() {
@@ -3489,15 +3458,12 @@
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
- if (mHal3Device != nullptr) {
- mHal3Device->common.close(&mHal3Device->common);
- } else {
- auto err = mHidlSession->close();
- // Interface will be dead shortly anyway, so don't log errors
- if (!err.isOk()) {
- res = DEAD_OBJECT;
- }
+ auto err = mHidlSession->close();
+ // Interface will be dead shortly anyway, so don't log errors
+ if (!err.isOk()) {
+ res = DEAD_OBJECT;
}
+
return res;
}
@@ -3614,11 +3580,13 @@
void Camera3Device::RequestThread::setNotificationListener(
wp<NotificationListener> listener) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
mListener = listener;
}
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
mReconfigured = true;
// Prepare video stream for high speed recording.
@@ -3629,6 +3597,7 @@
List<sp<CaptureRequest> > &requests,
/*out*/
int64_t *lastFrameNumber) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end();
++it) {
@@ -3651,7 +3620,7 @@
status_t Camera3Device::RequestThread::queueTrigger(
RequestTrigger trigger[],
size_t count) {
-
+ ATRACE_CALL();
Mutex::Autolock l(mTriggerMutex);
status_t ret;
@@ -3708,6 +3677,7 @@
const RequestList &requests,
/*out*/
int64_t *lastFrameNumber) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
if (lastFrameNumber != NULL) {
*lastFrameNumber = mRepeatingLastFrameNumber;
@@ -3734,6 +3704,7 @@
}
status_t Camera3Device::RequestThread::clearRepeatingRequests(/*out*/int64_t *lastFrameNumber) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
return clearRepeatingRequestsLocked(lastFrameNumber);
@@ -3750,6 +3721,7 @@
status_t Camera3Device::RequestThread::clear(
/*out*/int64_t *lastFrameNumber) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
ALOGV("RequestThread::%s:", __FUNCTION__);
@@ -3805,6 +3777,7 @@
}
void Camera3Device::RequestThread::setPaused(bool paused) {
+ ATRACE_CALL();
Mutex::Autolock l(mPauseLock);
mDoPause = paused;
mDoPauseSignal.signal();
@@ -3812,6 +3785,7 @@
status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
int32_t requestId, nsecs_t timeout) {
+ ATRACE_CALL();
Mutex::Autolock l(mLatestRequestMutex);
status_t res;
while (mLatestRequestId != requestId) {
@@ -3838,6 +3812,7 @@
}
void Camera3Device::RequestThread::checkAndStopRepeatingRequest() {
+ ATRACE_CALL();
bool surfaceAbandoned = false;
int64_t lastFrameNumber = 0;
sp<NotificationListener> listener;
@@ -3866,6 +3841,7 @@
}
bool Camera3Device::RequestThread::sendRequestsBatch() {
+ ATRACE_CALL();
status_t res;
size_t batchSize = mNextRequests.size();
std::vector<camera3_capture_request_t*> requests(batchSize);
@@ -4261,6 +4237,7 @@
}
CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
+ ATRACE_CALL();
Mutex::Autolock al(mLatestRequestMutex);
ALOGV("RequestThread::%s", __FUNCTION__);
@@ -4270,6 +4247,7 @@
bool Camera3Device::RequestThread::isStreamPending(
sp<Camera3StreamInterface>& stream) {
+ ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
for (const auto& nextRequest : mNextRequests) {
@@ -4299,6 +4277,7 @@
}
nsecs_t Camera3Device::getExpectedInFlightDuration() {
+ ATRACE_CALL();
Mutex::Autolock al(mInFlightLock);
return mExpectedInflightDuration > kMinInflightDuration ?
mExpectedInflightDuration : kMinInflightDuration;
@@ -4370,6 +4349,7 @@
}
void Camera3Device::RequestThread::waitForNextRequestBatch() {
+ ATRACE_CALL();
// Optimized a bit for the simple steady-state case (single repeating
// request), to avoid putting that request in the queue temporarily.
Mutex::Autolock l(mRequestLock);
@@ -4519,6 +4499,7 @@
}
bool Camera3Device::RequestThread::waitIfPaused() {
+ ATRACE_CALL();
status_t res;
Mutex::Autolock l(mPauseLock);
while (mDoPause) {
@@ -4543,6 +4524,7 @@
}
void Camera3Device::RequestThread::unpauseForNewRequests() {
+ ATRACE_CALL();
// With work to do, mark thread as unpaused.
// If paused by request (setPaused), don't resume, to avoid
// extra signaling/waiting overhead to waitUntilPaused
@@ -4574,7 +4556,7 @@
status_t Camera3Device::RequestThread::insertTriggers(
const sp<CaptureRequest> &request) {
-
+ ATRACE_CALL();
Mutex::Autolock al(mTriggerMutex);
sp<Camera3Device> parent = mParent.promote();
@@ -4663,6 +4645,7 @@
status_t Camera3Device::RequestThread::removeTriggers(
const sp<CaptureRequest> &request) {
+ ATRACE_CALL();
Mutex::Autolock al(mTriggerMutex);
CameraMetadata &metadata = request->mSettings;
@@ -4779,6 +4762,7 @@
}
status_t Camera3Device::PreparerThread::prepare(int maxCount, sp<Camera3StreamInterface>& stream) {
+ ATRACE_CALL();
status_t res;
Mutex::Autolock l(mLock);
@@ -4822,6 +4806,7 @@
}
status_t Camera3Device::PreparerThread::clear() {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
for (const auto& stream : mPendingStreams) {
@@ -4834,6 +4819,7 @@
}
void Camera3Device::PreparerThread::setNotificationListener(wp<NotificationListener> listener) {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
mListener = listener;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index d700e03..363bd88 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -117,12 +117,12 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint32_t consumerUsage = 0) override;
+ bool isShared = false, uint64_t consumerUsage = 0) override;
status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint32_t consumerUsage = 0) override;
+ bool isShared = false, uint64_t consumerUsage = 0) override;
status_t createInputStream(
uint32_t width, uint32_t height, int format,
@@ -271,7 +271,6 @@
void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
private:
- camera3_device_t *mHal3Device;
sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
@@ -590,7 +589,7 @@
static hardware::graphics::common::V1_0::PixelFormat mapToPixelFormat(int frameworkFormat);
static hardware::camera::device::V3_2::DataspaceFlags mapToHidlDataspace(
android_dataspace dataSpace);
- static hardware::camera::device::V3_2::BufferUsageFlags mapToConsumerUsage(uint32_t usage);
+ static hardware::camera::device::V3_2::BufferUsageFlags mapToConsumerUsage(uint64_t usage);
static hardware::camera::device::V3_2::StreamRotation mapToStreamRotation(
camera3_stream_rotation_t rotation);
// Returns a negative error code if the passed-in operation mode is not valid.
@@ -598,9 +597,9 @@
/*out*/ hardware::camera::device::V3_2::StreamConfigurationMode *mode);
static camera3_buffer_status_t mapHidlBufferStatus(hardware::camera::device::V3_2::BufferStatus status);
static int mapToFrameworkFormat(hardware::graphics::common::V1_0::PixelFormat pixelFormat);
- static uint32_t mapConsumerToFrameworkUsage(
+ static uint64_t mapConsumerToFrameworkUsage(
hardware::camera::device::V3_2::BufferUsageFlags usage);
- static uint32_t mapProducerToFrameworkUsage(
+ static uint64_t mapProducerToFrameworkUsage(
hardware::camera::device::V3_2::BufferUsageFlags usage);
struct RequestTrigger {
@@ -886,6 +885,11 @@
// For auto-exposure modes, equal to 1/(lower end of target FPS range)
nsecs_t maxExpectedDuration;
+ // Whether the result metadata for this request is to be skipped. The
+ // result metadata should be skipped in the case of
+ // REQUEST/RESULT error.
+ bool skipResultMetadata;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
shutterTimestamp(0),
@@ -895,7 +899,8 @@
numBuffersLeft(0),
hasInputBuffer(false),
hasCallback(true),
- maxExpectedDuration(kDefaultExpectedDuration) {
+ maxExpectedDuration(kDefaultExpectedDuration),
+ skipResultMetadata(false) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
@@ -908,7 +913,8 @@
resultExtras(extras),
hasInputBuffer(hasInput),
hasCallback(hasAppCallback),
- maxExpectedDuration(maxDuration) {
+ maxExpectedDuration(maxDuration),
+ skipResultMetadata(false) {
}
};
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 9c951b7..6e2978f 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -95,7 +95,7 @@
return OK;
}
-status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) const {
+status_t Camera3DummyStream::getEndpointUsage(uint64_t *usage) const {
*usage = DUMMY_USAGE;
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 35a6a18..492fb49 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -94,7 +94,7 @@
static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
- static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
+ static const uint64_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
/**
* Internal Camera3Stream interface
@@ -107,7 +107,7 @@
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) const;
+ virtual status_t getEndpointUsage(uint64_t *usage) const;
}; // class Camera3DummyStream
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 7ad2300..a52422d 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -69,7 +69,7 @@
(void) args;
String8 lines;
- uint32_t consumerUsage = 0;
+ uint64_t consumerUsage = 0;
status_t res = getEndpointUsage(&consumerUsage);
if (res != OK) consumerUsage = 0;
@@ -78,8 +78,8 @@
camera3_stream::width, camera3_stream::height,
camera3_stream::format, camera3_stream::data_space);
lines.appendFormat(" Max size: %zu\n", mMaxSize);
- lines.appendFormat(" Combined usage: %d, max HAL buffers: %d\n",
- camera3_stream::usage | consumerUsage, camera3_stream::max_buffers);
+ lines.appendFormat(" Combined usage: %" PRIu64 ", max HAL buffers: %d\n",
+ mUsage | consumerUsage, camera3_stream::max_buffers);
lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n",
mFrameCount, mLastTimestamp);
lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n",
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 35dda39..2376058 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -85,7 +85,7 @@
virtual size_t getHandoutInputBufferCountLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
+ virtual status_t getEndpointUsage(uint64_t *usage) const = 0;
status_t getBufferPreconditionCheckLocked() const;
status_t returnBufferPreconditionCheckLocked() const;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index ff2dcef..2cb1ea7 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -258,7 +258,7 @@
camera3_stream::max_buffers : minBufs;
// TODO: somehow set the total buffer count when producer connects?
- mConsumer = new BufferItemConsumer(consumer, camera3_stream::usage,
+ mConsumer = new BufferItemConsumer(consumer, mUsage,
mTotalBufferCount);
mConsumer->setName(String8::format("Camera3-InputStream-%d", mId));
@@ -284,7 +284,7 @@
return OK;
}
-status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) const {
+status_t Camera3InputStream::getEndpointUsage(uint64_t *usage) const {
// Per HAL3 spec, input streams have 0 for their initial usage field.
*usage = 0;
return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 8f5b431..81226f8 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -76,7 +76,7 @@
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) const;
+ virtual status_t getEndpointUsage(uint64_t *usage) const;
/**
* BufferItemConsumer::BufferFreedListener interface
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index b02cd6a..dcaefe3 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -90,7 +90,7 @@
Camera3OutputStream::Camera3OutputStream(int id,
uint32_t width, uint32_t height, int format,
- uint32_t consumerUsage, android_dataspace dataSpace,
+ uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation, nsecs_t timestampOffset, int setId) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
/*maxSize*/0, format, dataSpace, rotation, setId),
@@ -111,7 +111,8 @@
// Sanity check for the consumer usage flag.
if ((consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) == 0 &&
(consumerUsage & GraphicBuffer::USAGE_HW_COMPOSER) == 0) {
- ALOGE("%s: Deferred consumer usage flag is illegal (0x%x)!", __FUNCTION__, consumerUsage);
+ ALOGE("%s: Deferred consumer usage flag is illegal %" PRIu64 "!",
+ __FUNCTION__, consumerUsage);
mState = STATE_ERROR;
}
@@ -127,7 +128,7 @@
int format,
android_dataspace dataSpace,
camera3_stream_rotation_t rotation,
- uint32_t consumerUsage, nsecs_t timestampOffset,
+ uint64_t consumerUsage, nsecs_t timestampOffset,
int setId) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
@@ -365,10 +366,10 @@
mConsumerName = mConsumer->getConsumerName();
- res = native_window_set_usage(mConsumer.get(), camera3_stream::usage);
+ res = native_window_set_usage(mConsumer.get(), mUsage);
if (res != OK) {
- ALOGE("%s: Unable to configure usage %08x for stream %d",
- __FUNCTION__, camera3_stream::usage, mId);
+ ALOGE("%s: Unable to configure usage %" PRIu64 " for stream %d",
+ __FUNCTION__, mUsage, mId);
return res;
}
@@ -461,11 +462,11 @@
* HAL3.2 devices may not support the dynamic buffer registeration.
*/
if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID) {
- uint32_t consumerUsage = 0;
+ uint64_t consumerUsage = 0;
getEndpointUsage(&consumerUsage);
StreamInfo streamInfo(
getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),
- camera3_stream::usage | consumerUsage, mTotalBufferCount,
+ mUsage | consumerUsage, mTotalBufferCount,
/*isConfigured*/true);
wp<Camera3OutputStream> weakThis(this);
res = mBufferManager->registerStream(weakThis,
@@ -628,7 +629,7 @@
return OK;
}
-status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) const {
+status_t Camera3OutputStream::getEndpointUsage(uint64_t *usage) const {
status_t res;
@@ -643,14 +644,12 @@
return res;
}
-status_t Camera3OutputStream::getEndpointUsageForSurface(uint32_t *usage,
+status_t Camera3OutputStream::getEndpointUsageForSurface(uint64_t *usage,
const sp<Surface>& surface) const {
status_t res;
- int32_t u = 0;
+ uint64_t u = 0;
- res = static_cast<ANativeWindow*>(surface.get())->query(surface.get(),
- NATIVE_WINDOW_CONSUMER_USAGE_BITS, &u);
-
+ res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(surface.get()), &u);
// If an opaque output stream's endpoint is ImageReader, add
// GRALLOC_USAGE_HW_CAMERA_ZSL to the usage so HAL knows it will be used
// for the ZSL use case.
@@ -670,7 +669,7 @@
}
bool Camera3OutputStream::isVideoStream() const {
- uint32_t usage = 0;
+ uint64_t usage = 0;
status_t res = getEndpointUsage(&usage);
if (res != OK) {
ALOGE("%s: getting end point usage failed: %s (%d).", __FUNCTION__, strerror(-res), res);
@@ -813,7 +812,7 @@
}
bool Camera3OutputStream::isConsumedByHWComposer() const {
- uint32_t usage = 0;
+ uint64_t usage = 0;
status_t res = getEndpointUsage(&usage);
if (res != OK) {
ALOGE("%s: getting end point usage failed: %s (%d).", __FUNCTION__, strerror(-res), res);
@@ -824,7 +823,7 @@
}
bool Camera3OutputStream::isConsumedByHWTexture() const {
- uint32_t usage = 0;
+ uint64_t usage = 0;
status_t res = getEndpointUsage(&usage);
if (res != OK) {
ALOGE("%s: getting end point usage failed: %s (%d).", __FUNCTION__, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 97aa7d4..7023d5d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -44,7 +44,7 @@
uint32_t height;
uint32_t format;
android_dataspace dataSpace;
- uint32_t combinedUsage;
+ uint64_t combinedUsage;
size_t totalBufferCount;
bool isConfigured;
explicit StreamInfo(int id = CAMERA3_STREAM_ID_INVALID,
@@ -53,7 +53,7 @@
uint32_t h = 0,
uint32_t fmt = 0,
android_dataspace ds = HAL_DATASPACE_UNKNOWN,
- uint32_t usage = 0,
+ uint64_t usage = 0,
size_t bufferCount = 0,
bool configured = false) :
streamId(id),
@@ -101,7 +101,7 @@
* stream set id needs to be set to support buffer sharing between multiple streams.
*/
Camera3OutputStream(int id, uint32_t width, uint32_t height, int format,
- uint32_t consumerUsage, android_dataspace dataSpace,
+ uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
int setId = CAMERA3_STREAM_SET_ID_INVALID);
@@ -176,7 +176,7 @@
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation,
- uint32_t consumerUsage = 0, nsecs_t timestampOffset = 0,
+ uint64_t consumerUsage = 0, nsecs_t timestampOffset = 0,
int setId = CAMERA3_STREAM_SET_ID_INVALID);
/**
@@ -191,14 +191,14 @@
virtual status_t disconnectLocked();
- status_t getEndpointUsageForSurface(uint32_t *usage,
+ status_t getEndpointUsageForSurface(uint64_t *usage,
const sp<Surface>& surface) const;
status_t configureConsumerQueueLocked();
// Consumer as the output of camera HAL
sp<Surface> mConsumer;
- uint32_t getPresetConsumerUsage() const { return mConsumerUsage; }
+ uint64_t getPresetConsumerUsage() const { return mConsumerUsage; }
static const nsecs_t kDequeueBufferTimeout = 1000000000; // 1 sec
@@ -245,7 +245,7 @@
* Consumer end point usage flag set by the constructor for the deferred
* consumer case.
*/
- uint32_t mConsumerUsage;
+ uint64_t mConsumerUsage;
/**
* Internal Camera3Stream interface
@@ -262,7 +262,7 @@
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) const;
+ virtual status_t getEndpointUsage(uint64_t *usage) const;
/**
* Private methods
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 2ae5660..5051711 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -23,7 +23,7 @@
Camera3SharedOutputStream::Camera3SharedOutputStream(int id,
const std::vector<sp<Surface>>& surfaces,
uint32_t width, uint32_t height, int format,
- uint32_t consumerUsage, android_dataspace dataSpace,
+ uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation,
nsecs_t timestampOffset, int setId) :
Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
@@ -41,7 +41,7 @@
mStreamSplitter = new Camera3StreamSplitter();
- uint32_t usage;
+ uint64_t usage;
getEndpointUsage(&usage);
res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, &mConsumer);
@@ -191,10 +191,10 @@
return res;
}
-status_t Camera3SharedOutputStream::getEndpointUsage(uint32_t *usage) const {
+status_t Camera3SharedOutputStream::getEndpointUsage(uint64_t *usage) const {
status_t res = OK;
- uint32_t u = 0;
+ uint64_t u = 0;
if (mConsumer == nullptr) {
// Called before shared buffer queue is constructed.
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 7be0940..22bb2fc 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -34,7 +34,7 @@
*/
Camera3SharedOutputStream(int id, const std::vector<sp<Surface>>& surfaces,
uint32_t width, uint32_t height, int format,
- uint32_t consumerUsage, android_dataspace dataSpace,
+ uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
int setId = CAMERA3_STREAM_SET_ID_INVALID);
@@ -74,7 +74,7 @@
virtual status_t disconnectLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) const;
+ virtual status_t getEndpointUsage(uint64_t *usage) const;
}; // class Camera3SharedOutputStream
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 9e6ac79..25e44a5 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -56,6 +56,7 @@
mState(STATE_CONSTRUCTED),
mStatusId(StatusTracker::NO_STATUS_ID),
mStreamUnpreparable(true),
+ mUsage(0),
mOldUsage(0),
mOldMaxBuffers(0),
mPrepared(false),
@@ -69,7 +70,6 @@
camera3_stream::format = format;
camera3_stream::data_space = dataSpace;
camera3_stream::rotation = rotation;
- camera3_stream::usage = 0;
camera3_stream::max_buffers = 0;
camera3_stream::priv = NULL;
@@ -104,6 +104,14 @@
return camera3_stream::data_space;
}
+uint64_t Camera3Stream::getUsage() const {
+ return mUsage;
+}
+
+void Camera3Stream::setUsage(uint64_t usage) {
+ mUsage = usage;
+}
+
camera3_stream* Camera3Stream::startConfiguration() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -133,10 +141,10 @@
return NULL;
}
- mOldUsage = camera3_stream::usage;
+ mOldUsage = mUsage;
mOldMaxBuffers = camera3_stream::max_buffers;
- res = getEndpointUsage(&(camera3_stream::usage));
+ res = getEndpointUsage(&mUsage);
if (res != OK) {
ALOGE("%s: Cannot query consumer endpoint usage!",
__FUNCTION__);
@@ -197,7 +205,7 @@
// Check if the stream configuration is unchanged, and skip reallocation if
// so. As documented in hardware/camera3.h:configure_streams().
if (mState == STATE_IN_RECONFIG &&
- mOldUsage == camera3_stream::usage &&
+ mOldUsage == mUsage &&
mOldMaxBuffers == camera3_stream::max_buffers) {
mState = STATE_CONFIGURED;
return OK;
@@ -243,7 +251,7 @@
return INVALID_OPERATION;
}
- camera3_stream::usage = mOldUsage;
+ mUsage = mOldUsage;
camera3_stream::max_buffers = mOldMaxBuffers;
mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 44fe6b6..9090f83 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -144,6 +144,8 @@
uint32_t getHeight() const;
int getFormat() const;
android_dataspace getDataSpace() const;
+ uint64_t getUsage() const;
+ void setUsage(uint64_t usage);
camera3_stream* asHalStream() override {
return this;
@@ -459,7 +461,7 @@
// Get the usage flags for the other endpoint, or return
// INVALID_OPERATION if they cannot be obtained.
- virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
+ virtual status_t getEndpointUsage(uint64_t *usage) const = 0;
// Return whether the buffer is in the list of outstanding buffers.
bool isOutstandingBuffer(const camera3_stream_buffer& buffer) const;
@@ -473,8 +475,10 @@
// prepareNextBuffer called on it.
bool mStreamUnpreparable;
+ uint64_t mUsage;
+
private:
- uint32_t mOldUsage;
+ uint64_t mOldUsage;
uint32_t mOldMaxBuffers;
Condition mOutputBufferReturnedSignal;
Condition mInputBufferReturnedSignal;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 869e93a..a0a50c2 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -39,7 +39,7 @@
namespace android {
status_t Camera3StreamSplitter::connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
+ uint64_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
ATRACE_CALL();
if (consumer == nullptr) {
SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
@@ -195,10 +195,8 @@
// Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
// We need skip these cases as timeout will disable the non-blocking (async) mode.
- int32_t usage = 0;
- static_cast<ANativeWindow*>(outputQueue.get())->query(
- outputQueue.get(),
- NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
+ uint64_t usage = 0;
+ res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(outputQueue.get()), &usage);
if (!(usage & (GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE))) {
outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
}
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index cc623e0..3b8839e 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -52,7 +52,7 @@
// Connect to the stream splitter by creating buffer queue and connecting it
// with output surfaces.
status_t connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t halMaxBuffers,
+ uint64_t consumerUsage, size_t halMaxBuffers,
sp<Surface>* consumer);
// addOutput adds an output BufferQueue to the splitter. The splitter
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index 3d54460..ee018c3 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -38,7 +38,7 @@
namespace android {
RingBufferConsumer::RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer,
- uint32_t consumerUsage,
+ uint64_t consumerUsage,
int bufferCount) :
ConsumerBase(consumer),
mBufferCount(bufferCount),
@@ -368,7 +368,7 @@
return mConsumer->setDefaultBufferFormat(defaultFormat);
}
-status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) {
+status_t RingBufferConsumer::setConsumerUsage(uint64_t usage) {
Mutex::Autolock _l(mMutex);
return mConsumer->setConsumerUsageBits(usage);
}
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index 2bafe4a..b737469 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -60,7 +60,7 @@
// the consumer usage flags passed to the graphics allocator. The
// bufferCount parameter specifies how many buffers can be pinned for user
// access at the same time.
- RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage,
+ RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount);
virtual ~RingBufferConsumer();
@@ -80,7 +80,7 @@
// setConsumerUsage allows the BufferQueue consumer usage to be
// set at a later time after construction.
- status_t setConsumerUsage(uint32_t usage);
+ status_t setConsumerUsage(uint64_t usage);
// Buffer info, minus the graphics buffer/slot itself.
struct BufferInfo {
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index f3bb35c..2836525 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -29,12 +29,15 @@
#include <unistd.h>
#include <string.h>
+#include <pwd.h>
#include <cutils/atomic.h>
#include <cutils/properties.h> // for property_get
#include <utils/misc.h>
+#include <android/content/pm/IPackageManagerNative.h>
+
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryHeapBase.h>
@@ -80,27 +83,26 @@
namespace android {
+ using namespace android::base;
+ using namespace android::content::pm;
+
// summarized records
-// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// up to 36 sets, each covering an hour -- so at least 1.5 days
// (will be longer if there are hours without any media action)
static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
-static const int kMaxRecordSets = 48;
-// individual records kept in memory
-static const int kMaxRecords = 100;
+static const int kMaxRecordSets = 36;
+// individual records kept in memory: age or count
+// age: <= 36 hours (1.5 days)
+// count: hard limit of # records
+// (0 for either of these disables that threshold)
+static const nsecs_t kMaxRecordAgeNs = 36 * 3600 * (1000*1000*1000ll);
+static const int kMaxRecords = 0;
static const char *kServiceName = "media.metrics";
-
-//using android::status_t;
-//using android::OK;
-//using android::BAD_VALUE;
-//using android::NOT_ENOUGH_DATA;
-//using android::Parcel;
-
-
void MediaAnalyticsService::instantiate() {
defaultServiceManager()->addService(
String16(kServiceName), new MediaAnalyticsService());
@@ -110,6 +112,7 @@
MediaAnalyticsService::SummarizerSet::SummarizerSet() {
mSummarizers = new List<MetricsSummarizer *>();
}
+
MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
// empty the list
List<MetricsSummarizer *> *l = mSummarizers;
@@ -153,8 +156,10 @@
MediaAnalyticsService::MediaAnalyticsService()
: mMaxRecords(kMaxRecords),
+ mMaxRecordAgeNs(kMaxRecordAgeNs),
mMaxRecordSets(kMaxRecordSets),
- mNewSetInterval(kNewSetIntervalNs) {
+ mNewSetInterval(kNewSetIntervalNs),
+ mDumpProto(MediaAnalyticsItem::PROTO_V0) {
ALOGD("MediaAnalyticsService created");
// clear our queues
@@ -167,6 +172,8 @@
mItemsSubmitted = 0;
mItemsFinalized = 0;
mItemsDiscarded = 0;
+ mItemsDiscardedExpire = 0;
+ mItemsDiscardedCount = 0;
mLastSessionID = 0;
// recover any persistency we set up
@@ -177,8 +184,23 @@
ALOGD("MediaAnalyticsService destroyed");
// clean out mOpen and mFinalized
+ while (mOpen->size() > 0) {
+ MediaAnalyticsItem * oitem = *(mOpen->begin());
+ mOpen->erase(mOpen->begin());
+ delete oitem;
+ mItemsDiscarded++;
+ mItemsDiscardedCount++;
+ }
delete mOpen;
mOpen = NULL;
+
+ while (mFinalized->size() > 0) {
+ MediaAnalyticsItem * oitem = *(mFinalized->begin());
+ mFinalized->erase(mFinalized->begin());
+ delete oitem;
+ mItemsDiscarded++;
+ mItemsDiscardedCount++;
+ }
delete mFinalized;
mFinalized = NULL;
@@ -212,13 +234,15 @@
//
bool isTrusted = false;
+ ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
+
switch (uid) {
case AID_MEDIA:
case AID_MEDIA_CODEC:
case AID_MEDIA_EX:
case AID_MEDIA_DRM:
// trusted source, only override default values
- isTrusted = true;
+ isTrusted = true;
if (uid_given == (-1)) {
item->setUid(uid);
}
@@ -233,6 +257,9 @@
break;
}
+ item->setPkgName(getPkgName(item->getUid(), true));
+ item->setPkgVersionCode(0);
+ ALOGV("info is from uid %d pkg '%s', version %d", item->getUid(), item->getPkgName().c_str(), item->getPkgVersionCode());
mItemsSubmitted++;
@@ -316,6 +343,7 @@
return id;
}
+
status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 512;
@@ -333,22 +361,41 @@
}
// crack any parameters
- bool clear = false;
- bool summary = false;
- nsecs_t ts_since = 0;
String16 summaryOption("-summary");
+ bool summary = false;
+ String16 protoOption("-proto");
String16 clearOption("-clear");
+ bool clear = false;
String16 sinceOption("-since");
+ nsecs_t ts_since = 0;
String16 helpOption("-help");
String16 onlyOption("-only");
- const char *only = NULL;
+ AString only;
int n = args.size();
+
for (int i = 0; i < n; i++) {
String8 myarg(args[i]);
if (args[i] == clearOption) {
clear = true;
} else if (args[i] == summaryOption) {
summary = true;
+ } else if (args[i] == protoOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ int proto = MediaAnalyticsItem::PROTO_V0; // default to original
+ char *endp;
+ const char *p = value.string();
+ proto = strtol(p, &endp, 10);
+ if (endp != p || *endp == '\0') {
+ if (proto < MediaAnalyticsItem::PROTO_FIRST) {
+ proto = MediaAnalyticsItem::PROTO_FIRST;
+ } else if (proto > MediaAnalyticsItem::PROTO_LAST) {
+ proto = MediaAnalyticsItem::PROTO_LAST;
+ }
+ mDumpProto = proto;
+ }
+ }
} else if (args[i] == sinceOption) {
i++;
if (i < n) {
@@ -368,18 +415,12 @@
i++;
if (i < n) {
String8 value(args[i]);
- const char *p = value.string();
- char *q = strdup(p);
- if (q != NULL) {
- if (only != NULL) {
- free((void*)only);
- }
- only = q;
- }
+ only = value.string();
}
} else if (args[i] == helpOption) {
result.append("Recognized parameters:\n");
result.append("-help this help message\n");
+ result.append("-proto X dump using protocol X (defaults to 1)");
result.append("-summary show summary info\n");
result.append("-clear clears out saved records\n");
result.append("-only X process records for component X\n");
@@ -398,14 +439,12 @@
dumpHeaders(result, ts_since);
- // only want 1, to avoid confusing folks that parse the output
+ // want exactly 1, to avoid confusing folks that parse the output
if (summary) {
- dumpSummaries(result, ts_since, only);
+ dumpSummaries(result, ts_since, only.c_str());
} else {
- dumpRecent(result, ts_since, only);
+ dumpRecent(result, ts_since, only.c_str());
}
- free((void*)only);
- only=NULL;
if (clear) {
@@ -430,6 +469,9 @@
const size_t SIZE = 512;
char buffer[SIZE];
+ snprintf(buffer, SIZE, "Protocol Version: %d\n", mDumpProto);
+ result.append(buffer);
+
int enabled = MediaAnalyticsItem::isEnabled();
if (enabled) {
snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -439,10 +481,14 @@
result.append(buffer);
snprintf(buffer, SIZE,
- "Since Boot: Submissions: %" PRId64
- " Finalizations: %" PRId64
- " Discarded: %" PRId64 "\n",
- mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
+ "Since Boot: Submissions: %8" PRId64
+ " Finalizations: %8" PRId64 "\n",
+ mItemsSubmitted, mItemsFinalized);
+ result.append(buffer);
+ snprintf(buffer, SIZE,
+ "Records Discarded: %8" PRId64
+ " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
+ mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
result.append(buffer);
snprintf(buffer, SIZE,
"Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
@@ -464,6 +510,10 @@
snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
result.append(buffer);
+ if (only != NULL && *only == '\0') {
+ only = NULL;
+ }
+
// have each of the distillers dump records
if (mSummarizerSets != NULL) {
List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
@@ -490,6 +540,10 @@
const size_t SIZE = 512;
char buffer[SIZE];
+ if (only != NULL && *only == '\0') {
+ only = NULL;
+ }
+
// show the recently recorded records
snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
result.append(buffer);
@@ -526,7 +580,7 @@
ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
continue;
}
- AString entry = (*it)->toString();
+ AString entry = (*it)->toString(mDumpProto);
result.appendFormat("%5d: %s\n", slot, entry.c_str());
slot++;
}
@@ -551,13 +605,32 @@
l->push_back(item);
}
- // keep removing old records the front until we're in-bounds
+ // keep removing old records the front until we're in-bounds (count)
if (mMaxRecords > 0) {
while (l->size() > (size_t) mMaxRecords) {
MediaAnalyticsItem * oitem = *(l->begin());
l->erase(l->begin());
delete oitem;
mItemsDiscarded++;
+ mItemsDiscardedCount++;
+ }
+ }
+
+ // keep removing old records the front until we're in-bounds (count)
+ if (mMaxRecordAgeNs > 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ while (l->size() > 0) {
+ MediaAnalyticsItem * oitem = *(l->begin());
+ nsecs_t when = oitem->getTimestamp();
+ // careful about timejumps too
+ if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
+ // this (and the rest) are recent enough to keep
+ break;
+ }
+ l->erase(l->begin());
+ delete oitem;
+ mItemsDiscarded++;
+ mItemsDiscardedExpire++;
}
}
}
@@ -720,4 +793,85 @@
}
+// mapping uids to package names
+
+// give me the package name, perhaps going to find it
+AString MediaAnalyticsService::getPkgName(uid_t uid, bool addIfMissing) {
+ ssize_t i = mPkgMappings.indexOfKey(uid);
+ if (i >= 0) {
+ AString pkg = mPkgMappings.valueAt(i);
+ ALOGV("returning pkg '%s' for uid %d", pkg.c_str(), uid);
+ return pkg;
+ }
+
+ AString pkg;
+
+ if (addIfMissing == false) {
+ return pkg;
+ }
+
+ struct passwd *pw = getpwuid(uid);
+ if (pw) {
+ pkg = pw->pw_name;
+ } else {
+ pkg = "-";
+ }
+
+ // find the proper value
+
+ sp<IBinder> binder = NULL;
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == NULL) {
+ ALOGE("defaultServiceManager failed");
+ } else {
+ binder = sm->getService(String16("package_native"));
+ if (binder == NULL) {
+ ALOGE("getService package_native failed");
+ }
+ }
+
+ if (binder != NULL) {
+ sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+
+ std::vector<int> uids;
+ std::vector<std::string> names;
+
+ uids.push_back(uid);
+
+ binder::Status status = package_mgr->getNamesForUids(uids, &names);
+ if (!status.isOk()) {
+ ALOGE("package_native::getNamesForUids failed: %s",
+ status.exceptionMessage().c_str());
+ } else {
+ if (!names[0].empty()) {
+ pkg = names[0].c_str();
+ }
+ }
+ }
+
+ // XXX determine whether package was side-loaded or from playstore.
+ // for privacy, we only list apps loaded from playstore.
+
+ // Sanitize the package name for ":"
+ // as an example, we get "shared:android.uid.systemui"
+ // replace : with something benign (I'm going to use !)
+ if (!pkg.empty()) {
+ int n = pkg.size();
+ char *p = (char *) pkg.c_str();
+ for (int i = 0 ; i < n; i++) {
+ if (p[i] == ':') {
+ p[i] = '!';
+ }
+ }
+ }
+
+ // add it to the map, to save a subsequent lookup
+ if (!pkg.empty()) {
+ ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
+ mPkgMappings.add(uid, pkg);
+ }
+
+ return pkg;
+}
+
} // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 6685967..4fe2fb2 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -54,6 +54,8 @@
int64_t mItemsSubmitted;
int64_t mItemsFinalized;
int64_t mItemsDiscarded;
+ int64_t mItemsDiscardedExpire;
+ int64_t mItemsDiscardedCount;
int64_t mSetsDiscarded;
MediaAnalyticsItem::SessionID_t mLastSessionID;
@@ -61,9 +63,12 @@
mutable Mutex mLock;
mutable Mutex mLock_ids;
- // the most we hold in memory
- // up to this many in each queue (open, finalized)
+ // limit how many records we'll retain
+ // by count (in each queue (open, finalized))
int32_t mMaxRecords;
+ // by time (none older than this long agan
+ nsecs_t mMaxRecordAgeNs;
+ //
// # of sets of summaries
int32_t mMaxRecordSets;
// nsecs until we start a new record set
@@ -118,6 +123,7 @@
void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
// support for generating output
+ int mDumpProto;
String8 dumpQueue(List<MediaAnalyticsItem*> *);
String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
@@ -125,6 +131,15 @@
void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
+ // mapping uids to package names
+ struct UidToPkgMap {
+ uid_t uid;
+ AString pkg;
+ };
+
+ KeyedVector<uid_t,AString> mPkgMappings;
+ AString getPkgName(uid_t uid, bool addIfMissing);
+
};
// ----------------------------------------------------------------------------
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
index 3477f1f..93fe0ec 100644
--- a/services/mediaanalytics/MetricsSummarizer.cpp
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -141,23 +141,23 @@
List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
for (; it != mSummaries->end(); it++) {
bool good = sameAttributes((*it), item, getIgnorables());
- ALOGV("Match against %s says %d",
- (*it)->toString().c_str(), good);
+ ALOGV("Match against %s says %d", (*it)->toString().c_str(), good);
if (good)
break;
}
if (it == mSummaries->end()) {
ALOGV("save new record");
- item = item->dup();
- if (item == NULL) {
+ MediaAnalyticsItem *nitem = item->dup();
+ if (nitem == NULL) {
ALOGE("unable to save MediaMetrics record");
}
- sortProps(item);
- item->setInt32("count",1);
- mSummaries->push_back(item);
+ sortProps(nitem);
+ nitem->setInt32("aggregated",1);
+ mergeRecord(*nitem, *item);
+ mSummaries->push_back(nitem);
} else {
ALOGV("increment existing record");
- (*it)->addInt32("count",1);
+ (*it)->addInt32("aggregated",1);
mergeRecord(*(*it), *item);
}
}
@@ -168,6 +168,71 @@
return;
}
+// keep some stats for things: sums, counts, standard deviation
+// the integer version -- all of these pieces are in 64 bits
+void MetricsSummarizer::minMaxVar64(MediaAnalyticsItem &summation, const char *key, int64_t value) {
+ if (key == NULL)
+ return;
+ int len = strlen(key) + 32;
+ char *tmpKey = (char *)malloc(len);
+
+ if (tmpKey == NULL) {
+ return;
+ }
+
+ // N - count of samples
+ snprintf(tmpKey, len, "%s.n", key);
+ summation.addInt64(tmpKey, 1);
+
+ // zero - count of samples that are zero
+ if (value == 0) {
+ snprintf(tmpKey, len, "%s.zero", key);
+ int64_t zero = 0;
+ (void) summation.getInt64(tmpKey,&zero);
+ zero++;
+ summation.setInt64(tmpKey, zero);
+ }
+
+ // min
+ snprintf(tmpKey, len, "%s.min", key);
+ int64_t min = value;
+ if (summation.getInt64(tmpKey,&min)) {
+ if (min > value) {
+ summation.setInt64(tmpKey, value);
+ }
+ } else {
+ summation.setInt64(tmpKey, value);
+ }
+
+ // max
+ snprintf(tmpKey, len, "%s.max", key);
+ int64_t max = value;
+ if (summation.getInt64(tmpKey,&max)) {
+ if (max < value) {
+ summation.setInt64(tmpKey, value);
+ }
+ } else {
+ summation.setInt64(tmpKey, value);
+ }
+
+ // components for mean, stddev;
+ // stddev = sqrt(1/4*(sumx2 - (2*sumx*sumx/n) + ((sumx/n)^2)))
+ // sum x
+ snprintf(tmpKey, len, "%s.sumX", key);
+ summation.addInt64(tmpKey, value);
+ // sum x^2
+ snprintf(tmpKey, len, "%s.sumX2", key);
+ summation.addInt64(tmpKey, value*value);
+
+
+ // last thing we do -- remove the base key from the summation
+ // record so we won't get confused about it having both individual
+ // and summary information in there.
+ summation.removeProp(key);
+
+ free(tmpKey);
+}
+
//
// Comparators
@@ -186,20 +251,23 @@
ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
+ // keep different sources/users separate
+ if (single->mUid != summ->mUid) {
+ return false;
+ }
+
// this can be made better.
for(size_t i=0;i<single->mPropCount;i++) {
MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
const char *attrName = prop1->mName;
- ALOGV("compare on attr '%s'", attrName);
// is it something we should ignore
if (ignorable != NULL) {
const char **ig = ignorable;
- while (*ig) {
+ for (;*ig; ig++) {
if (strcmp(*ig, attrName) == 0) {
break;
}
- ig++;
}
if (*ig) {
ALOGV("we don't mind that it has attr '%s'", attrName);
@@ -218,29 +286,42 @@
}
switch (prop1->mType) {
case MediaAnalyticsItem::kTypeInt32:
- if (prop1->u.int32Value != prop2->u.int32Value)
+ if (prop1->u.int32Value != prop2->u.int32Value) {
+ ALOGV("mismatch values");
return false;
+ }
break;
case MediaAnalyticsItem::kTypeInt64:
- if (prop1->u.int64Value != prop2->u.int64Value)
+ if (prop1->u.int64Value != prop2->u.int64Value) {
+ ALOGV("mismatch values");
return false;
+ }
break;
case MediaAnalyticsItem::kTypeDouble:
// XXX: watch out for floating point comparisons!
- if (prop1->u.doubleValue != prop2->u.doubleValue)
+ if (prop1->u.doubleValue != prop2->u.doubleValue) {
+ ALOGV("mismatch values");
return false;
+ }
break;
case MediaAnalyticsItem::kTypeCString:
- if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+ if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) {
+ ALOGV("mismatch values");
return false;
+ }
break;
case MediaAnalyticsItem::kTypeRate:
- if (prop1->u.rate.count != prop2->u.rate.count)
+ if (prop1->u.rate.count != prop2->u.rate.count) {
+ ALOGV("mismatch values");
return false;
- if (prop1->u.rate.duration != prop2->u.rate.duration)
+ }
+ if (prop1->u.rate.duration != prop2->u.rate.duration) {
+ ALOGV("mismatch values");
return false;
+ }
break;
default:
+ ALOGV("mismatch values in default type");
return false;
}
}
@@ -248,15 +329,6 @@
return true;
}
-bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
-
- // verify same user
- if (summ->mPid != single->mPid)
- return false;
-
- // and finally do the more expensive validation of the attributes
- return sameAttributes(summ, single, ignorable);
-}
int MetricsSummarizer::PropSorter(const void *a, const void *b) {
MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
@@ -267,14 +339,8 @@
// we sort in the summaries so that it looks pretty in the dumpsys
void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
if (item->mPropCount != 0) {
- if (DEBUG_SORT) {
- ALOGD("sortProps(pre): %s", item->toString().c_str());
- }
qsort(item->mProps, item->mPropCount,
sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
- if (DEBUG_SORT) {
- ALOGD("sortProps(pst): %s", item->toString().c_str());
- }
}
}
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
index 0b64eac..a9f0786 100644
--- a/services/mediaanalytics/MetricsSummarizer.h
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -59,10 +59,9 @@
// various comparators
// "do these records have same attributes and values in those attrs"
- // ditto, but watch for "error" fields
bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
- // attributes + from the same app/userid
- bool sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+
+ void minMaxVar64(MediaAnalyticsItem &summ, const char *key, int64_t value);
static int PropSorter(const void *a, const void *b);
void sortProps(MediaAnalyticsItem *item);
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
index 5162059..f882cb9 100644
--- a/services/mediaanalytics/MetricsSummarizerPlayer.cpp
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -51,37 +51,43 @@
setIgnorables(player_ignorable);
}
+// NB: this is also called for the first time -- so summation == item
+// Not sure if we need a flag for that or not.
+// In this particular mergeRecord() code -- we're' ok for this.
void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
ALOGV("MetricsSummarizerPlayer::mergeRecord()");
- //
- // we sum time & frames.
- // be careful about our special "-1" values that indicate 'unknown'
- // treat those as 0 [basically, not summing them into the totals].
+
int64_t duration = 0;
if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
ALOGV("found durationMs of %" PRId64, duration);
- summation.addInt64("android.media.mediaplayer.durationMs",duration);
+ minMaxVar64(summation, "android.media.mediaplayer.durationMs", duration);
}
+
int64_t playing = 0;
- if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+ if (item.getInt64("android.media.mediaplayer.playingMs", &playing)) {
ALOGV("found playingMs of %" PRId64, playing);
- if (playing >= 0) {
- summation.addInt64("android.media.mediaplayer.playingMs",playing);
- }
+ }
+ if (playing >= 0) {
+ minMaxVar64(summation,"android.media.mediaplayer.playingMs",playing);
+ }
+
int64_t frames = 0;
- if (item.getInt64("android.media.mediaplayer.frames", &frames))
+ if (item.getInt64("android.media.mediaplayer.frames", &frames)) {
ALOGV("found framess of %" PRId64, frames);
- if (frames >= 0) {
- summation.addInt64("android.media.mediaplayer.frames",frames);
- }
+ }
+ if (frames >= 0) {
+ minMaxVar64(summation,"android.media.mediaplayer.frames",frames);
+ }
+
int64_t dropped = 0;
- if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+ if (item.getInt64("android.media.mediaplayer.dropped", &dropped)) {
ALOGV("found dropped of %" PRId64, dropped);
- if (dropped >= 0) {
- summation.addInt64("android.media.mediaplayer.dropped",dropped);
- }
+ }
+ if (dropped >= 0) {
+ minMaxVar64(summation,"android.media.mediaplayer.dropped",dropped);
+ }
}
} // namespace android
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index ec2f5b9..f41219e 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -26,6 +26,10 @@
#include <utility/AAudioUtilities.h>
#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceEndpointMMAP.h"
+#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointPlay.h"
using namespace android;
using namespace aaudio;
@@ -34,159 +38,241 @@
AAudioEndpointManager::AAudioEndpointManager()
: Singleton<AAudioEndpointManager>()
- , mInputs()
- , mOutputs() {
+ , mSharedStreams()
+ , mExclusiveStreams() {
}
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";
- }
+ int index = 0;
result << "AAudioEndpointManager:" << "\n";
- size_t inputs = mInputs.size();
- result << "Input Endpoints: " << inputs << "\n";
- for (const auto &input : mInputs) {
- result << " Input: " << input->dump() << "\n";
+
+ const bool isSharedLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mSharedLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isSharedLocked) {
+ result << "AAudioEndpointManager Shared may be deadlocked\n";
}
- size_t outputs = mOutputs.size();
- result << "Output Endpoints: " << outputs << "\n";
- for (const auto &output : mOutputs) {
- result << " Output: " << output->dump() << "\n";
+ {
+ const bool isExclusiveLocked = AAudio_tryUntilTrue(
+ [this]() -> bool { return mExclusiveLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isExclusiveLocked) {
+ result << "AAudioEndpointManager Exclusive may be deadlocked\n";
+ }
+
+ result << "Exclusive MMAP Endpoints: " << mExclusiveStreams.size() << "\n";
+ index = 0;
+ for (const auto &output : mExclusiveStreams) {
+ result << " #" << index++ << ":";
+ result << output->dump() << "\n";
+ }
+
+ if (isExclusiveLocked) {
+ mExclusiveLock.unlock();
+ }
}
- if (isLocked) {
- mLock.unlock();
+ result << "Shared Endpoints: " << mSharedStreams.size() << "\n";
+ index = 0;
+ for (const auto &input : mSharedStreams) {
+ result << " #" << index++ << ":";
+ result << input->dump() << "\n";
+ }
+
+ if (isSharedLocked) {
+ mSharedLock.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.
+sp<AAudioServiceEndpoint> AAudioEndpointManager::findExclusiveEndpoint_l(
+ const AAudioStreamConfiguration &configuration) {
+ sp<AAudioServiceEndpoint> endpoint;
+ for (const auto ep : mExclusiveStreams) {
+ if (ep->matches(configuration)) {
+ endpoint = ep;
+ break;
+ }
+ }
+
+ ALOGD("AAudioEndpointManager.findExclusiveEndpoint_l(), found %p for device = %d",
+ endpoint.get(), configuration.getDeviceId());
+ return endpoint;
+}
+
+// Try to find an existing endpoint.
+sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l(
+ const AAudioStreamConfiguration &configuration) {
+ sp<AAudioServiceEndpointShared> endpoint;
+ for (const auto ep : mSharedStreams) {
+ if (ep->matches(configuration)) {
+ endpoint = ep;
+ break;
+ }
+ }
+
+ ALOGD("AAudioEndpointManager.findSharedEndpoint_l(), found %p for device = %d",
+ endpoint.get(), configuration.getDeviceId());
+ return endpoint;
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
+ const aaudio::AAudioStreamRequest &request,
+ aaudio_sharing_mode_t sharingMode) {
+ if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ return openExclusiveEndpoint(audioService, request);
+ } else {
+ return openSharedEndpoint(audioService, request);
+ }
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
+ AAudioService &aaudioService __unused,
+ const aaudio::AAudioStreamRequest &request) {
+
+ std::lock_guard<std::mutex> lock(mExclusiveLock);
+
+ const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
// Try to find an existing endpoint.
+ sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration);
+ // If we find an existing one then this one cannot be exclusive.
+ if (endpoint.get() != nullptr) {
+ ALOGE("AAudioEndpointManager.openExclusiveEndpoint() already in use");
+ // Already open so do not allow a second stream.
+ return nullptr;
+ } else {
+ sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP();
+ ALOGE("AAudioEndpointManager.openEndpoint(),created MMAP %p", endpointMMap.get());
+ endpoint = endpointMMap;
-
- switch (direction) {
- case AAUDIO_DIRECTION_INPUT:
- for (AAudioServiceEndpoint *ep : mInputs) {
- if (ep->matches(configuration)) {
- endpoint = ep;
- break;
- }
- }
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- 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, configuration.getDeviceId(), (int)direction);
-
- // If we can't find an existing one then open a new one.
- if (endpoint == nullptr) {
- // 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;
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- player = new AAudioServiceEndpointPlay(audioService);
- endpoint = player;
- break;
- default:
- break;
+ aaudio_result_t result = endpoint->open(request);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+ endpoint.clear();
+ } else {
+ mExclusiveStreams.push_back(endpointMMap);
}
- 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);
+ ALOGD("AAudioEndpointManager.openEndpoint(), created %p for device = %d",
+ endpoint.get(), configuration.getDeviceId());
}
- if (endpoint != nullptr) {
- ALOGD("AAudioEndpointManager::openEndpoint(), sampleRate = %d, framesPerBurst = %d",
- endpoint->getSampleRate(), endpoint->getFramesPerBurst());
+ if (endpoint.get() != nullptr) {
// Increment the reference count under this lock.
- endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+ endpoint->setOpenCount(endpoint->getOpenCount() + 1);
}
return endpoint;
}
-void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
- std::lock_guard<std::mutex> lock(mLock);
- if (serviceEndpoint == nullptr) {
- return;
- }
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openSharedEndpoint(
+ AAudioService &aaudioService,
+ const aaudio::AAudioStreamRequest &request) {
- // Decrement the reference count under this lock.
- int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
- serviceEndpoint->setReferenceCount(newRefCount);
- ALOGD("AAudioEndpointManager::closeEndpoint(%p) newRefCount = %d",
- serviceEndpoint, newRefCount);
+ std::lock_guard<std::mutex> lock(mSharedLock);
- // If no longer in use then close and delete it.
- if (newRefCount <= 0) {
- aaudio_direction_t direction = serviceEndpoint->getDirection();
- // Track endpoints based on requested deviceId because UNSPECIFIED
- // can change to a specific device after opening.
- int32_t deviceId = serviceEndpoint->getRequestedDeviceId();
+ const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+ aaudio_direction_t direction = configuration.getDirection();
+ // Try to find an existing endpoint.
+ sp<AAudioServiceEndpointShared> endpoint = findSharedEndpoint_l(configuration);
+
+ // If we can't find an existing one then open a new one.
+ if (endpoint.get() == nullptr) {
+ // we must call openStream with audioserver identity
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
- mInputs.erase(
- std::remove(mInputs.begin(), mInputs.end(), serviceEndpoint), mInputs.end());
+ endpoint = new AAudioServiceEndpointCapture(aaudioService);
break;
case AAUDIO_DIRECTION_OUTPUT:
- mOutputs.erase(
- std::remove(mOutputs.begin(), mOutputs.end(), serviceEndpoint), mOutputs.end());
+ endpoint = new AAudioServiceEndpointPlay(aaudioService);
break;
default:
break;
}
+ if (endpoint.get() != nullptr) {
+ aaudio_result_t result = endpoint->open(request);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+ endpoint.clear();
+ } else {
+ mSharedStreams.push_back(endpoint);
+ }
+ }
+ ALOGD("AAudioEndpointManager.openSharedEndpoint(), created %p for device = %d, dir = %d",
+ endpoint.get(), configuration.getDeviceId(), (int)direction);
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ }
+
+ if (endpoint.get() != nullptr) {
+ // Increment the reference count under this lock.
+ endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+ }
+ return endpoint;
+}
+
+void AAudioEndpointManager::closeEndpoint(sp<AAudioServiceEndpoint>serviceEndpoint) {
+ if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ return closeExclusiveEndpoint(serviceEndpoint);
+ } else {
+ return closeSharedEndpoint(serviceEndpoint);
+ }
+}
+
+void AAudioEndpointManager::closeExclusiveEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+ if (serviceEndpoint.get() == nullptr) {
+ return;
+ }
+
+ // Decrement the reference count under this lock.
+ std::lock_guard<std::mutex> lock(mExclusiveLock);
+ int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+ serviceEndpoint->setOpenCount(newRefCount);
+ ALOGD("AAudioEndpointManager::closeExclusiveEndpoint(%p) newRefCount = %d",
+ serviceEndpoint.get(), newRefCount);
+
+ // If no longer in use then close and delete it.
+ if (newRefCount <= 0) {
+ mExclusiveStreams.erase(
+ std::remove(mExclusiveStreams.begin(), mExclusiveStreams.end(), serviceEndpoint),
+ mExclusiveStreams.end());
+
serviceEndpoint->close();
- ALOGD("AAudioEndpointManager::closeEndpoint() delete %p for device %d, dir = %d",
- serviceEndpoint, deviceId, (int)direction);
- delete serviceEndpoint;
+ ALOGD("AAudioEndpointManager::closeExclusiveEndpoint() %p for device %d",
+ serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+ }
+}
+
+void AAudioEndpointManager::closeSharedEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+ if (serviceEndpoint.get() == nullptr) {
+ return;
+ }
+
+ // Decrement the reference count under this lock.
+ std::lock_guard<std::mutex> lock(mSharedLock);
+ int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+ serviceEndpoint->setOpenCount(newRefCount);
+ ALOGD("AAudioEndpointManager::closeSharedEndpoint(%p) newRefCount = %d",
+ serviceEndpoint.get(), newRefCount);
+
+ // If no longer in use then close and delete it.
+ if (newRefCount <= 0) {
+ mSharedStreams.erase(
+ std::remove(mSharedStreams.begin(), mSharedStreams.end(), serviceEndpoint),
+ mSharedStreams.end());
+
+ serviceEndpoint->close();
+ ALOGD("AAudioEndpointManager::closeSharedEndpoint() %p for device %d",
+ serviceEndpoint.get(), serviceEndpoint->getDeviceId());
}
}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 2511b2f..32c8454 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -24,11 +24,12 @@
#include "binding/AAudioServiceMessage.h"
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointMMAP.h"
#include "AAudioServiceEndpointPlay.h"
namespace aaudio {
-class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager>{
+class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager> {
public:
AAudioEndpointManager();
~AAudioEndpointManager() = default;
@@ -49,22 +50,42 @@
* Find a service endpoint for the given deviceId and direction.
* If an endpoint does not already exist then try to create one.
*
- * @param deviceId
- * @param direction
- * @return endpoint or nullptr
+ * @param audioService
+ * @param request
+ * @param sharingMode
+ * @return endpoint or null
*/
- AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
- const AAudioStreamConfiguration& configuration,
- aaudio_direction_t direction);
+ android::sp<AAudioServiceEndpoint> openEndpoint(android::AAudioService &audioService,
+ const aaudio::AAudioStreamRequest &request,
+ aaudio_sharing_mode_t sharingMode);
- void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+ void closeEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
private:
+ android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
+ const aaudio::AAudioStreamRequest &request);
- mutable std::mutex mLock;
+ android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
+ const aaudio::AAudioStreamRequest &request);
- std::vector<AAudioServiceEndpointCapture *> mInputs;
- std::vector<AAudioServiceEndpointPlay *> mOutputs;
+ android::sp<AAudioServiceEndpoint> findExclusiveEndpoint_l(
+ const AAudioStreamConfiguration& configuration);
+
+ android::sp<AAudioServiceEndpointShared> findSharedEndpoint_l(
+ const AAudioStreamConfiguration& configuration);
+
+ void closeExclusiveEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+ void closeSharedEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+
+ // Use separate locks because opening a Shared endpoint requires opening an Exclusive one.
+ // That could cause a recursive lock.
+ // Lock mSharedLock before mExclusiveLock.
+ // it is OK to only lock mExclusiveLock.
+ mutable std::mutex mSharedLock;
+ std::vector<android::sp<AAudioServiceEndpointShared>> mSharedStreams;
+
+ mutable std::mutex mExclusiveLock;
+ std::vector<android::sp<AAudioServiceEndpointMMAP>> mExclusiveStreams;
};
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 8222734..952aa82 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -88,11 +88,11 @@
}
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 */
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 3992719..855ae69 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -114,14 +114,12 @@
mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
inService = request.isInService();
}
- serviceStream = new AAudioServiceStreamMMAP(mAudioClient, inService);
- result = serviceStream->open(request, configurationOutput);
+ serviceStream = new AAudioServiceStreamMMAP(*this, inService);
+ result = serviceStream->open(request);
if (result != AAUDIO_OK) {
- // fall back to using a shared stream
+ // Clear it so we can possibly fall back to using a shared stream.
ALOGW("AAudioService::openStream(), could not open in EXCLUSIVE mode");
serviceStream.clear();
- } else {
- configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
}
}
@@ -129,8 +127,7 @@
if (sharingMode == AAUDIO_SHARING_MODE_SHARED
|| (serviceStream == nullptr && !sharingModeMatchRequired)) {
serviceStream = new AAudioServiceStreamShared(*this);
- result = serviceStream->open(request, configurationOutput);
- configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+ result = serviceStream->open(request);
}
if (result != AAUDIO_OK) {
@@ -149,6 +146,7 @@
serviceStream->setHandle(handle);
pid_t pid = request.getProcessId();
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
+ configurationOutput.copyFrom(*serviceStream);
}
return handle;
}
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 8421efc..ffaf538 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -28,7 +28,9 @@
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceInterface.h"
-#include "AAudioServiceStreamBase.h"
+namespace aaudio {
+ class AAudioServiceStreamBase;
+};
namespace android {
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 0f863fe..cba5bc8 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -33,17 +33,14 @@
#include "core/AudioStreamBuilder.h"
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
-#define MIN_TIMEOUT_NANOS (1000 * AAUDIO_NANOS_PER_MILLISECOND)
-
-// Wait at least this many times longer than the operation should take.
-#define MIN_TIMEOUT_OPERATIONS 4
-
-// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
-#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+AAudioServiceEndpoint::~AAudioServiceEndpoint() {
+ ALOGD("AAudioServiceEndpoint::~AAudioServiceEndpoint() destroying endpoint %p", this);
+}
std::string AAudioServiceEndpoint::dump() const {
std::stringstream result;
@@ -53,19 +50,20 @@
50 /* times */,
20 /* sleepMs */);
if (!isLocked) {
- result << "EndpointManager may be deadlocked\n";
+ result << "AAudioServiceEndpoint 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();
+ result << " Direction: " << ((getDirection() == AAUDIO_DIRECTION_OUTPUT)
+ ? "OUTPUT" : "INPUT") << "\n";
+ result << " Sample Rate: " << getSampleRate() << "\n";
+ result << " Frames Per Burst: " << mFramesPerBurst << "\n";
+ result << " Reference Count: " << mOpenCount << "\n";
+ result << " Requested Device Id: " << mRequestedDeviceId << "\n";
+ result << " Device Id: " << getDeviceId() << "\n";
+ result << " Registered Streams: " << "\n";
+ result << AAudioServiceStreamShared::dumpHeader() << "\n";
+ for (const auto stream : mRegisteredStreams) {
+ result << stream->dump() << "\n";
}
if (isLocked) {
@@ -74,110 +72,44 @@
return result.str();
}
-// Set up an EXCLUSIVE MMAP stream that will be shared.
-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(mRequestedDeviceId);
- builder.setFormat(configuration.getFormat());
- builder.setSampleRate(configuration.getSampleRate());
- builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
- builder.setDirection(getDirection());
- builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
-
- return getStreamInternal()->open(builder);
-}
-
-aaudio_result_t AAudioServiceEndpoint::close() {
- return getStreamInternal()->close();
-}
-
-// TODO, maybe use an interface to reduce exposure
-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(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(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 result;
-}
-
-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;
-}
-
-static void *aaudio_endpoint_thread_proc(void *context) {
- AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
- if (endpoint != NULL) {
- return endpoint->callbackLoop();
- } else {
- return NULL;
- }
-}
-
-aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
- // Launch the callback loop thread.
- int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
- * AAUDIO_NANOS_PER_SECOND
- / getSampleRate();
- mCallbackEnabled.store(true);
- return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
-}
-
-aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
- mCallbackEnabled.store(false);
- aaudio_result_t result = getStreamInternal()->joinThread(NULL);
- return result;
-}
-
void AAudioServiceEndpoint::disconnectRegisteredStreams() {
std::lock_guard<std::mutex> lock(mLockStreams);
- for(auto sharedStream : mRegisteredStreams) {
- sharedStream->stop();
- sharedStream->disconnect();
+ for (const auto stream : mRegisteredStreams) {
+ stream->stop();
+ stream->disconnect();
}
mRegisteredStreams.clear();
}
+aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
+ std::lock_guard<std::mutex> lock(mLockStreams);
+ mRegisteredStreams.push_back(stream);
+ return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamBase>stream) {
+ std::lock_guard<std::mutex> lock(mLockStreams);
+ mRegisteredStreams.erase(std::remove(
+ mRegisteredStreams.begin(), mRegisteredStreams.end(), stream),
+ mRegisteredStreams.end());
+ return AAUDIO_OK;
+}
+
bool AAudioServiceEndpoint::matches(const AAudioStreamConfiguration& configuration) {
+ if (configuration.getDirection() != getDirection()) {
+ return false;
+ }
if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
- configuration.getDeviceId() != mStreamInternal->getDeviceId()) {
+ configuration.getDeviceId() != getDeviceId()) {
return false;
}
if (configuration.getSampleRate() != AAUDIO_UNSPECIFIED &&
- configuration.getSampleRate() != mStreamInternal->getSampleRate()) {
+ configuration.getSampleRate() != getSampleRate()) {
return false;
}
if (configuration.getSamplesPerFrame() != AAUDIO_UNSPECIFIED &&
- configuration.getSamplesPerFrame() != mStreamInternal->getSamplesPerFrame()) {
+ configuration.getSamplesPerFrame() != getSamplesPerFrame()) {
return false;
}
-
return true;
}
-
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index e40a670..2ef6234 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -24,70 +24,93 @@
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalPlay.h"
+#include "core/AAudioStreamParameters.h"
#include "binding/AAudioServiceMessage.h"
-#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceStreamMMAP.h"
-#include "AAudioMixer.h"
-#include "AAudioService.h"
+#include "binding/AAudioStreamConfiguration.h"
+
+#include "AAudioServiceStreamBase.h"
namespace aaudio {
-class AAudioServiceEndpoint {
+/**
+ * AAudioServiceEndpoint is used by a subclass of AAudioServiceStreamBase
+ * to communicate with the underlying audio device or port.
+ */
+class AAudioServiceEndpoint
+ : public virtual android::RefBase
+ , public AAudioStreamParameters {
public:
- virtual ~AAudioServiceEndpoint() = default;
- std::string dump() const;
+ virtual ~AAudioServiceEndpoint();
- virtual aaudio_result_t open(const AAudioStreamConfiguration& configuration);
+ virtual std::string dump() const;
- int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
- int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame(); }
- int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst(); }
+ virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
- 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();
+ virtual aaudio_result_t close() = 0;
+
+ virtual aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
+
+ virtual aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
+
+ virtual aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t *clientHandle) = 0;
+
+ virtual aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t clientHandle) = 0;
+
+ virtual aaudio_result_t startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ ALOGD("AAudioServiceEndpoint::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle) {
+ ALOGD("AAudioServiceEndpoint::stopClient(...) AAUDIO_ERROR_UNAVAILABLE");
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ /**
+ * @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 getTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+ int32_t getFramesPerBurst() const {
+ return mFramesPerBurst;
+ }
int32_t getRequestedDeviceId() const { return mRequestedDeviceId; }
- int32_t getDeviceId() const { return mStreamInternal->getDeviceId(); }
-
- aaudio_direction_t getDirection() const { return mStreamInternal->getDirection(); }
-
- void disconnectRegisteredStreams();
-
- virtual void *callbackLoop() = 0;
-
- // This should only be called from the AAudioEndpointManager under a mutex.
- int32_t getReferenceCount() const {
- return mReferenceCount;
- }
-
- // This should only be called from the AAudioEndpointManager under a mutex.
- void setReferenceCount(int32_t count) {
- mReferenceCount = count;
- }
bool matches(const AAudioStreamConfiguration& configuration);
- virtual AudioStreamInternal *getStreamInternal() = 0;
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ int32_t getOpenCount() const {
+ return mOpenCount;
+ }
- std::atomic<bool> mCallbackEnabled{false};
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ void setOpenCount(int32_t count) {
+ mOpenCount = count;
+ }
+
+protected:
+ void disconnectRegisteredStreams();
mutable std::mutex mLockStreams;
+ std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
- std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
- std::atomic<int> mRunningStreams{0};
+ android::AudioClient mMmapClient; // set in open, used in open and startStream
-private:
- aaudio_result_t startSharingThread_l();
- aaudio_result_t stopSharingThread();
-
- AudioStreamInternal *mStreamInternal = nullptr;
- int32_t mReferenceCount = 0;
+ int32_t mFramesPerBurst = 0;
+ int32_t mOpenCount = 0;
int32_t mRequestedDeviceId = 0;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 6a37330..97558ca 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -30,20 +30,22 @@
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService &audioService)
: mStreamInternalCapture(audioService, true) {
+ mStreamInternal = &mStreamInternalCapture;
}
AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
delete mDistributionBuffer;
}
-aaudio_result_t AAudioServiceEndpointCapture::open(const AAudioStreamConfiguration& configuration) {
- aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointCapture::open(const aaudio::AAudioStreamRequest &request) {
+ aaudio_result_t result = AAudioServiceEndpointShared::open(request);
if (result == AAUDIO_OK) {
delete mDistributionBuffer;
int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
@@ -62,6 +64,9 @@
// result might be a frame count
while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
+
+ int64_t mmapFramesRead = getStreamInternal()->getFramesRead();
+
// Read audio data from stream using a blocking read.
result = getStreamInternal()->read(mDistributionBuffer, getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -74,18 +79,35 @@
}
// Distribute data to each active stream.
- { // use lock guard
+ { // brackets are for lock_guard
+
std::lock_guard <std::mutex> lock(mLockStreams);
- for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
- if (sharedStream->isRunning()) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ for (const auto clientStream : mRegisteredStreams) {
+ if (clientStream->isRunning()) {
+ AAudioServiceStreamShared *streamShared =
+ static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+ FifoBuffer *fifo = streamShared->getDataFifoBuffer();
+
+ // Determine offset between framePosition in client's stream vs the underlying
+ // MMAP stream.
+ int64_t clientFramesWritten = fifo->getWriteCounter();
+ // There are two indices that refer to the same frame.
+ int64_t positionOffset = mmapFramesRead - clientFramesWritten;
+ streamShared->setTimestampPositionOffset(positionOffset);
+
if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
getFramesPerBurst()) {
underflowCount++;
} else {
fifo->write(mDistributionBuffer, getFramesPerBurst());
}
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
+
+ // This timestamp represents the completion of data being written into the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when data will be available to read.
+ Timestamp timestamp(fifo->getWriteCounter(), AudioClock::getNanoseconds());
+ streamShared->markTransferTime(timestamp);
}
}
}
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 8a3d72f..971da9a 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -20,18 +20,18 @@
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalCapture.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+
namespace aaudio {
-class AAudioServiceEndpointCapture : public AAudioServiceEndpoint {
+class AAudioServiceEndpointCapture : public AAudioServiceEndpointShared {
public:
explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
virtual ~AAudioServiceEndpointCapture();
- aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
- AudioStreamInternal *getStreamInternal() override {
- return &mStreamInternalCapture;
- }
void *callbackLoop() override;
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
new file mode 100644
index 0000000..58213f8
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -0,0 +1,350 @@
+/*
+ * 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 "AAudioServiceEndpointMMAP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <sstream>
+#include <utils/Singleton.h>
+#include <vector>
+
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointMMAP.h"
+
+
+#define AAUDIO_BUFFER_CAPACITY_MIN 4 * 512
+#define AAUDIO_SAMPLE_RATE_DEFAULT 48000
+
+// This is an estimate of the time difference between the HW and the MMAP time.
+// TODO Get presentation timestamps from the HAL instead of using these estimates.
+#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (3 * AAUDIO_NANOS_PER_MILLISECOND)
+#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (-1 * AAUDIO_NANOS_PER_MILLISECOND)
+
+using namespace android; // TODO just import names needed
+using namespace aaudio; // TODO just import names needed
+
+AAudioServiceEndpointMMAP::AAudioServiceEndpointMMAP()
+ : mMmapStream(nullptr) {}
+
+AAudioServiceEndpointMMAP::~AAudioServiceEndpointMMAP() {}
+
+std::string AAudioServiceEndpointMMAP::dump() const {
+ std::stringstream result;
+
+ result << " MMAP: framesTransferred = " << mFramesTransferred.get();
+ result << ", HW nanos = " << mHardwareTimeOffsetNanos;
+ result << ", port handle = " << mPortHandle;
+ result << ", audio data FD = " << mAudioDataFileDescriptor;
+ result << "\n";
+
+ result << " HW Offset Micros: " <<
+ (getHardwareTimeOffsetNanos()
+ / AAUDIO_NANOS_PER_MICROSECOND) << "\n";
+
+ result << AAudioServiceEndpoint::dump();
+ return result.str();
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
+ aaudio_result_t result = AAUDIO_OK;
+ const audio_attributes_t attributes = {
+ .content_type = AUDIO_CONTENT_TYPE_MUSIC,
+ .usage = AUDIO_USAGE_MEDIA,
+ .source = AUDIO_SOURCE_VOICE_RECOGNITION,
+ .flags = AUDIO_FLAG_LOW_LATENCY,
+ .tags = ""
+ };
+ audio_config_base_t config;
+ audio_port_handle_t deviceId;
+
+ int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
+ int32_t burstMicros = 0;
+
+ copyFrom(request.getConstantConfiguration());
+
+ mMmapClient.clientUid = request.getUserId();
+ mMmapClient.clientPid = request.getProcessId();
+ mMmapClient.packageName.setTo(String16(""));
+
+ mRequestedDeviceId = deviceId = getDeviceId();
+
+ // Fill in config
+ aaudio_format_t aaudioFormat = getFormat();
+ if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+ }
+ config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
+
+ int32_t aaudioSampleRate = getSampleRate();
+ if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
+ aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
+ }
+ config.sample_rate = aaudioSampleRate;
+
+ int32_t aaudioSamplesPerFrame = getSamplesPerFrame();
+
+ aaudio_direction_t direction = getDirection();
+ if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+ ? AUDIO_CHANNEL_OUT_STEREO
+ : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
+ mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
+
+ } else if (direction == AAUDIO_DIRECTION_INPUT) {
+ config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+ ? AUDIO_CHANNEL_IN_STEREO
+ : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
+ mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
+
+ } else {
+ ALOGE("openMmapStream - invalid direction = %d", direction);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ }
+
+ MmapStreamInterface::stream_direction_t streamDirection =
+ (direction == AAUDIO_DIRECTION_OUTPUT)
+ ? MmapStreamInterface::DIRECTION_OUTPUT
+ : MmapStreamInterface::DIRECTION_INPUT;
+
+ // Open HAL stream. Set mMmapStream
+ status_t status = MmapStreamInterface::openMmapStream(streamDirection,
+ &attributes,
+ &config,
+ mMmapClient,
+ &deviceId,
+ this, // callback
+ mMmapStream,
+ &mPortHandle);
+ ALOGD("AAudioServiceEndpointMMAP::open() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
+ mMmapClient.clientUid, mMmapClient.clientPid, mPortHandle);
+ if (status != OK) {
+ ALOGE("openMmapStream returned status %d", status);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ if (deviceId == AAUDIO_UNSPECIFIED) {
+ ALOGW("AAudioServiceEndpointMMAP::open() - openMmapStream() failed to set deviceId");
+ }
+ setDeviceId(deviceId);
+
+ // Create MMAP/NOIRQ buffer.
+ int32_t minSizeFrames = getBufferCapacity();
+ if (minSizeFrames <= 0) { // zero will get rejected
+ minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
+ }
+ status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
+ if (status != OK) {
+ ALOGE("AAudioServiceEndpointMMAP::open() - createMmapBuffer() failed with status %d %s",
+ status, strerror(-status));
+ result = AAUDIO_ERROR_UNAVAILABLE;
+ goto error;
+ } else {
+ 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");
+ }
+
+ setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
+ // The audio HAL indicates if the shared memory fd can be shared outside of audioserver
+ // by returning a negative buffer size
+ if (getBufferCapacity() < 0) {
+ // Exclusive mode can be used by client or service.
+ setBufferCapacity(-getBufferCapacity());
+ } else {
+ // Exclusive mode can only be used by the service because the FD cannot be shared.
+ uid_t audioServiceUid = getuid();
+ if ((mMmapClient.clientUid != audioServiceUid) &&
+ getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ // Fallback is handled by caller but indicate what is possible in case
+ // this is used in the future
+ setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+ ALOGW("AAudioServiceEndpointMMAP::open() - exclusive FD cannot be used by client");
+ result = AAUDIO_ERROR_UNAVAILABLE;
+ goto error;
+ }
+ }
+
+ // Get information about the stream and pass it back to the caller.
+ setSamplesPerFrame((direction == AAUDIO_DIRECTION_OUTPUT)
+ ? audio_channel_count_from_out_mask(config.channel_mask)
+ : audio_channel_count_from_in_mask(config.channel_mask));
+
+ // 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("AAudioServiceEndpointMMAP::open() - could not dup shared_memory_fd");
+ result = AAUDIO_ERROR_INTERNAL;
+ goto error;
+ }
+ mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
+ setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
+ setSampleRate(config.sample_rate);
+
+ // Scale up the burst size to meet the minimum equivalent in microseconds.
+ // This is to avoid waking the CPU too often when the HW burst is very small
+ // or at high sample rates.
+ do {
+ if (burstMicros > 0) { // skip first loop
+ mFramesPerBurst *= 2;
+ }
+ burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
+ } while (burstMicros < burstMinMicros);
+
+ ALOGD("AAudioServiceEndpointMMAP::open() original burst = %d, minMicros = %d, to burst = %d\n",
+ mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+
+ ALOGD("AAudioServiceEndpointMMAP::open() actual rate = %d, channels = %d"
+ ", deviceId = %d, capacity = %d\n",
+ getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
+
+ return result;
+
+error:
+ close();
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::close() {
+
+ if (mMmapStream != 0) {
+ ALOGD("AAudioServiceEndpointMMAP::close() clear() endpoint");
+ // Needs to be explicitly cleared or CTS will fail but it is not clear why.
+ mMmapStream.clear();
+ // 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 AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t *clientHandle) {
+ return startClient(mMmapClient, &mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopStream(sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t clientHandle) {
+ mFramesTransferred.reset32();
+ return stopClient(mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+ audio_port_handle_t originalHandle = *clientHandle;
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
+ clientHandle));
+ ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
+ &client, client.clientUid, client.clientPid,
+ originalHandle, *clientHandle, result);
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopClient(audio_port_handle_t clientHandle) {
+ if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+ ALOGD("AAudioServiceEndpointMMAP::stopClient(%d) returns %d", clientHandle, result);
+ return result;
+}
+
+// Get free-running DSP or DMA hardware position from the HAL.
+aaudio_result_t AAudioServiceEndpointMMAP::getFreeRunningPosition(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ struct audio_mmap_position position;
+ if (mMmapStream == nullptr) {
+ return AAUDIO_ERROR_NULL;
+ }
+ status_t status = mMmapStream->getMmapPosition(&position);
+ ALOGV("AAudioServiceEndpointMMAP::getFreeRunningPosition() status= %d, pos = %d, nanos = %lld\n",
+ status, position.position_frames, (long long) position.time_nanoseconds);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+ if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data available");
+ } else if (result != AAUDIO_OK) {
+ ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
+ } else {
+ // Convert 32-bit position to 64-bit position.
+ mFramesTransferred.update32(position.position_frames);
+ *positionFrames = mFramesTransferred.get();
+ *timeNanos = position.time_nanoseconds;
+ }
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::getTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ return 0; // TODO
+}
+
+
+void AAudioServiceEndpointMMAP::onTearDown() {
+ ALOGD("AAudioServiceEndpointMMAP::onTearDown() called");
+ disconnectRegisteredStreams();
+};
+
+void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
+ android::Vector<float> values) {
+ // TODO do we really need a different volume for each channel?
+ float volume = values[0];
+ ALOGD("AAudioServiceEndpointMMAP::onVolumeChanged() volume[0] = %f", volume);
+ std::lock_guard<std::mutex> lock(mLockStreams);
+ for(const auto stream : mRegisteredStreams) {
+ stream->onVolumeChanged(volume);
+ }
+};
+
+void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
+ ALOGD("AAudioServiceEndpointMMAP::onRoutingChanged() called with %d, old = %d",
+ deviceId, getDeviceId());
+ if (getDeviceId() != AUDIO_PORT_HANDLE_NONE && getDeviceId() != deviceId) {
+ disconnectRegisteredStreams();
+ }
+ setDeviceId(deviceId);
+};
+
+/**
+ * Get an immutable description of the data queue from the HAL.
+ */
+aaudio_result_t AAudioServiceEndpointMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
+{
+ // Gather information on the data queue based on HAL info.
+ int32_t bytesPerFrame = calculateBytesPerFrame();
+ int32_t capacityInBytes = getBufferCapacity() * bytesPerFrame;
+ int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
+ parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
+ parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
+ parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
+ parcelable.mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
+ return AAUDIO_OK;
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
new file mode 100644
index 0000000..16b6269
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -0,0 +1,102 @@
+/*
+ * 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_SERVICE_ENDPOINT_MMAP_H
+#define AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This is used by AAudioServiceStreamMMAP to access the MMAP devices
+ * through AudioFlinger.
+ */
+class AAudioServiceEndpointMMAP
+ : public AAudioServiceEndpoint
+ , public android::MmapStreamCallback {
+
+public:
+ AAudioServiceEndpointMMAP();
+
+ virtual ~AAudioServiceEndpointMMAP();
+
+ std::string dump() const override;
+
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+ aaudio_result_t close() override;
+
+ aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t *clientHandle) override;
+
+ aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t clientHandle) override;
+
+ aaudio_result_t startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) override;
+
+ aaudio_result_t stopClient(audio_port_handle_t clientHandle) override;
+
+ aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+ aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+ // -------------- Callback functions for MmapStreamCallback ---------------------
+ void onTearDown() override;
+
+ void onVolumeChanged(audio_channel_mask_t channels,
+ android::Vector<float> values) override;
+
+ void onRoutingChanged(audio_port_handle_t deviceId) override;
+ // ------------------------------------------------------------------------------
+
+ aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable);
+
+ int64_t getHardwareTimeOffsetNanos() const {
+ return mHardwareTimeOffsetNanos;
+ }
+
+private:
+ MonotonicCounter mFramesTransferred;
+
+ // Interface to the AudioFlinger MMAP support.
+ android::sp<android::MmapStreamInterface> mMmapStream;
+ struct audio_mmap_buffer_info mMmapBufferinfo;
+ audio_port_handle_t mPortHandle = AUDIO_PORT_HANDLE_NONE;
+
+ android::base::unique_fd mAudioDataFileDescriptor;
+
+ int64_t mHardwareTimeOffsetNanos = 0; // TODO get from HAL
+
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index b83b918..c42a6e2 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceEndpointPlay"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -33,6 +33,7 @@
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
@@ -41,13 +42,14 @@
AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
: mStreamInternalPlay(audioService, true) {
+ mStreamInternal = &mStreamInternalPlay;
}
AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
}
-aaudio_result_t AAudioServiceEndpointPlay::open(const AAudioStreamConfiguration& configuration) {
- aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
+ aaudio_result_t result = AAudioServiceEndpointShared::open(request);
if (result == AAUDIO_OK) {
mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
getStreamInternal()->getFramesPerBurst());
@@ -65,7 +67,6 @@
// Mix data from each application stream and write result to the shared MMAP stream.
void *AAudioServiceEndpointPlay::callbackLoop() {
- int32_t underflowCount = 0;
aaudio_result_t result = AAUDIO_OK;
int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
@@ -73,19 +74,42 @@
while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
// Mix data from each active stream.
mMixer.clear();
- { // use lock guard
+
+ { // brackets are for lock_guard
int index = 0;
+ int64_t mmapFramesWritten = getStreamInternal()->getFramesWritten();
+
std::lock_guard <std::mutex> lock(mLockStreams);
- 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());
+ for (const auto clientStream : mRegisteredStreams) {
+ if (!clientStream->isRunning()) {
+ continue;
}
- index++;
+
+ AAudioServiceStreamShared *streamShared =
+ static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+ FifoBuffer *fifo = streamShared->getDataFifoBuffer();
+ // Determine offset between framePosition in client's stream vs the underlying
+ // MMAP stream.
+ int64_t clientFramesRead = fifo->getReadCounter();
+ // These two indices refer to the same frame.
+ int64_t positionOffset = mmapFramesWritten - clientFramesRead;
+ streamShared->setTimestampPositionOffset(positionOffset);
+
+ float volume = 1.0; // to match legacy volume
+ bool underflowed = mMixer.mix(index, fifo, volume);
+
+ // This timestamp represents the completion of data being read out of the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when the client has room to write more data.
+ Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
+ streamShared->markTransferTime(timestamp);
+
+ if (underflowed) {
+ streamShared->incrementXRunCount();
+ }
+
+ index++; // just used for labelling tracks in systrace
}
}
@@ -93,7 +117,7 @@
result = getStreamInternal()->write(mMixer.getOutputBuffer(),
getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
- disconnectRegisteredStreams();
+ AAudioServiceEndpointShared::disconnectRegisteredStreams();
break;
} else if (result != getFramesPerBurst()) {
ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
@@ -102,8 +126,5 @@
}
}
- ALOGW_IF((underflowCount > 0),
- "AAudioServiceEndpointPlay(): callbackLoop() had %d underflows", underflowCount);
-
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h
index c22f510..a0a383c 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.h
+++ b/services/oboeservice/AAudioServiceEndpointPlay.h
@@ -25,6 +25,7 @@
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalPlay.h"
#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceStreamMMAP.h"
#include "AAudioMixer.h"
@@ -35,16 +36,12 @@
/**
* Contains a mixer and a stream for writing the result of the mix.
*/
-class AAudioServiceEndpointPlay : public AAudioServiceEndpoint {
+class AAudioServiceEndpointPlay : public AAudioServiceEndpointShared {
public:
explicit AAudioServiceEndpointPlay(android::AAudioService &audioService);
virtual ~AAudioServiceEndpointPlay();
- aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
-
- AudioStreamInternal *getStreamInternal() override {
- return &mStreamInternalPlay;
- }
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
void *callbackLoop() override;
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
new file mode 100644
index 0000000..43d73b7
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 "AAudioServiceEndpointShared"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "binding/AAudioServiceMessage.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "core/AudioStreamBuilder.h"
+
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+using namespace android;
+using namespace aaudio;
+
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+
+std::string AAudioServiceEndpointShared::dump() const {
+ std::stringstream result;
+
+ result << " SHARED: sharing exclusive stream with handle = 0x"
+ << std::setfill('0') << std::setw(8)
+ << std::hex << mStreamInternal->getServiceHandle()
+ << std::dec << std::setfill(' ');
+ result << "\n";
+ result << " Running Stream Count: " << mRunningStreamCount << "\n";
+
+ result << AAudioServiceEndpoint::dump();
+ return result.str();
+}
+
+// Share an AudioStreamInternal.
+aaudio_result_t AAudioServiceEndpointShared::open(const aaudio::AAudioStreamRequest &request) {
+ aaudio_result_t result = AAUDIO_OK;
+ const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+
+ mRequestedDeviceId = configuration.getDeviceId();
+ setDirection(configuration.getDirection());
+
+ AudioStreamBuilder builder;
+ builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+ // Don't fall back to SHARED because that would cause recursion.
+ builder.setSharingModeMatchRequired(true);
+ builder.setDeviceId(mRequestedDeviceId);
+ builder.setFormat(configuration.getFormat());
+ builder.setSampleRate(configuration.getSampleRate());
+ builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
+ builder.setDirection(configuration.getDirection());
+ builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
+ result = mStreamInternal->open(builder);
+
+ setSampleRate(mStreamInternal->getSampleRate());
+ setSamplesPerFrame(mStreamInternal->getSamplesPerFrame());
+ setDeviceId(mStreamInternal->getDeviceId());
+ mFramesPerBurst = mStreamInternal->getFramesPerBurst();
+
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::close() {
+ return getStreamInternal()->close();
+}
+
+// Glue between C and C++ callbacks.
+static void *aaudio_endpoint_thread_proc(void *context) {
+ AAudioServiceEndpointShared *endpoint = (AAudioServiceEndpointShared *) context;
+ if (endpoint != NULL) {
+ return endpoint->callbackLoop();
+ } else {
+ return NULL;
+ }
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::startSharingThread_l() {
+ // Launch the callback loop thread.
+ int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
+ * AAUDIO_NANOS_PER_SECOND
+ / getSampleRate();
+ mCallbackEnabled.store(true);
+ return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
+ mCallbackEnabled.store(false);
+ aaudio_result_t result = getStreamInternal()->joinThread(NULL);
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
+ audio_port_handle_t *clientHandle) {
+ aaudio_result_t result = AAUDIO_OK;
+ if (++mRunningStreamCount == 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();
+ }
+ if (result == AAUDIO_OK) {
+ ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
+ result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
+ }
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::stopStream(sp<AAudioServiceStreamBase> sharedStream,
+ audio_port_handle_t clientHandle) {
+ // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+
+ // Ignore result.
+ (void) getStreamInternal()->stopClient(clientHandle);
+
+ if (--mRunningStreamCount == 0) { // atomic
+ stopSharingThread();
+ getStreamInternal()->requestStop();
+ }
+ return AAUDIO_OK;
+}
+
+
+// Get timestamp that was written by the real-time service thread, eg. mixer.
+aaudio_result_t AAudioServiceEndpointShared::getFreeRunningPosition(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+}
+
+aaudio_result_t AAudioServiceEndpointShared::getTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
+}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
new file mode 100644
index 0000000..e3bd2c1
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -0,0 +1,78 @@
+/*
+ * 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_SERVICE_ENDPOINT_SHARED_H
+#define AAUDIO_SERVICE_ENDPOINT_SHARED_H
+
+#include <atomic>
+#include <mutex>
+
+#include "AAudioServiceEndpoint.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This Service class corresponds to a Client stream that shares an MMAP device through a mixer
+ * or an input distributor.
+ */
+class AAudioServiceEndpointShared : public AAudioServiceEndpoint {
+
+public:
+
+ std::string dump() const override;
+
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+ aaudio_result_t close() override;
+
+ aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t *clientHandle) override;
+
+ aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+ audio_port_handle_t clientHandle) override;
+
+ aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+ aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+ virtual void *callbackLoop() = 0;
+
+ AudioStreamInternal *getStreamInternal() const {
+ return mStreamInternal;
+ };
+
+protected:
+
+ aaudio_result_t startSharingThread_l();
+
+ aaudio_result_t stopSharingThread();
+
+ // pointer to object statically allocated in subclasses
+ AudioStreamInternal *mStreamInternal = nullptr;
+
+ std::atomic<bool> mCallbackEnabled{false};
+
+ std::atomic<int> mRunningStreamCount{0};
+};
+
+}
+
+#endif //AAUDIO_SERVICE_ENDPOINT_SHARED_H
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 5f7d179..2dc62a0 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -18,12 +18,17 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <iomanip>
+#include <iostream>
#include <mutex>
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
#include "utility/AudioClock.h"
+#include "AAudioEndpointManager.h"
+#include "AAudioService.h"
+#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamBase.h"
#include "TimestampScheduler.h"
@@ -35,9 +40,11 @@
* @return
*/
-AAudioServiceStreamBase::AAudioServiceStreamBase()
+AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
: mUpMessageQueue(nullptr)
- , mAAudioThread() {
+ , mAAudioThread()
+ , mAtomicTimestamp()
+ , mAudioService(audioService) {
mMmapClient.clientUid = -1;
mMmapClient.clientPid = -1;
mMmapClient.packageName = String16("");
@@ -53,55 +60,124 @@
"service stream still open, state = %d", mState);
}
+std::string AAudioServiceStreamBase::dumpHeader() {
+ return std::string(" T Handle UId Run State Format Burst Chan Capacity");
+}
+
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";
+ result << " 0x" << std::setfill('0') << std::setw(8) << std::hex << mHandle
+ << std::dec << std::setfill(' ') ;
+ result << std::setw(6) << mMmapClient.clientUid;
+ result << std::setw(4) << (isRunning() ? "yes" : " no");
+ result << std::setw(6) << mState;
+ result << std::setw(7) << getFormat();
+ result << std::setw(6) << mFramesPerBurst;
+ result << std::setw(5) << getSamplesPerFrame();
+ result << std::setw(9) << getBufferCapacity();
return result.str();
}
aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) {
+ aaudio_sharing_mode_t sharingMode) {
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ aaudio_result_t result = AAUDIO_OK;
mMmapClient.clientUid = request.getUserId();
mMmapClient.clientPid = request.getProcessId();
- mMmapClient.packageName.setTo(String16("")); // FIXME what should we do here?
+ mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
- std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
- if (mUpMessageQueue != nullptr) {
- return AAUDIO_ERROR_INVALID_STATE;
- } else {
+ // Limit scope of lock to avoid recursive lock in close().
+ {
+ std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
+ if (mUpMessageQueue != nullptr) {
+ ALOGE("AAudioServiceStreamBase::open() called twice");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
mUpMessageQueue = new SharedRingBuffer();
- return mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+ result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
+ QUEUE_UP_CAPACITY_COMMANDS);
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+
+ mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
+ request,
+ sharingMode);
+ if (mServiceEndpoint == nullptr) {
+ ALOGE("AAudioServiceStreamBase::open() openEndpoint() failed");
+ result = AAUDIO_ERROR_UNAVAILABLE;
+ goto error;
+ }
+ mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
+ copyFrom(*mServiceEndpoint);
}
+ return result;
+
+error:
+ close();
+ return result;
}
aaudio_result_t AAudioServiceStreamBase::close() {
- if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+ aaudio_result_t result = AAUDIO_OK;
+ if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ return AAUDIO_OK;
+ }
+
+ stop();
+
+ if (mServiceEndpoint == nullptr) {
+ result = AAUDIO_ERROR_INVALID_STATE;
+ } else {
+ mServiceEndpoint->unregisterStream(this);
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ mEndpointManager.closeEndpoint(mServiceEndpoint);
+ mServiceEndpoint.clear();
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
stopTimestampThread();
- std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
delete mUpMessageQueue;
mUpMessageQueue = nullptr;
- mState = AAUDIO_STREAM_STATE_CLOSED;
}
- return AAUDIO_OK;
+
+ mState = AAUDIO_STREAM_STATE_CLOSED;
+ return result;
}
+/**
+ * Start the flow of audio data.
+ *
+ * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
+ */
aaudio_result_t AAudioServiceStreamBase::start() {
if (isRunning()) {
return AAUDIO_OK;
}
- sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
- mState = AAUDIO_STREAM_STATE_STARTED;
- mThreadEnabled.store(true);
- return mAAudioThread.start(this);
+
+ if (mServiceEndpoint == nullptr) {
+ ALOGE("AAudioServiceStreamBase::start() missing endpoint");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ mClientHandle = AUDIO_PORT_HANDLE_NONE;
+ aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
+ disconnect();
+ } else {
+ if (result == AAUDIO_OK) {
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+ mState = AAUDIO_STREAM_STATE_STARTED;
+ mThreadEnabled.store(true);
+ result = mAAudioThread.start(this);
+ }
+ }
+ return result;
}
aaudio_result_t AAudioServiceStreamBase::pause() {
@@ -109,6 +185,16 @@
if (!isRunning()) {
return result;
}
+ if (mServiceEndpoint == nullptr) {
+ ALOGE("AAudioServiceStreamShared::pause() missing endpoint");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ result = mServiceEndpoint->stopStream(this, mClientHandle);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+ disconnect(); // TODO should we return or pause Base first?
+ }
+
sendCurrentTimestamp();
mThreadEnabled.store(false);
result = mAAudioThread.stop();
@@ -126,13 +212,27 @@
if (!isRunning()) {
return result;
}
- // TODO wait for data to be played out
+
+ if (mServiceEndpoint == nullptr) {
+ ALOGE("AAudioServiceStreamShared::stop() missing endpoint");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
sendCurrentTimestamp(); // warning - this calls a virtual function
result = stopTimestampThread();
if (result != AAUDIO_OK) {
disconnect();
return result;
}
+
+ // TODO wait for data to be played out
+ result = mServiceEndpoint->stopStream(this, mClientHandle);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
+ disconnect();
+ // TODO what to do with result here?
+ }
+
sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
mState = AAUDIO_STREAM_STATE_STOPPED;
return result;
@@ -148,6 +248,12 @@
}
aaudio_result_t AAudioServiceStreamBase::flush() {
+ if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+ ALOGE("AAudioServiceStreamBase::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.
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
mState = AAUDIO_STREAM_STATE_FLUSHED;
return AAUDIO_OK;
@@ -157,7 +263,7 @@
void AAudioServiceStreamBase::run() {
ALOGD("AAudioServiceStreamBase::run() entering ----------------");
TimestampScheduler timestampScheduler;
- timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
+ timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
timestampScheduler.start(AudioClock::getNanoseconds());
int64_t nextTime = timestampScheduler.nextAbsoluteTime();
while(mThreadEnabled.load()) {
@@ -195,7 +301,7 @@
}
aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
- std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+ std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
if (mUpMessageQueue == nullptr) {
ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
return AAUDIO_ERROR_NULL;
@@ -211,15 +317,25 @@
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
+ // Send a timestamp for the clock model.
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
- // ALOGD("sendCurrentTimestamp(): position = %lld, nanos = %lld",
- // (long long) command.timestamp.position,
- // (long long) command.timestamp.timestamp);
- command.what = AAudioServiceMessage::code::TIMESTAMP;
+ command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
result = writeUpMessageQueue(&command);
- } else if (result == AAUDIO_ERROR_UNAVAILABLE) {
+
+ if (result == AAUDIO_OK) {
+ // Send a hardware timestamp for presentation time.
+ result = getHardwareTimestamp(&command.timestamp.position,
+ &command.timestamp.timestamp);
+ if (result == AAUDIO_OK) {
+ command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
+ result = writeUpMessageQueue(&command);
+ }
+ }
+ }
+
+ if (result == AAUDIO_ERROR_UNAVAILABLE) {
result = AAUDIO_OK; // just not available yet, try again later
}
return result;
@@ -235,3 +351,7 @@
parcelable.mUpMessageQueueParcelable);
return getDownDataDescription(parcelable);
}
+
+void AAudioServiceStreamBase::onVolumeChanged(float volume) {
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index cebefec..301795d 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,6 +20,7 @@
#include <assert.h>
#include <mutex>
+#include <media/AudioClient.h>
#include <utils/RefBase.h>
#include "fifo/FifoBuffer.h"
@@ -27,71 +28,94 @@
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceMessage.h"
#include "utility/AAudioUtilities.h"
-#include <media/AudioClient.h>
+#include "utility/AudioClock.h"
#include "SharedRingBuffer.h"
#include "AAudioThread.h"
+#include "AAudioService.h"
namespace aaudio {
+class AAudioServiceEndpoint;
+
// We expect the queue to only have a few commands.
// This should be way more than we need.
#define QUEUE_UP_CAPACITY_COMMANDS (128)
/**
- * Base class for a stream in the AAudio service.
+ * Each instance of AAudioServiceStreamBase corresponds to a client stream.
+ * It uses a subclass of AAudioServiceEndpoint to communicate with the underlying device or port.
*/
class AAudioServiceStreamBase
: public virtual android::RefBase
+ , public AAudioStreamParameters
, public Runnable {
public:
- AAudioServiceStreamBase();
+ AAudioServiceStreamBase(android::AAudioService &aAudioService);
+
virtual ~AAudioServiceStreamBase();
enum {
ILLEGAL_THREAD_ID = 0
};
- std::string dump() const;
+ static std::string dumpHeader();
+
+ // does not include EOL
+ virtual std::string dump() const;
// -------------------------------------------------------------------
/**
* Open the device.
*/
- virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
+ virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
virtual aaudio_result_t close();
/**
- * Start the flow of data.
+ * Start the flow of audio data.
+ *
+ * This is not guaranteed to be synchronous but it currently is.
+ * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
virtual aaudio_result_t start();
/**
- * Stop the flow of data such that start() can resume with loss of data.
- */
+ * Stop the flow of data so that start() can resume without loss of data.
+ *
+ * This is not guaranteed to be synchronous but it currently is.
+ * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
+ */
virtual aaudio_result_t pause();
/**
- * Stop the flow of data after data in buffer has played.
+ * Stop the flow of data after the currently queued data has finished playing.
+ *
+ * This is not guaranteed to be synchronous but it currently is.
+ * An AAUDIO_SERVICE_EVENT_STOPPED will be sent to the client when complete.
+ *
*/
virtual aaudio_result_t stop();
aaudio_result_t stopTimestampThread();
/**
- * Discard any data held by the underlying HAL or Service.
+ * Discard any data held by the underlying HAL or Service.
+ *
+ * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
*/
virtual aaudio_result_t flush();
+
virtual aaudio_result_t startClient(const android::AudioClient& client __unused,
audio_port_handle_t *clientHandle __unused) {
+ ALOGD("AAudioServiceStreamBase::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
return AAUDIO_ERROR_UNAVAILABLE;
}
virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle __unused) {
+ ALOGD("AAudioServiceStreamBase::stopClient(%d) AAUDIO_ERROR_UNAVAILABLE", clientHandle);
return AAUDIO_ERROR_UNAVAILABLE;
}
@@ -126,14 +150,14 @@
return mFramesPerBurst;
}
- int32_t calculateBytesPerFrame() const {
- return mSamplesPerFrame * AAudioConvert_formatToSizeInBytes(mAudioFormat);
- }
-
void run() override; // to implement Runnable
void disconnect();
+ const android::AudioClient &getAudioClient() {
+ return mMmapClient;
+ }
+
uid_t getOwnerUserId() const {
return mMmapClient.clientUid;
}
@@ -153,8 +177,16 @@
return mState;
}
+ void onVolumeChanged(float volume);
+
protected:
+ /**
+ * Open the device.
+ */
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
+ aaudio_sharing_mode_t sharingMode);
+
void setState(aaudio_stream_state_t state) {
mState = state;
}
@@ -170,6 +202,8 @@
*/
virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
+ virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
@@ -177,20 +211,21 @@
pid_t mRegisteredClientThread = ILLEGAL_THREAD_ID;
SharedRingBuffer* mUpMessageQueue;
- std::mutex mLockUpMessageQueue;
+ std::mutex mUpMessageQueueLock;
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{false};
- 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;
+ android::AudioClient mMmapClient; // set in open, used in MMAP start()
audio_port_handle_t mClientHandle = AUDIO_PORT_HANDLE_NONE;
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
+
+ android::AAudioService &mAudioService;
+ android::sp<AAudioServiceEndpoint> mServiceEndpoint;
+
private:
aaudio_handle_t mHandle = -1;
};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index ff02c0f..47041c5 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -19,35 +19,33 @@
#include <utils/Log.h>
#include <atomic>
+#include <iomanip>
+#include <iostream>
#include <stdint.h>
#include <utils/String16.h>
#include <media/nbaio/AudioStreamOutSink.h>
#include <media/MmapStreamInterface.h>
-#include "AAudioServiceStreamBase.h"
-#include "AAudioServiceStreamMMAP.h"
#include "binding/AudioEndpointParcelable.h"
-#include "SharedMemoryProxy.h"
#include "utility/AAudioUtilities.h"
+#include "AAudioServiceEndpointMMAP.h"
+#include "AAudioServiceStreamBase.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "SharedMemoryProxy.h"
+
+using android::base::unique_fd;
using namespace android;
using namespace aaudio;
-#define AAUDIO_BUFFER_CAPACITY_MIN 4 * 512
-#define AAUDIO_SAMPLE_RATE_DEFAULT 48000
-
/**
* Service Stream that uses an MMAP buffer.
*/
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(const android::AudioClient& serviceClient,
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
bool inService)
- : AAudioServiceStreamBase()
- , mMmapStreamCallback(new MyMmapStreamCallback(*this))
- , mPreviousFrameCounter(0)
- , mMmapStream(nullptr)
- , mServiceClient(serviceClient)
+ : AAudioServiceStreamBase(aAudioService)
, mInService(inService) {
}
@@ -55,165 +53,32 @@
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);
- }
- if (mAudioDataFileDescriptor != -1) {
- ::close(mAudioDataFileDescriptor);
- mAudioDataFileDescriptor = -1;
- }
+ stop();
return AAudioServiceStreamBase::close();
}
// Open stream on HAL and pass information about the shared memory buffer back to the client.
-aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) {
- const audio_attributes_t attributes = {
- .content_type = AUDIO_CONTENT_TYPE_MUSIC,
- .usage = AUDIO_USAGE_MEDIA,
- .source = AUDIO_SOURCE_VOICE_RECOGNITION,
- .flags = AUDIO_FLAG_LOW_LATENCY,
- .tags = ""
- };
- audio_config_base_t config;
+aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
- aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+ sp<AAudioServiceStreamMMAP> keep(this);
+
+ aaudio_result_t result = AAudioServiceStreamBase::open(request,
+ AAUDIO_SHARING_MODE_EXCLUSIVE);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamBase open returned %d", result);
return result;
}
- const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
- audio_port_handle_t deviceId = configurationInput.getDeviceId();
- aaudio_direction_t direction = request.getDirection();
-
- // Fill in config
- aaudio_format_t aaudioFormat = configurationInput.getFormat();
- if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+ result = mServiceEndpoint->registerStream(keep);
+ if (result != AAUDIO_OK) {
+ goto error;
}
- config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
-
- int32_t aaudioSampleRate = configurationInput.getSampleRate();
- if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
- aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
- }
- config.sample_rate = aaudioSampleRate;
-
- int32_t aaudioSamplesPerFrame = configurationInput.getSamplesPerFrame();
-
- if (direction == AAUDIO_DIRECTION_OUTPUT) {
- config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
- ? AUDIO_CHANNEL_OUT_STEREO
- : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
- } else if (direction == AAUDIO_DIRECTION_INPUT) {
- config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
- ? AUDIO_CHANNEL_IN_STEREO
- : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
- } else {
- ALOGE("openMmapStream - invalid direction = %d", direction);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- }
-
- MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
- ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
-
- // Open HAL stream.
- status_t status = MmapStreamInterface::openMmapStream(streamDirection,
- &attributes,
- &config,
- mMmapClient,
- &deviceId,
- mMmapStreamCallback,
- 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
- minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
- }
- status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
- if (status != OK) {
- ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d",
- status);
- return AAUDIO_ERROR_UNAVAILABLE;
- } else {
- 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.
- mSamplesPerFrame = (direction == AAUDIO_DIRECTION_OUTPUT)
- ? audio_channel_count_from_out_mask(config.channel_mask)
- : audio_channel_count_from_in_mask(config.channel_mask);
-
- mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
- mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
- mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
- mSampleRate = config.sample_rate;
-
- // Scale up the burst size to meet the minimum equivalent in microseconds.
- // This is to avoid waking the CPU too often when the HW burst is very small
- // or at high sample rates.
- int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
- int32_t burstMicros = 0;
- do {
- if (burstMicros > 0) { // skip first loop
- mFramesPerBurst *= 2;
- }
- burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / mSampleRate;
- } while (burstMicros < burstMinMicros);
-
- 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.setFormat(mAudioFormat);
- configurationOutput.setDeviceId(deviceId);
setState(AAUDIO_STREAM_STATE_OPEN);
+
+error:
return AAUDIO_OK;
}
@@ -224,18 +89,10 @@
if (isRunning()) {
return AAUDIO_OK;
}
- if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
- aaudio_result_t result;
- status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
- if (status != OK) {
- ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
- disconnect();
- result = AAudioConvert_androidToAAudioResult(status);
- } else {
- result = AAudioServiceStreamBase::start();
- if (!mInService && result == AAUDIO_OK) {
- startClient(mMmapClient, &mClientHandle);
- }
+
+ aaudio_result_t result = AAudioServiceStreamBase::start();
+ if (!mInService && result == AAUDIO_OK) {
+ result = startClient(mMmapClient, &mClientHandle);
}
return result;
}
@@ -247,104 +104,76 @@
if (!isRunning()) {
return AAUDIO_OK;
}
- if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
- aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+ aaudio_result_t result = AAudioServiceStreamBase::pause();
+ // TODO put before base::pause()?
if (!mInService) {
- stopClient(mClientHandle);
+ (void) stopClient(mClientHandle);
}
- status_t status = mMmapStream->stop(mPortHandle);
- mFramesRead.reset32();
- return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
+ return result;
}
aaudio_result_t AAudioServiceStreamMMAP::stop() {
if (!isRunning()) {
return AAUDIO_OK;
}
- if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
- aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+ aaudio_result_t result = AAudioServiceStreamBase::stop();
+ // TODO put before base::stop()?
if (!mInService) {
- stopClient(mClientHandle);
- }
- aaudio_result_t status = mMmapStream->stop(mPortHandle);
- mFramesRead.reset32();
- return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
-}
-
-/**
- * Discard any data held by the underlying HAL or Service.
- */
-aaudio_result_t AAudioServiceStreamMMAP::flush() {
- if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
- // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
- 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) {
- disconnect();
- return AAUDIO_ERROR_NULL;
- }
- status_t status = mMmapStream->getMmapPosition(&position);
- 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;
+ (void) stopClient(mClientHandle);
}
return result;
}
-void AAudioServiceStreamMMAP::onTearDown() {
- ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
- disconnect();
-};
+aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) {
+ aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
+ return result;
+}
-void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) {
- // TODO do we really need a different volume for each channel?
- float volume = values[0];
- ALOGD("AAudioServiceStreamMMAP::onVolumeChanged() volume[0] = %f", volume);
- sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
-};
+aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
+ aaudio_result_t result = mServiceEndpoint->stopClient(clientHandle);
+ return result;
+}
-void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
- ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
- deviceId, mDeviceId);
- if (mDeviceId != AUDIO_PORT_HANDLE_NONE && mDeviceId != deviceId) {
+// Get free-running DSP or DMA hardware position from the HAL.
+aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+ static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+ aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos);
+ if (result == AAUDIO_OK) {
+ Timestamp timestamp(*positionFrames, *timeNanos);
+ mAtomicTimestamp.write(timestamp);
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds();
+ } else if (result != AAUDIO_ERROR_UNAVAILABLE) {
disconnect();
}
- mDeviceId = deviceId;
-};
+ return result;
+}
+
+// Get timestamp that was written by getFreeRunningPosition()
+aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+ static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+ // TODO Get presentation timestamp from the HAL
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds() + serviceEndpointMMAP->getHardwareTimeOffsetNanos();
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+}
/**
* Get an immutable description of the data queue from the HAL.
*/
aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
{
- // Gather information on the data queue based on HAL info.
- int32_t bytesPerFrame = calculateBytesPerFrame();
- int32_t capacityInBytes = mCapacityInFrames * bytesPerFrame;
- int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
- parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
- parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
- parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
- parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
- return AAUDIO_OK;
+ sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+ static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+ return serviceEndpointMMAP->getDownDataDescription(parcelable);
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 533e5a8..bf0aab3 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,21 +34,23 @@
#include "TimestampScheduler.h"
#include "utility/MonotonicCounter.h"
+
namespace aaudio {
- /**
- * Manage one memory mapped buffer that originated from a HAL.
- */
-class AAudioServiceStreamMMAP
- : public AAudioServiceStreamBase
- , public android::MmapStreamCallback {
+
+/**
+ * These corresponds to an EXCLUSIVE mode MMAP client stream.
+ * It has exclusive use of one AAudioServiceEndpointMMAP to communicate with the underlying
+ * device or port.
+ */
+class AAudioServiceStreamMMAP : public AAudioServiceStreamBase {
public:
- AAudioServiceStreamMMAP(const android::AudioClient& serviceClient, bool inService);
+ AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
+ bool inService);
virtual ~AAudioServiceStreamMMAP() = default;
- aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) override;
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
/**
* Start the flow of audio data.
@@ -67,33 +70,16 @@
aaudio_result_t stop() override;
- /**
- * Discard any data held by the underlying HAL or Service.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
- */
- aaudio_result_t flush() override;
+ aaudio_result_t startClient(const android::AudioClient& client,
+ audio_port_handle_t *clientHandle) override;
+
+ aaudio_result_t stopClient(audio_port_handle_t clientHandle) override;
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.
*/
- aaudio_result_t sendCurrentTimestamp();
-
- // -------------- Callback functions ---------------------
- void onTearDown() override;
-
- void onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) override;
-
- void onRoutingChanged(audio_port_handle_t deviceId) override;
protected:
@@ -101,45 +87,11 @@
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
private:
- // This proxy class was needed to prevent a crash in AudioFlinger
- // when the stream was closed.
- class MyMmapStreamCallback : public android::MmapStreamCallback {
- public:
- explicit MyMmapStreamCallback(android::MmapStreamCallback &serviceCallback)
- : mServiceCallback(serviceCallback){}
- virtual ~MyMmapStreamCallback() = default;
- void onTearDown() override {
- mServiceCallback.onTearDown();
- };
-
- void onVolumeChanged(audio_channel_mask_t channels, android::Vector<float> values) override
- {
- mServiceCallback.onVolumeChanged(channels, values);
- };
-
- void onRoutingChanged(audio_port_handle_t deviceId) override {
- mServiceCallback.onRoutingChanged(deviceId);
- };
-
- private:
- android::MmapStreamCallback &mServiceCallback;
- };
-
- android::sp<MyMmapStreamCallback> mMmapStreamCallback;
- 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;
- audio_port_handle_t mPortHandle = AUDIO_PORT_HANDLE_NONE;
- audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
- android::AudioClient mServiceClient;
- bool mInService = false;
+ bool mInService = false;
};
} // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 5654113..834f39f 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -18,6 +18,8 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <iomanip>
+#include <iostream>
#include <mutex>
#include <aaudio/AAudio.h>
@@ -40,8 +42,32 @@
#define MAX_FRAMES_PER_BUFFER (32 * 1024)
AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
- : mAudioService(audioService)
- {
+ : AAudioServiceStreamBase(audioService)
+ , mTimestampPositionOffset(0)
+ , mXRunCount(0) {
+}
+
+std::string AAudioServiceStreamShared::dumpHeader() {
+ std::stringstream result;
+ result << AAudioServiceStreamBase::dumpHeader();
+ result << " Write# Read# Avail XRuns";
+ return result.str();
+}
+
+std::string AAudioServiceStreamShared::dump() const {
+ std::stringstream result;
+
+ result << AAudioServiceStreamBase::dump();
+
+ auto fifo = mAudioDataQueue->getFifoBuffer();
+ int32_t readCounter = fifo->getReadCounter();
+ int32_t writeCounter = fifo->getWriteCounter();
+ result << std::setw(10) << writeCounter;
+ result << std::setw(10) << readCounter;
+ result << std::setw(8) << (writeCounter - readCounter);
+ result << std::setw(8) << getXRunCount();
+
+ return result.str();
}
int32_t AAudioServiceStreamShared::calculateBufferCapacity(int32_t requestedCapacityFrames,
@@ -90,87 +116,69 @@
return capacityInFrames;
}
-aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) {
+aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request) {
sp<AAudioServiceStreamShared> keep(this);
- aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+ aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamBase open() returned %d", result);
return result;
}
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
- aaudio_direction_t direction = request.getDirection();
- AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, configurationOutput, direction);
- if (mServiceEndpoint == nullptr) {
- ALOGE("AAudioServiceStreamShared::open() mServiceEndPoint = %p", mServiceEndpoint);
- return AAUDIO_ERROR_UNAVAILABLE;
- }
// Is the request compatible with the shared endpoint?
- 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);
+ setFormat(configurationInput.getFormat());
+ if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
+ setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) {
+ ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", getFormat());
result = AAUDIO_ERROR_INVALID_FORMAT;
goto error;
}
- mSampleRate = configurationInput.getSampleRate();
- if (mSampleRate == AAUDIO_UNSPECIFIED) {
- mSampleRate = mServiceEndpoint->getSampleRate();
- } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+ setSampleRate(configurationInput.getSampleRate());
+ if (getSampleRate() == AAUDIO_UNSPECIFIED) {
+ setSampleRate(mServiceEndpoint->getSampleRate());
+ } else if (getSampleRate() != mServiceEndpoint->getSampleRate()) {
ALOGE("AAudioServiceStreamShared::open() mSampleRate = %d, need %d",
- mSampleRate, mServiceEndpoint->getSampleRate());
+ getSampleRate(), mServiceEndpoint->getSampleRate());
result = AAUDIO_ERROR_INVALID_RATE;
goto error;
}
- mSamplesPerFrame = configurationInput.getSamplesPerFrame();
- if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
- mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
- } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+ setSamplesPerFrame(configurationInput.getSamplesPerFrame());
+ if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
+ setSamplesPerFrame(mServiceEndpoint->getSamplesPerFrame());
+ } else if (getSamplesPerFrame() != mServiceEndpoint->getSamplesPerFrame()) {
ALOGE("AAudioServiceStreamShared::open() mSamplesPerFrame = %d, need %d",
- mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
+ getSamplesPerFrame(), mServiceEndpoint->getSamplesPerFrame());
result = AAUDIO_ERROR_OUT_OF_RANGE;
goto error;
}
- mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
- ALOGD("AAudioServiceStreamShared::open() mSampleRate = %d, mFramesPerBurst = %d",
- mSampleRate, mFramesPerBurst);
-
- mCapacityInFrames = calculateBufferCapacity(configurationInput.getBufferCapacity(),
- mFramesPerBurst);
- if (mCapacityInFrames < 0) {
- result = mCapacityInFrames; // negative error code
- mCapacityInFrames = 0;
+ setBufferCapacity(calculateBufferCapacity(configurationInput.getBufferCapacity(),
+ mFramesPerBurst));
+ if (getBufferCapacity() < 0) {
+ result = getBufferCapacity(); // negative error code
+ setBufferCapacity(0);
goto error;
}
// Create audio data shared memory buffer for client.
mAudioDataQueue = new SharedRingBuffer();
- result = mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+ result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
- mCapacityInFrames);
+ getBufferCapacity());
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.setFormat(mAudioFormat);
- configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
+ getSampleRate(), getSamplesPerFrame(), mServiceEndpoint->getDeviceId());
result = mServiceEndpoint->registerStream(keep);
if (result != AAUDIO_OK) {
@@ -185,114 +193,14 @@
return result;
}
-/**
- * Start the flow of audio data.
- *
- * 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;
- }
- // For output streams, this will add the stream to the mixer.
- aaudio_result_t result = endpoint->startStream(this);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
- disconnect();
- } else {
- result = endpoint->getStreamInternal()->startClient(mMmapClient, &mClientHandle);
- if (result == AAUDIO_OK) {
- result = AAudioServiceStreamBase::start();
- }
- }
- return result;
-}
-
-/**
- * Stop the flow of data so that start() can resume without loss of data.
- *
- * 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;
- }
- endpoint->getStreamInternal()->stopClient(mClientHandle);
- aaudio_result_t result = endpoint->stopStream(this);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
- 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;
- }
- endpoint->getStreamInternal()->stopClient(mClientHandle);
- aaudio_result_t result = endpoint->stopStream(this);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
- disconnect();
- }
- return AAudioServiceStreamBase::stop();
-}
-
-/**
- * Discard any data held by the underlying HAL or Service.
- *
- * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
- */
-aaudio_result_t AAudioServiceStreamShared::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() {
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
- return AAUDIO_OK;
- }
+ aaudio_result_t result = AAudioServiceStreamBase::close();
- stop();
+ delete mAudioDataQueue;
+ mAudioDataQueue = nullptr;
- 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;
- }
- return AAudioServiceStreamBase::close();
+ return result;
}
/**
@@ -307,15 +215,31 @@
return AAUDIO_OK;
}
-void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
- mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
- mMarkedTime = nanoseconds;
+void AAudioServiceStreamShared::markTransferTime(Timestamp ×tamp) {
+ mAtomicTimestamp.write(timestamp);
}
+// Get timestamp that was written by mixer or distributor.
aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ // TODO Get presentation timestamp from the HAL
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+}
+
+// Get timestamp from lower level service.
+aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
int64_t *timeNanos) {
- // TODO get these two numbers as an atomic pair
- *positionFrames = mMarkedPosition;
- *timeNanos = mMarkedTime;
- return AAUDIO_OK;
+
+ aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
+ if (result == AAUDIO_OK) {
+ *positionFrames -= mTimestampPositionOffset.load(); // Offset from shared MMAP stream
+ }
+ return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 6b67337..bc86dcc 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -46,37 +46,11 @@
AAudioServiceStreamShared(android::AAudioService &aAudioService);
virtual ~AAudioServiceStreamShared() = default;
- aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) override;
+ static std::string dumpHeader();
- /**
- * Start the flow of audio data.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
- */
- aaudio_result_t start() override;
+ std::string dump() const override;
- /**
- * Stop the flow of data so that start() can resume without loss of data.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
- */
- aaudio_result_t pause() override;
-
- /**
- * Stop the flow of data after data in buffer has played.
- */
- aaudio_result_t stop() override;
-
- /**
- * Discard any data held by the underlying HAL or Service.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
- */
- aaudio_result_t flush() override;
+ aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
aaudio_result_t close() override;
@@ -85,7 +59,19 @@
/* Keep a record of when a buffer transfer completed.
* This allows for a more accurate timing model.
*/
- void markTransferTime(int64_t nanoseconds);
+ void markTransferTime(Timestamp ×tamp);
+
+ void setTimestampPositionOffset(int64_t deltaFrames) {
+ mTimestampPositionOffset.store(deltaFrames);
+ }
+
+ void incrementXRunCount() {
+ mXRunCount++;
+ }
+
+ int32_t getXRunCount() const {
+ return mXRunCount.load();
+ }
protected:
@@ -93,6 +79,8 @@
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
/**
* @param requestedCapacityFrames
* @param framesPerBurst
@@ -102,12 +90,11 @@
int32_t framesPerBurst);
private:
- android::AAudioService &mAudioService;
- AAudioServiceEndpoint *mServiceEndpoint = nullptr;
SharedRingBuffer *mAudioDataQueue = nullptr;
- int64_t mMarkedPosition = 0;
- int64_t mMarkedTime = 0;
+ std::atomic<int64_t> mTimestampPositionOffset;
+ std::atomic<int32_t> mXRunCount;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioThread.cpp b/services/oboeservice/AAudioThread.cpp
index ebb50f8..c6fb57d 100644
--- a/services/oboeservice/AAudioThread.cpp
+++ b/services/oboeservice/AAudioThread.cpp
@@ -53,7 +53,7 @@
aaudio_result_t AAudioThread::start(Runnable *runnable) {
if (mHasThread) {
- ALOGE("AAudioThread::start() - mHasThread.load() already true");
+ ALOGE("AAudioThread::start() - mHasThread already true");
return AAUDIO_ERROR_INVALID_STATE;
}
// mRunnable will be read by the new thread when it starts.
@@ -71,6 +71,7 @@
aaudio_result_t AAudioThread::stop() {
if (!mHasThread) {
+ ALOGE("AAudioThread::stop() but no thread running");
return AAUDIO_ERROR_INVALID_STATE;
}
int err = pthread_join(mThread, nullptr);
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index a896a7a..1b74ad3 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -31,7 +31,9 @@
AAudioService.cpp \
AAudioServiceEndpoint.cpp \
AAudioServiceEndpointCapture.cpp \
+ AAudioServiceEndpointMMAP.cpp \
AAudioServiceEndpointPlay.cpp \
+ AAudioServiceEndpointShared.cpp \
AAudioServiceStreamBase.cpp \
AAudioServiceStreamMMAP.cpp \
AAudioServiceStreamShared.cpp \
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 */
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 5b8d990..952acd7 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -89,7 +89,8 @@
uint32_t *numModules)
{
ALOGV("listModules");
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}
@@ -110,7 +111,8 @@
sp<ISoundTrigger>& moduleInterface)
{
ALOGV("attach module %d", handle);
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}
@@ -838,7 +840,7 @@
}
const bool supports_stop_all =
- (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
+ (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
for (size_t i = 0; i < mModels.size(); i++) {
sp<Model> model = mModels.valueAt(i);
@@ -942,7 +944,8 @@
void SoundTriggerHwService::ModuleClient::detach() {
ALOGV("detach()");
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return;
}
@@ -965,7 +968,8 @@
sound_model_handle_t *handle)
{
ALOGV("loadSoundModel() handle");
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}
@@ -979,7 +983,8 @@
status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle)
{
ALOGV("unloadSoundModel() model handle %d", handle);
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}
@@ -994,7 +999,8 @@
const sp<IMemory>& dataMemory)
{
ALOGV("startRecognition() model handle %d", handle);
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}
@@ -1008,7 +1014,8 @@
status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle)
{
ALOGV("stopRecognition() model handle %d", handle);
- if (!captureHotwordAllowed()) {
+ if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid())) {
return PERMISSION_DENIED;
}