Merge "Disable blocking allocator" into sc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index 545270e..6c45749 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -77,6 +77,9 @@
// - build artifacts (lib/javalib/bin) against Android 10 SDK
// so that the artifacts can run.
min_sdk_version: "29",
+ // Indicates that pre-installed version of this apex can be compressed.
+ // Whether it actually will be compressed is controlled on per-device basis.
+ compressible: true,
}
apex {
@@ -135,6 +138,9 @@
// - build artifacts (lib/javalib/bin) against Android 10 SDK
// so that the artifacts can run.
min_sdk_version: "29",
+ // Indicates that pre-installed version of this apex can be compressed.
+ // Whether it actually will be compressed is controlled on per-device basis.
+ compressible: true,
}
prebuilt_etc {
diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
index d428b4e..bbb0289 100644
--- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
@@ -35,4 +35,10 @@
* Update the status of a camera device.
*/
oneway void notifyCameraState(in CameraSessionStats cameraSessionStats);
+
+ /**
+ * Reports whether the top activity needs a rotate and crop override.
+ */
+ boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
+ int lensFacing);
}
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 2f6bc30..d6642f3 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -72,6 +72,10 @@
return mIsMultiResolution;
}
+const std::vector<int32_t> &OutputConfiguration::getSensorPixelModesUsed() const {
+ return mSensorPixelModesUsed;
+}
+
OutputConfiguration::OutputConfiguration() :
mRotation(INVALID_ROTATION),
mSurfaceSetID(INVALID_SET_ID),
@@ -156,6 +160,11 @@
return err;
}
+ std::vector<int32_t> sensorPixelModesUsed;
+ if ((err = parcel->readParcelableVector(&sensorPixelModesUsed)) != OK) {
+ ALOGE("%s: Failed to read sensor pixel mode(s) from parcel", __FUNCTION__);
+ return err;
+ }
mRotation = rotation;
mSurfaceSetID = setID;
mSurfaceType = surfaceType;
@@ -171,6 +180,8 @@
mGbps.push_back(surface.graphicBufferProducer);
}
+ mSensorPixelModesUsed = std::move(sensorPixelModesUsed);
+
ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d,"
" physicalCameraId = %s, isMultiResolution = %d", __FUNCTION__, mRotation,
mSurfaceSetID, mSurfaceType, String8(mPhysicalCameraId).string(), mIsMultiResolution);
@@ -240,24 +251,51 @@
err = parcel->writeInt32(mIsMultiResolution ? 1 : 0);
if (err != OK) return err;
+ err = parcel->writeParcelableVector(mSensorPixelModesUsed);
+ if (err != OK) return err;
+
return OK;
}
+template <typename T>
+static bool simpleVectorsEqual(T first, T second) {
+ if (first.size() != second.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < first.size(); i++) {
+ if (first[i] != second[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool OutputConfiguration::gbpsEqual(const OutputConfiguration& other) const {
const std::vector<sp<IGraphicBufferProducer> >& otherGbps =
other.getGraphicBufferProducers();
+ return simpleVectorsEqual(otherGbps, mGbps);
+}
- if (mGbps.size() != otherGbps.size()) {
- return false;
+bool OutputConfiguration::sensorPixelModesUsedEqual(const OutputConfiguration& other) const {
+ const std::vector<int32_t>& othersensorPixelModesUsed = other.getSensorPixelModesUsed();
+ return simpleVectorsEqual(othersensorPixelModesUsed, mSensorPixelModesUsed);
+}
+
+bool OutputConfiguration::sensorPixelModesUsedLessThan(const OutputConfiguration& other) const {
+ const std::vector<int32_t>& spms = other.getSensorPixelModesUsed();
+
+ if (mSensorPixelModesUsed.size() != spms.size()) {
+ return mSensorPixelModesUsed.size() < spms.size();
}
- for (size_t i = 0; i < mGbps.size(); i++) {
- if (mGbps[i] != otherGbps[i]) {
- return false;
+ for (size_t i = 0; i < spms.size(); i++) {
+ if (mSensorPixelModesUsed[i] != spms[i]) {
+ return mSensorPixelModesUsed[i] < spms[i];
}
}
- return true;
+ return false;
}
bool OutputConfiguration::gbpsLessThan(const OutputConfiguration& other) const {
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 6009370..f80ed3a 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -49,6 +49,8 @@
String16 getPhysicalCameraId() const;
bool isMultiResolution() const;
+ // set of sensor pixel mode resolutions allowed {MAX_RESOLUTION, DEFAULT_MODE};
+ const std::vector<int32_t>& getSensorPixelModesUsed() const;
/**
* Keep impl up-to-date with OutputConfiguration.java in frameworks/base
*/
@@ -86,7 +88,8 @@
mIsShared == other.mIsShared &&
gbpsEqual(other) &&
mPhysicalCameraId == other.mPhysicalCameraId &&
- mIsMultiResolution == other.mIsMultiResolution);
+ mIsMultiResolution == other.mIsMultiResolution &&
+ sensorPixelModesUsedEqual(other));
}
bool operator != (const OutputConfiguration& other) const {
return !(*this == other);
@@ -120,13 +123,19 @@
if (mIsMultiResolution != other.mIsMultiResolution) {
return mIsMultiResolution < other.mIsMultiResolution;
}
+ if (!sensorPixelModesUsedEqual(other)) {
+ return sensorPixelModesUsedLessThan(other);
+ }
return gbpsLessThan(other);
}
+
bool operator > (const OutputConfiguration& other) const {
return (*this != other && !(*this < other));
}
bool gbpsEqual(const OutputConfiguration& other) const;
+ bool sensorPixelModesUsedEqual(const OutputConfiguration& other) const;
+ bool sensorPixelModesUsedLessThan(const OutputConfiguration& other) const;
bool gbpsLessThan(const OutputConfiguration& other) const;
void addGraphicProducer(sp<IGraphicBufferProducer> gbp) {mGbps.push_back(gbp);}
private:
@@ -140,6 +149,7 @@
bool mIsShared;
String16 mPhysicalCameraId;
bool mIsMultiResolution;
+ std::vector<int32_t> mSensorPixelModesUsed;
};
} // namespace params
} // namespace camera2
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 895514e..7387442 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -534,6 +534,7 @@
case ACAMERA_SENSOR_SENSITIVITY:
case ACAMERA_SENSOR_TEST_PATTERN_DATA:
case ACAMERA_SENSOR_TEST_PATTERN_MODE:
+ case ACAMERA_SENSOR_PIXEL_MODE:
case ACAMERA_SHADING_MODE:
case ACAMERA_STATISTICS_FACE_DETECT_MODE:
case ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE:
@@ -584,6 +585,7 @@
ANDROID_SENSOR_PROFILE_HUE_SAT_MAP,
ANDROID_SENSOR_PROFILE_TONE_CURVE,
ANDROID_SENSOR_OPAQUE_RAW_SIZE,
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
ANDROID_SHADING_STRENGTH,
ANDROID_STATISTICS_HISTOGRAM_MODE,
ANDROID_STATISTICS_SHARPNESS_MAP_MODE,
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 889b8ab..70ce864 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -527,6 +527,13 @@
* scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability,
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
@@ -536,7 +543,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AE_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 4,
@@ -718,6 +728,12 @@
* scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
@@ -727,7 +743,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AF_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 8,
@@ -904,6 +923,12 @@
* the scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
* <p>The data representation is <code>int[5 * area_count]</code>.
* Every five elements represent a metering region of <code>(xmin, ymin, xmax, ymax, weight)</code>.
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
@@ -913,7 +938,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AWB_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 12,
@@ -2801,6 +2829,51 @@
*/
ACAMERA_LENS_DISTORTION = // float[5]
ACAMERA_LENS_START + 13,
+ /**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion for a
+ * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_LENS_DISTORTION, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_LENS_DISTORTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_LENS_DISTORTION_MAXIMUM_RESOLUTION = // float[5]
+ ACAMERA_LENS_START + 14,
+ /**
+ * <p>The parameters for this camera device's intrinsic
+ * calibration when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_LENS_INTRINSIC_CALIBRATION, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION = // float[5]
+ ACAMERA_LENS_START + 15,
ACAMERA_LENS_END,
/**
@@ -3428,6 +3501,12 @@
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* ACAMERA_CONTROL_ZOOM_RATIO for details.</p>
+ * <p>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
*
* @see ACAMERA_CONTROL_AE_TARGET_FPS_RANGE
@@ -3436,7 +3515,10 @@
* @see ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see ACAMERA_SCALER_CROPPING_TYPE
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_SCALER_CROP_REGION = // int32[4]
ACAMERA_SCALER_START,
@@ -3538,8 +3620,6 @@
* set to either OFF or FAST.</p>
* <p>When multiple streams are used in a request, the minimum frame
* duration will be max(individual stream min durations).</p>
- * <p>The minimum frame duration of a stream (of a particular format, size)
- * is the same regardless of whether the stream is input or output.</p>
* <p>See ACAMERA_SENSOR_FRAME_DURATION and
* ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for more details about
* calculating the max frame rate.</p>
@@ -3884,10 +3964,10 @@
* configurations which belong to this physical camera, and it will advertise and will only
* advertise the maximum supported resolutions for a particular format.</p>
* <p>If this camera device isn't a physical camera device constituting a logical camera,
- * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
- * multi-resolution input/output stream configurations of default mode and max resolution
- * modes. The sizes will be the maximum resolution of a particular format for default mode
- * and max resolution mode.</p>
+ * but a standalone <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * camera, this field represents the multi-resolution input/output stream configurations of
+ * default mode and max resolution modes. The sizes will be the maximum resolution of a
+ * particular format for default mode and max resolution mode.</p>
* <p>This field will only be advertised if the device is a physical camera of a
* logical multi-camera device or an ultra high resolution sensor camera. For a logical
* multi-camera, the camera API will derive the logical camera’s multi-resolution stream
@@ -3897,6 +3977,93 @@
ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS =
// int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)
ACAMERA_SCALER_START + 19,
+ /**
+ * <p>The available stream configurations that this
+ * camera device supports (i.e. format, width, height, output/input stream) for a
+ * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>Not all output formats may be supported in a configuration with
+ * an input stream of a particular format. For more details, see
+ * android.scaler.availableInputOutputFormatsMapMaximumResolution.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t)
+ ACAMERA_SCALER_START + 20,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination when the camera device is sent a CaptureRequest with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>When multiple streams are used in a request (if supported, when ACAMERA_SENSOR_PIXEL_MODE
+ * is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>), the
+ * minimum frame duration will be max(individual stream min durations).</p>
+ * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about
+ * calculating the max frame rate.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_FRAME_DURATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_SCALER_START + 21,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination when CaptureRequests are submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a></p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_SCALER_START + 22,
ACAMERA_SCALER_END,
/**
@@ -4683,6 +4850,67 @@
*/
ACAMERA_SENSOR_DYNAMIC_WHITE_LEVEL = // int32
ACAMERA_SENSOR_START + 29,
+ /**
+ * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_pixel_mode_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>This key controls whether the camera sensor operates in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode or not. By default, all camera devices operate in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode.
+ * When operating in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode, sensors
+ * with <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability would typically perform pixel binning in order to improve low light
+ * performance, noise reduction etc. However, in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode (supported only
+ * by <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+ * The stream configurations supported in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode are also different from those of
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode.
+ * They can be queried through
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#get">CameraCharacteristics#get</a> with
+ * <a href="https://developer.android.com/reference/CameraCharacteristics.html#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION)">CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION)</a>.
+ * Unless reported by both
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap.html">StreamConfigurationMap</a>s, the outputs from
+ * <code>android.scaler.streamConfigurationMapMaximumResolution</code> and
+ * <code>android.scaler.streamConfigurationMap</code>
+ * must not be mixed in the same CaptureRequest. In other words, these outputs are
+ * exclusive to each other.
+ * This key does not need to be set for reprocess requests.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE = // byte (acamera_metadata_enum_android_sensor_pixel_mode_t)
+ ACAMERA_SENSOR_START + 32,
+ /**
+ * <p>Whether <code>RAW</code> images requested have their bayer pattern as described by
+ * ACAMERA_SENSOR_INFO_BINNING_FACTOR.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * </ul></p>
+ *
+ * <p>This key will only be present in devices advertisting the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability which also advertise <code>REMOSAIC_REPROCESSING</code> capability. On all other devices
+ * RAW targets will have a regular bayer pattern.</p>
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED = // byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t)
+ ACAMERA_SENSOR_START + 33,
ACAMERA_SENSOR_END,
/**
@@ -4984,6 +5212,120 @@
*/
ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE = // int32[4]
ACAMERA_SENSOR_INFO_START + 10,
+ /**
+ * <p>The area of the image sensor which corresponds to active pixels after any geometric
+ * distortion correction has been applied, when the sensor runs in maximum resolution mode.</p>
+ *
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE
+ * is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * Refer to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE for details, with sensor array related keys
+ * replaced with their
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * counterparts.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ * <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[4]
+ ACAMERA_SENSOR_INFO_START + 11,
+ /**
+ * <p>Dimensions of the full pixel array, possibly
+ * including black calibration pixels, when the sensor runs in maximum resolution mode.
+ * Analogous to ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE is
+ * set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </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
+ * the raw buffers produced by this sensor, when it runs in maximum resolution mode. That
+ * is, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[2]
+ ACAMERA_SENSOR_INFO_START + 12,
+ /**
+ * <p>The area of the image sensor which corresponds to active pixels prior to the
+ * application of any geometric distortion correction, when the sensor runs in maximum
+ * resolution mode. This key must be used for crop / metering regions, only when
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ * when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ * <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+ // int32[4]
+ ACAMERA_SENSOR_INFO_START + 13,
+ /**
+ * <p>Dimensions of the group of pixels which are under the same color filter.
+ * This specifies the width and height (pair of integers) of the group of pixels which fall
+ * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.</p>
+ *
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Sensors can have pixels grouped together under the same color filter in order
+ * to improve various aspects of imaging such as noise reduction, low light
+ * performance etc. These groups can be of various sizes such as 2X2 (quad bayer),
+ * 3X3 (nona-bayer). This key specifies the length and width of the pixels grouped under
+ * the same color filter.</p>
+ * <p>This key will not be present if REMOSAIC_REPROCESSING is not supported, since RAW images
+ * will have a regular bayer pattern.</p>
+ * <p>This key will not be present for sensors which don't have the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ */
+ ACAMERA_SENSOR_INFO_BINNING_FACTOR = // int32[2]
+ ACAMERA_SENSOR_INFO_START + 14,
ACAMERA_SENSOR_INFO_END,
/**
@@ -6189,6 +6531,162 @@
*/
ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS = // int64[4*n]
ACAMERA_DEPTH_START + 8,
+ /**
+ * <p>The available depth dataspace stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t)
+ ACAMERA_DEPTH_START + 9,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for depth output formats when a CaptureRequest is submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about
+ * calculating the max frame rate.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_FRAME_DURATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 10,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for depth streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 11,
+ /**
+ * <p>The available dynamic depth dataspace stream
+ * configurations that this camera device supports (i.e. format, width, height,
+ * output/input stream) for CaptureRequests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t)
+ ACAMERA_DEPTH_START + 12,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for dynamic depth output streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 13,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for dynamic depth streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 14,
ACAMERA_DEPTH_END,
/**
@@ -6409,6 +6907,71 @@
*/
ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS = // int64[4*n]
ACAMERA_HEIC_START + 2,
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12) stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS for details.</p>
+ * <p>All the configuration tuples <code>(format, width, height, input?)</code> will contain
+ * AIMAGE_FORMAT_HEIC format as OUTPUT only.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t)
+ ACAMERA_HEIC_START + 3,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC output formats for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS for details.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_HEIC_START + 4,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS for details.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_HEIC_START + 5,
ACAMERA_HEIC_END,
} acamera_metadata_tag_t;
@@ -8359,6 +8922,20 @@
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14,
+ /**
+ * <p>This camera device is capable of producing ultra high resolution images in
+ * addition to the image sizes described in the
+ * android.scaler.streamConfigurationMap.
+ * It can operate in 'default' mode and 'max resolution' mode. It generally does this
+ * by binning pixels in 'default' mode and not binning them in 'max resolution' mode.
+ * <code>android.scaler.streamConfigurationMap</code> describes the streams supported in 'default'
+ * mode.
+ * The stream configurations supported in 'max resolution' mode are described by
+ * <code>android.scaler.streamConfigurationMapMaximumResolution</code>.</p>
+ */
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+ = 16,
+
} acamera_metadata_enum_android_request_available_capabilities_t;
@@ -8514,6 +9091,16 @@
} acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t;
+// ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_scaler_available_stream_configurations_maximum_resolution {
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t;
+
// ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
@@ -8672,6 +9259,42 @@
} acamera_metadata_enum_android_sensor_test_pattern_mode_t;
+// ACAMERA_SENSOR_PIXEL_MODE
+typedef enum acamera_metadata_enum_acamera_sensor_pixel_mode {
+ /**
+ * <p>This is the default sensor pixel mode. This is the only sensor pixel mode
+ * supported unless a camera device advertises
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE_DEFAULT = 0,
+
+ /**
+ * <p>This sensor pixel mode is offered by devices with capability
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILTIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>.
+ * In this mode, sensors typically do not bin pixels, as a result can offer larger
+ * image sizes.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1,
+
+} acamera_metadata_enum_android_sensor_pixel_mode_t;
+
+// ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED
+typedef enum acamera_metadata_enum_acamera_sensor_raw_binning_factor_used {
+ /**
+ * <p>The <code>RAW</code> targets in this capture have ACAMERA_SENSOR_INFO_BINNING_FACTOR as the
+ * bayer pattern.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_TRUE = 0,
+
+ /**
+ * <p>The <code>RAW</code> targets have a regular bayer pattern in this capture.</p>
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_FALSE = 1,
+
+} acamera_metadata_enum_android_sensor_raw_binning_factor_used_t;
+
// ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
typedef enum acamera_metadata_enum_acamera_sensor_info_color_filter_arrangement {
@@ -9156,6 +9779,26 @@
} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t;
+// ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_depth_available_depth_stream_configurations_maximum_resolution {
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t;
+
+// ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_configurations_maximum_resolution {
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t;
+
// ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type {
@@ -9207,6 +9850,16 @@
} acamera_metadata_enum_android_heic_available_heic_stream_configurations_t;
+// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations_maximum_resolution {
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t;
+
diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h
index df977da..0838fba 100644
--- a/camera/ndk/include/camera/NdkCameraWindowType.h
+++ b/camera/ndk/include/camera/NdkCameraWindowType.h
@@ -50,4 +50,6 @@
typedef ANativeWindow ACameraWindowType;
#endif
+/** @} */
+
#endif //_NDK_CAMERA_WINDOW_TYPE_H
diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
index 988cda9..ec0b878 100644
--- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h
+++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
@@ -211,7 +211,7 @@
}
auto allLogs(gLogBuf.getLogs());
- LOG2BI("framework logs size %zu; plugin logs size %zu",
+ LOG2BD("framework logs size %zu; plugin logs size %zu",
allLogs.size(), pluginLogs.size());
std::copy(pluginLogs.begin(), pluginLogs.end(), std::back_inserter(allLogs));
std::sort(allLogs.begin(), allLogs.end(),
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
index a537e63..7c6d86c 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
@@ -22,7 +22,6 @@
#include <openssl/aes.h>
#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
-#include <utils/RefBase.h>
namespace android {
struct ABuffer;
@@ -30,7 +29,7 @@
namespace clearkeycas {
class KeyFetcher;
-class ClearKeyCasSession : public RefBase {
+class ClearKeyCasSession {
public:
explicit ClearKeyCasSession(CasPlugin *plugin);
diff --git a/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp
index 6ac3510..089eb1c 100644
--- a/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp
@@ -207,6 +207,7 @@
}
infoMap.clear();
+ android::Mutex::Autolock lock(mPlayPolicyLock);
for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
infoMap.add(mPlayPolicy.keyAt(i), mPlayPolicy.valueAt(i));
}
diff --git a/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h
index aa9b59d..95f15ca 100644
--- a/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h
@@ -262,7 +262,7 @@
void initProperties();
void setPlayPolicy();
- android::Mutex mPlayPolicyLock;
+ mutable android::Mutex mPlayPolicyLock;
android::KeyedVector<String8, String8> mPlayPolicy;
android::KeyedVector<String8, String8> mStringProperties;
android::KeyedVector<String8, Vector<uint8_t>> mByteArrayProperties;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index e6e1f80..c49d5fe 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -50,7 +50,7 @@
relative_install_path: "hw",
- cflags: ["-Wall", "-Werror"],
+ cflags: ["-Wall", "-Werror", "-Wthread-safety"],
shared_libs: [
"android.hardware.drm@1.0",
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index d278633..302dd39 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -37,6 +37,8 @@
sp<IMemory> hidlMemory = mapMemory(base);
ALOGE_IF(hidlMemory == nullptr, "mapMemory returns nullptr");
+ std::lock_guard<std::mutex> shared_buffer_lock(mSharedBufferLock);
+
// allow mapMemory to return nullptr
mSharedBufferMap[bufferId] = hidlMemory;
return Void();
@@ -94,6 +96,7 @@
return Void();
}
+ std::unique_lock<std::mutex> shared_buffer_lock(mSharedBufferLock);
if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
"source decrypt buffer base not set");
@@ -142,12 +145,17 @@
base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
- if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
+ totalSize = 0;
+ if (__builtin_add_overflow(destBuffer.offset, destBuffer.size, &totalSize) ||
+ totalSize > destBase->getSize()) {
+ android_errorWriteLog(0x534e4554, "176444622");
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
return Void();
}
- destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
+ destPtr = static_cast<void*>(base + destination.nonsecureMemory.offset);
+ // release mSharedBufferLock
+ shared_buffer_lock.unlock();
// Calculate the output buffer size and determine if any subsamples are
// encrypted.
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index a77759e..6f69110 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -220,6 +220,7 @@
if (requestString.find(kOfflineLicense) != std::string::npos) {
std::string emptyResponse;
std::string keySetIdString(keySetId.begin(), keySetId.end());
+ Mutex::Autolock lock(mFileHandleLock);
if (!mFileHandle.StoreLicense(keySetIdString,
DeviceFiles::kLicenseStateReleasing,
emptyResponse)) {
@@ -335,6 +336,7 @@
}
*keySetId = kKeySetIdPrefix + ByteArrayToHexString(
reinterpret_cast<const uint8_t*>(randomData.data()), randomData.size());
+ Mutex::Autolock lock(mFileHandleLock);
if (mFileHandle.LicenseExists(*keySetId)) {
// collision, regenerate
ALOGV("Retry generating KeySetId");
@@ -392,6 +394,7 @@
if (status == Status::OK) {
if (isOfflineLicense) {
if (isRelease) {
+ Mutex::Autolock lock(mFileHandleLock);
mFileHandle.DeleteLicense(keySetId);
mSessionLibrary->destroySession(session);
} else {
@@ -400,6 +403,7 @@
return Void();
}
+ Mutex::Autolock lock(mFileHandleLock);
bool ok = mFileHandle.StoreLicense(
keySetId,
DeviceFiles::kLicenseStateActive,
@@ -454,6 +458,7 @@
DeviceFiles::LicenseState licenseState;
std::string offlineLicense;
Status status = Status::OK;
+ Mutex::Autolock lock(mFileHandleLock);
if (!mFileHandle.RetrieveLicense(std::string(keySetId.begin(), keySetId.end()),
&licenseState, &offlineLicense)) {
ALOGE("Failed to restore offline license");
@@ -576,7 +581,6 @@
Return<void> DrmPlugin::queryKeyStatus(
const hidl_vec<uint8_t>& sessionId,
queryKeyStatus_cb _hidl_cb) {
-
if (sessionId.size() == 0) {
// Returns empty key status KeyValue pair
_hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>());
@@ -586,12 +590,14 @@
std::vector<KeyValue> infoMapVec;
infoMapVec.clear();
+ mPlayPolicyLock.lock();
KeyValue keyValuePair;
for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
keyValuePair.key = mPlayPolicy[i].key;
keyValuePair.value = mPlayPolicy[i].value;
infoMapVec.push_back(keyValuePair);
}
+ mPlayPolicyLock.unlock();
_hidl_cb(Status::OK, toHidlVec(infoMapVec));
return Void();
}
@@ -704,6 +710,8 @@
}
Return<void> DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) {
+ Mutex::Autolock lock(mFileHandleLock);
+
std::vector<std::string> licenseNames = mFileHandle.ListLicenses();
std::vector<KeySetId> keySetIds;
if (mMockError != Status_V1_2::OK) {
@@ -724,6 +732,7 @@
return toStatus_1_0(mMockError);
}
std::string licenseName(keySetId.begin(), keySetId.end());
+ Mutex::Autolock lock(mFileHandleLock);
if (mFileHandle.DeleteLicense(licenseName)) {
return Status::OK;
}
@@ -732,6 +741,8 @@
Return<void> DrmPlugin::getOfflineLicenseState(const KeySetId& keySetId,
getOfflineLicenseState_cb _hidl_cb) {
+ Mutex::Autolock lock(mFileHandleLock);
+
std::string licenseName(keySetId.begin(), keySetId.end());
DeviceFiles::LicenseState state;
std::string license;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
index 051a968..32cf2dc 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
@@ -24,11 +24,13 @@
}
bool MemoryFileSystem::FileExists(const std::string& fileName) const {
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
auto result = mMemoryFileSystem.find(fileName);
return result != mMemoryFileSystem.end();
}
ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const {
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
auto result = mMemoryFileSystem.find(fileName);
if (result != mMemoryFileSystem.end()) {
return static_cast<ssize_t>(result->second.getFileSize());
@@ -40,6 +42,7 @@
std::vector<std::string> MemoryFileSystem::ListFiles() const {
std::vector<std::string> list;
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
for (const auto& filename : mMemoryFileSystem) {
list.push_back(filename.first);
}
@@ -48,6 +51,7 @@
size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) {
std::string key = GetFileName(path);
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
auto result = mMemoryFileSystem.find(key);
if (result != mMemoryFileSystem.end()) {
std::string serializedHashFile = result->second.getContent();
@@ -61,6 +65,7 @@
size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) {
std::string key = GetFileName(path);
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
auto result = mMemoryFileSystem.find(key);
if (result != mMemoryFileSystem.end()) {
mMemoryFileSystem.erase(key);
@@ -70,6 +75,7 @@
}
bool MemoryFileSystem::RemoveFile(const std::string& fileName) {
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
auto result = mMemoryFileSystem.find(fileName);
if (result != mMemoryFileSystem.end()) {
mMemoryFileSystem.erase(result);
@@ -81,6 +87,7 @@
}
bool MemoryFileSystem::RemoveAllFiles() {
+ std::lock_guard<std::mutex> lock(mMemoryFileSystemLock);
mMemoryFileSystem.clear();
return mMemoryFileSystem.empty();
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
index 8680f0c..23a64fa 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
@@ -20,6 +20,8 @@
#include <android/hardware/drm/1.2/ICryptoPlugin.h>
#include <android/hidl/memory/1.0/IMemory.h>
+#include <mutex>
+
#include "ClearKeyTypes.h"
#include "Session.h"
#include "Utils.h"
@@ -93,7 +95,7 @@
const SharedBuffer& source,
uint64_t offset,
const DestinationBuffer& destination,
- decrypt_1_2_cb _hidl_cb);
+ decrypt_1_2_cb _hidl_cb) NO_THREAD_SAFETY_ANALYSIS; // use unique_lock
Return<void> setSharedBufferBase(const hidl_memory& base,
uint32_t bufferId);
@@ -105,7 +107,8 @@
private:
CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin);
- std::map<uint32_t, sp<IMemory> > mSharedBufferMap;
+ std::mutex mSharedBufferLock;
+ std::map<uint32_t, sp<IMemory>> mSharedBufferMap GUARDED_BY(mSharedBufferLock);
sp<Session> mSession;
Status mInitStatus;
};
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 076beb8..894985b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -416,7 +416,8 @@
mMockError = Status_V1_2::OK;
}
- DeviceFiles mFileHandle;
+ DeviceFiles mFileHandle GUARDED_BY(mFileHandleLock);
+ Mutex mFileHandleLock;
Mutex mSecureStopLock;
CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
index bcd9fd6..6ac0e2c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
@@ -5,7 +5,9 @@
#ifndef CLEARKEY_MEMORY_FILE_SYSTEM_H_
#define CLEARKEY_MEMORY_FILE_SYSTEM_H_
+#include <android-base/thread_annotations.h>
#include <map>
+#include <mutex>
#include <string>
#include "ClearKeyTypes.h"
@@ -49,10 +51,12 @@
size_t Write(const std::string& pathName, const MemoryFile& memoryFile);
private:
+ mutable std::mutex mMemoryFileSystemLock;
+
// License file name is made up of a unique keySetId, therefore,
// the filename can be used as the key to locate licenses in the
// memory file system.
- std::map<std::string, MemoryFile> mMemoryFileSystem;
+ std::map<std::string, MemoryFile> mMemoryFileSystem GUARDED_BY(mMemoryFileSystemLock);
std::string GetFileName(const std::string& path);
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 0207311..f4a6e17 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -22,11 +22,11 @@
#include <C2Debug.h>
#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
#include <Codec2Mapper.h>
#include <SimpleC2Interface.h>
#include "C2SoftAvcDec.h"
-#include "ih264d.h"
namespace android {
@@ -332,6 +332,14 @@
free(mem);
}
+static IV_COLOR_FORMAT_T GetIvColorFormat() {
+ static IV_COLOR_FORMAT_T sColorFormat =
+ (GetYuv420FlexibleLayout() == FLEX_LAYOUT_SEMIPLANAR_UV) ? IV_YUV_420SP_UV :
+ (GetYuv420FlexibleLayout() == FLEX_LAYOUT_SEMIPLANAR_VU) ? IV_YUV_420SP_VU :
+ IV_YUV_420P;
+ return sColorFormat;
+}
+
C2SoftAvcDec::C2SoftAvcDec(
const char *name,
c2_node_id_t id,
@@ -340,7 +348,6 @@
mIntf(intfImpl),
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
- mIvColorFormat(IV_YUV_420P),
mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
@@ -391,12 +398,14 @@
}
while (true) {
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
+ ih264d_video_decode_ip_t s_h264d_decode_ip = {};
+ ih264d_video_decode_op_t s_h264d_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
- setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- if (0 == s_decode_op.u4_output_present) {
+ setDecodeArgs(ps_decode_ip, ps_decode_op, nullptr, nullptr, 0, 0, 0);
+ (void) ivdec_api_function(mDecHandle, &s_h264d_decode_ip, &s_h264d_decode_op);
+ if (0 == ps_decode_op->u4_output_present) {
resetPlugin();
break;
}
@@ -411,13 +420,19 @@
}
status_t C2SoftAvcDec::createDecoder() {
- ivdext_create_ip_t s_create_ip;
- ivdext_create_op_t s_create_op;
+ ivdext_create_ip_t s_create_ip = {};
+ ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
- s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat;
+ s_create_ip.s_ivd_create_ip_t.e_output_format = GetIvColorFormat();
+ switch (s_create_ip.s_ivd_create_ip_t.e_output_format) {
+ case IV_YUV_420P: ALOGD("Flex Planar"); break;
+ case IV_YUV_420SP_UV: ALOGD("Flex Semi-planar UV"); break;
+ case IV_YUV_420SP_VU: ALOGD("Flex Semi-planar VU"); break;
+ default: ALOGD("Unknown"); break;
+ }
s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
@@ -438,8 +453,8 @@
}
status_t C2SoftAvcDec::setNumCores() {
- ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
- ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+ ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+ ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -458,22 +473,26 @@
}
status_t C2SoftAvcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
- ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
- ivd_ctl_set_config_op_t s_set_dyn_params_op;
+ ih264d_ctl_set_config_ip_t s_h264d_set_dyn_params_ip = {};
+ ih264d_ctl_set_config_op_t s_h264d_set_dyn_params_op = {};
+ ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+ &s_h264d_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+ ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+ &s_h264d_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
- s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
- s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
- s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
- s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
- s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
- s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
- s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
- s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+ ps_set_dyn_params_ip->u4_size = sizeof(ih264d_ctl_set_config_ip_t);
+ ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+ ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ ps_set_dyn_params_ip->u4_disp_wd = (UWORD32) stride;
+ ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+ ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+ ps_set_dyn_params_op->u4_size = sizeof(ih264d_ctl_set_config_op_t);
IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
- &s_set_dyn_params_ip,
- &s_set_dyn_params_op);
+ &s_h264d_set_dyn_params_ip,
+ &s_h264d_set_dyn_params_op);
if (status != IV_SUCCESS) {
- ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+ ALOGE("error in %s: 0x%x", __func__, ps_set_dyn_params_op->u4_error_code);
return UNKNOWN_ERROR;
}
@@ -481,8 +500,8 @@
}
void C2SoftAvcDec::getVersion() {
- ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
- ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+ ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip = {};
+ ivd_ctl_getversioninfo_op_t s_get_versioninfo_op = {};
UWORD8 au1_buf[512];
s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
@@ -538,7 +557,7 @@
if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
}
- ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_decode_ip->u4_size = sizeof(ih264d_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
ps_decode_ip->u4_ts = tsMarker;
@@ -550,8 +569,12 @@
ps_decode_ip->u4_num_Bytes = 0;
}
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
- ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
- ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+ if (GetIvColorFormat() == IV_YUV_420P) {
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+ } else {
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize * 2;
+ }
if (outBuffer) {
if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
@@ -560,21 +583,31 @@
}
ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
- ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+ if (GetIvColorFormat() == IV_YUV_420P) {
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+ } else if (GetIvColorFormat() == IV_YUV_420SP_VU) {
+ ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+ }
} else {
ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferFlush;
ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferFlush + lumaSize;
- ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferFlush + lumaSize + chromaSize;
+ if (GetIvColorFormat() == IV_YUV_420P) {
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferFlush + lumaSize + chromaSize;
+ }
}
- ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
- ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+ if (GetIvColorFormat() == IV_YUV_420P) {
+ ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+ } else {
+ ps_decode_ip->s_out_buffer.u4_num_bufs = 2;
+ }
+ ps_decode_op->u4_size = sizeof(ih264d_video_decode_op_t);
return true;
}
bool C2SoftAvcDec::getVuiParams() {
- ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip;
- ivdext_ctl_get_vui_params_op_t s_get_vui_params_op;
+ ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip = {};
+ ivdext_ctl_get_vui_params_op_t s_get_vui_params_op = {};
s_get_vui_params_ip.u4_size = sizeof(ivdext_ctl_get_vui_params_ip_t);
s_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -622,8 +655,8 @@
}
status_t C2SoftAvcDec::setFlushMode() {
- ivd_ctl_flush_ip_t s_set_flush_ip;
- ivd_ctl_flush_op_t s_set_flush_op;
+ ivd_ctl_flush_ip_t s_set_flush_ip = {};
+ ivd_ctl_flush_op_t s_set_flush_op = {};
s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -641,8 +674,8 @@
}
status_t C2SoftAvcDec::resetDecoder() {
- ivd_ctl_reset_ip_t s_reset_ip;
- ivd_ctl_reset_op_t s_reset_op;
+ ivd_ctl_reset_ip_t s_reset_ip = {};
+ ivd_ctl_reset_op_t s_reset_op = {};
s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -671,8 +704,8 @@
status_t C2SoftAvcDec::deleteDecoder() {
if (mDecHandle) {
- ivdext_delete_ip_t s_delete_ip;
- ivdext_delete_op_t s_delete_op;
+ ivdext_delete_ip_t s_delete_ip = {};
+ ivdext_delete_op_t s_delete_op = {};
s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
@@ -776,7 +809,7 @@
mOutBlock.reset();
}
if (!mOutBlock) {
- uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
c2_status_t err =
pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
@@ -792,8 +825,6 @@
}
// TODO: can overall error checking be improved?
-// TODO: allow configuration of color format and usage for graphic buffers instead
-// of hard coding them to HAL_PIXEL_FORMAT_YV12
// TODO: pass coloraspects information to surface
// TODO: test support for dynamic change in resolution
// TODO: verify if the decoder sent back all frames
@@ -837,8 +868,10 @@
return;
}
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
+ ih264d_video_decode_ip_t s_h264d_decode_ip = {};
+ ih264d_video_decode_op_t s_h264d_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
{
C2GraphicView wView = mOutBlock->map().get();
if (wView.error()) {
@@ -846,7 +879,7 @@
work->result = wView.error();
return;
}
- if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+ if (!setDecodeArgs(ps_decode_ip, ps_decode_op, &rView, &wView,
inOffset + inPos, inSize - inPos, workIndex)) {
mSignalledError = true;
work->workletsProcessed = 1u;
@@ -862,26 +895,27 @@
WORD32 delay;
GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ (void) ivdec_api_function(mDecHandle, &s_h264d_decode_ip, &s_h264d_decode_op);
WORD32 decodeTime;
GETTIME(&mTimeEnd, nullptr);
TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay,
- s_decode_op.u4_num_bytes_consumed);
+ ps_decode_op->u4_num_bytes_consumed);
}
- if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ if (IVD_MEM_ALLOC_FAILED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGE("allocation failure in decoder");
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
- } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED ==
+ (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
- } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ } else if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGV("resolution changed");
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetDecoder();
@@ -890,16 +924,16 @@
/* Decode header and get new dimensions */
setParams(mStride, IVD_DECODE_HEADER);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) {
- ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code);
+ (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ } else if (IS_IVD_FATAL_ERROR(ps_decode_op->u4_error_code)) {
+ ALOGE("Fatal error in decoder 0x%x", ps_decode_op->u4_error_code);
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
- if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
- mOutputDelay = s_decode_op.i4_reorder_depth;
+ if (ps_decode_op->i4_reorder_depth >= 0 && mOutputDelay != ps_decode_op->i4_reorder_depth) {
+ mOutputDelay = ps_decode_op->i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
@@ -917,16 +951,16 @@
return;
}
}
- if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+ if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- mStride = ALIGN32(s_decode_op.u4_pic_wd);
+ mStride = ALIGN32(ps_decode_op->u4_pic_wd);
setParams(mStride, IVD_DECODE_FRAME);
}
- if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
- mWidth = s_decode_op.u4_pic_wd;
- mHeight = s_decode_op.u4_pic_ht;
- CHECK_EQ(0u, s_decode_op.u4_output_present);
+ if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) {
+ mWidth = ps_decode_op->u4_pic_wd;
+ mHeight = ps_decode_op->u4_pic_ht;
+ CHECK_EQ(0u, ps_decode_op->u4_output_present);
C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
std::vector<std::unique_ptr<C2SettingResult>> failures;
@@ -945,11 +979,11 @@
}
}
(void)getVuiParams();
- hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
- if (s_decode_op.u4_output_present) {
- finishWork(s_decode_op.u4_ts, work);
+ hasPicture |= (1 == ps_decode_op->u4_frame_decoded_flag);
+ if (ps_decode_op->u4_output_present) {
+ finishWork(ps_decode_op->u4_ts, work);
}
- inPos += s_decode_op.u4_num_bytes_consumed;
+ inPos += ps_decode_op->u4_num_bytes_consumed;
}
if (eos) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
@@ -987,16 +1021,18 @@
ALOGE("graphic view map failed %d", wView.error());
return C2_CORRUPTED;
}
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
- if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+ ih264d_video_decode_ip_t s_h264d_decode_ip = {};
+ ih264d_video_decode_op_t s_h264d_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
+ if (!setDecodeArgs(ps_decode_ip, ps_decode_op, nullptr, &wView, 0, 0, 0)) {
mSignalledError = true;
work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- if (s_decode_op.u4_output_present) {
- finishWork(s_decode_op.u4_ts, work);
+ (void) ivdec_api_function(mDecHandle, &s_h264d_decode_ip, &s_h264d_decode_op);
+ if (ps_decode_op->u4_output_present) {
+ finishWork(ps_decode_op->u4_ts, work);
} else {
fillEmptyWork(work);
break;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index bd84de0..ed99ad1 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -25,8 +25,7 @@
#include <SimpleC2Component.h>
#include "ih264_typedefs.h"
-#include "iv.h"
-#include "ivd.h"
+#include "ih264d.h"
namespace android {
@@ -156,7 +155,6 @@
uint8_t *mOutBufferFlush;
size_t mNumCores;
- IV_COLOR_FORMAT_T mIvColorFormat;
uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 940f57c..fc5b75d 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -454,11 +454,19 @@
} // namespace
+static IV_COLOR_FORMAT_T GetIvColorFormat() {
+ static IV_COLOR_FORMAT_T sColorFormat =
+ (GetYuv420FlexibleLayout() == FLEX_LAYOUT_SEMIPLANAR_UV) ? IV_YUV_420SP_UV :
+ (GetYuv420FlexibleLayout() == FLEX_LAYOUT_SEMIPLANAR_VU) ? IV_YUV_420SP_VU :
+ IV_YUV_420P;
+ return sColorFormat;
+}
+
C2SoftAvcEnc::C2SoftAvcEnc(
const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl)
: SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
mIntf(intfImpl),
- mIvVideoColorFormat(IV_YUV_420P),
+ mIvVideoColorFormat(GetIvColorFormat()),
mAVCEncProfile(IV_PROFILE_BASE),
mAVCEncLevel(41),
mStarted(false),
@@ -1026,8 +1034,7 @@
// Assume worst case output buffer size to be equal to number of bytes in input
mOutBufferSize = std::max(width * height * 3 / 2, kMinOutBufferSize);
- // TODO
- mIvVideoColorFormat = IV_YUV_420P;
+ mIvVideoColorFormat = GetIvColorFormat();
ALOGD("Params width %d height %d level %d colorFormat %d bframes %d", width,
height, mAVCEncLevel, mIvVideoColorFormat, mBframes);
@@ -1082,29 +1089,31 @@
/* Getting MemRecords Attributes */
{
- iv_fill_mem_rec_ip_t s_fill_mem_rec_ip;
- iv_fill_mem_rec_op_t s_fill_mem_rec_op;
+ ih264e_fill_mem_rec_ip_t s_ih264e_mem_rec_ip = {};
+ ih264e_fill_mem_rec_op_t s_ih264e_mem_rec_op = {};
+ iv_fill_mem_rec_ip_t *ps_fill_mem_rec_ip = &s_ih264e_mem_rec_ip.s_ive_ip;
+ iv_fill_mem_rec_op_t *ps_fill_mem_rec_op = &s_ih264e_mem_rec_op.s_ive_op;
- s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t);
- s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t);
+ ps_fill_mem_rec_ip->u4_size = sizeof(ih264e_fill_mem_rec_ip_t);
+ ps_fill_mem_rec_op->u4_size = sizeof(ih264e_fill_mem_rec_op_t);
- s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
- s_fill_mem_rec_ip.ps_mem_rec = mMemRecords;
- s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords;
- s_fill_mem_rec_ip.u4_max_wd = width;
- s_fill_mem_rec_ip.u4_max_ht = height;
- s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel;
- s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT;
- s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
- s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
- s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
- s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+ ps_fill_mem_rec_ip->e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ ps_fill_mem_rec_ip->ps_mem_rec = mMemRecords;
+ ps_fill_mem_rec_ip->u4_num_mem_rec = mNumMemRecords;
+ ps_fill_mem_rec_ip->u4_max_wd = width;
+ ps_fill_mem_rec_ip->u4_max_ht = height;
+ ps_fill_mem_rec_ip->u4_max_level = mAVCEncLevel;
+ ps_fill_mem_rec_ip->e_color_format = DEFAULT_INP_COLOR_FORMAT;
+ ps_fill_mem_rec_ip->u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+ ps_fill_mem_rec_ip->u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+ ps_fill_mem_rec_ip->u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+ ps_fill_mem_rec_ip->u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
- status = ive_api_function(nullptr, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+ status = ive_api_function(nullptr, &s_ih264e_mem_rec_ip, &s_ih264e_mem_rec_op);
if (status != IV_SUCCESS) {
ALOGE("Fill memory records failed = 0x%x\n",
- s_fill_mem_rec_op.u4_error_code);
+ ps_fill_mem_rec_op->u4_error_code);
return C2_CORRUPTED;
}
}
@@ -1133,48 +1142,51 @@
/* Codec Instance Creation */
{
- ive_init_ip_t s_init_ip;
- ive_init_op_t s_init_op;
+ ih264e_init_ip_t s_enc_ip = {};
+ ih264e_init_op_t s_enc_op = {};
+
+ ive_init_ip_t *ps_init_ip = &s_enc_ip.s_ive_ip;
+ ive_init_op_t *ps_init_op = &s_enc_op.s_ive_op;
mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
mCodecCtx->u4_size = sizeof(iv_obj_t);
mCodecCtx->pv_fxns = (void *)ive_api_function;
- s_init_ip.u4_size = sizeof(ive_init_ip_t);
- s_init_op.u4_size = sizeof(ive_init_op_t);
+ ps_init_ip->u4_size = sizeof(ih264e_init_ip_t);
+ ps_init_op->u4_size = sizeof(ih264e_init_op_t);
- s_init_ip.e_cmd = IV_CMD_INIT;
- s_init_ip.u4_num_mem_rec = mNumMemRecords;
- s_init_ip.ps_mem_rec = mMemRecords;
- s_init_ip.u4_max_wd = width;
- s_init_ip.u4_max_ht = height;
- s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
- s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
- s_init_ip.u4_max_level = mAVCEncLevel;
- s_init_ip.e_inp_color_fmt = mIvVideoColorFormat;
+ ps_init_ip->e_cmd = IV_CMD_INIT;
+ ps_init_ip->u4_num_mem_rec = mNumMemRecords;
+ ps_init_ip->ps_mem_rec = mMemRecords;
+ ps_init_ip->u4_max_wd = width;
+ ps_init_ip->u4_max_ht = height;
+ ps_init_ip->u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+ ps_init_ip->u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+ ps_init_ip->u4_max_level = mAVCEncLevel;
+ ps_init_ip->e_inp_color_fmt = mIvVideoColorFormat;
if (mReconEnable || mPSNREnable) {
- s_init_ip.u4_enable_recon = 1;
+ ps_init_ip->u4_enable_recon = 1;
} else {
- s_init_ip.u4_enable_recon = 0;
+ ps_init_ip->u4_enable_recon = 0;
}
- s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
- s_init_ip.e_rc_mode = DEFAULT_RC_MODE;
- s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE;
- s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE;
- s_init_ip.u4_num_bframes = mBframes;
- s_init_ip.e_content_type = IV_PROGRESSIVE;
- s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
- s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
- s_init_ip.e_slice_mode = mSliceMode;
- s_init_ip.u4_slice_param = mSliceParam;
- s_init_ip.e_arch = mArch;
- s_init_ip.e_soc = DEFAULT_SOC;
+ ps_init_ip->e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
+ ps_init_ip->e_rc_mode = DEFAULT_RC_MODE;
+ ps_init_ip->u4_max_framerate = DEFAULT_MAX_FRAMERATE;
+ ps_init_ip->u4_max_bitrate = DEFAULT_MAX_BITRATE;
+ ps_init_ip->u4_num_bframes = mBframes;
+ ps_init_ip->e_content_type = IV_PROGRESSIVE;
+ ps_init_ip->u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+ ps_init_ip->u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+ ps_init_ip->e_slice_mode = mSliceMode;
+ ps_init_ip->u4_slice_param = mSliceParam;
+ ps_init_ip->e_arch = mArch;
+ ps_init_ip->e_soc = DEFAULT_SOC;
- status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op);
+ status = ive_api_function(mCodecCtx, &s_enc_ip, &s_enc_op);
if (status != IV_SUCCESS) {
- ALOGE("Init encoder failed = 0x%x\n", s_init_op.u4_error_code);
+ ALOGE("Init encoder failed = 0x%x\n", ps_init_op->u4_error_code);
return C2_CORRUPTED;
}
}
@@ -1320,7 +1332,6 @@
mSize->width, input->height(), mSize->height);
return C2_BAD_VALUE;
}
- ALOGV("width = %d, height = %d", input->width(), input->height());
const C2PlanarLayout &layout = input->layout();
uint8_t *yPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
uint8_t *uPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
@@ -1357,7 +1368,8 @@
return C2_BAD_VALUE;
}
- if (layout.planes[layout.PLANE_Y].colInc == 1
+ if (mIvVideoColorFormat == IV_YUV_420P
+ && layout.planes[layout.PLANE_Y].colInc == 1
&& layout.planes[layout.PLANE_U].colInc == 1
&& layout.planes[layout.PLANE_V].colInc == 1
&& uStride == vStride
@@ -1365,21 +1377,61 @@
// I420 compatible - already set up above
break;
}
+ if (mIvVideoColorFormat == IV_YUV_420SP_UV
+ && layout.planes[layout.PLANE_Y].colInc == 1
+ && layout.planes[layout.PLANE_U].colInc == 2
+ && layout.planes[layout.PLANE_V].colInc == 2
+ && uStride == vStride
+ && yStride == vStride
+ && uPlane + 1 == vPlane) {
+ // NV12 compatible - already set up above
+ break;
+ }
+ if (mIvVideoColorFormat == IV_YUV_420SP_VU
+ && layout.planes[layout.PLANE_Y].colInc == 1
+ && layout.planes[layout.PLANE_U].colInc == 2
+ && layout.planes[layout.PLANE_V].colInc == 2
+ && uStride == vStride
+ && yStride == vStride
+ && uPlane == vPlane + 1) {
+ // NV21 compatible - already set up above
+ break;
+ }
// copy to I420
yStride = width;
uStride = vStride = yStride / 2;
MemoryBlock conversionBuffer = mConversionBuffers.fetch(yPlaneSize * 3 / 2);
mConversionBuffersInUse.emplace(conversionBuffer.data(), conversionBuffer);
- MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, yStride, height);
+ MediaImage2 img;
+ switch (mIvVideoColorFormat) {
+ case IV_YUV_420P:
+ img = CreateYUV420PlanarMediaImage2(width, height, yStride, height);
+ yPlane = conversionBuffer.data();
+ uPlane = yPlane + yPlaneSize;
+ vPlane = uPlane + yPlaneSize / 4;
+ break;
+ case IV_YUV_420SP_VU:
+ img = CreateYUV420SemiPlanarMediaImage2(width, height, yStride, height);
+ img.mPlane[MediaImage2::U].mOffset++;
+ img.mPlane[MediaImage2::V].mOffset--;
+ yPlane = conversionBuffer.data();
+ vPlane = yPlane + yPlaneSize;
+ uPlane = vPlane + 1;
+ break;
+ case IV_YUV_420SP_UV:
+ default:
+ img = CreateYUV420SemiPlanarMediaImage2(width, height, yStride, height);
+ yPlane = conversionBuffer.data();
+ uPlane = yPlane + yPlaneSize;
+ vPlane = uPlane + 1;
+ break;
+ }
status_t err = ImageCopy(conversionBuffer.data(), &img, *input);
if (err != OK) {
ALOGE("Buffer conversion failed: %d", err);
return C2_BAD_VALUE;
}
- yPlane = conversionBuffer.data();
- uPlane = yPlane + yPlaneSize;
- vPlane = uPlane + yPlaneSize / 4;
break;
}
@@ -1401,13 +1453,13 @@
ps_inp_raw_buf->apv_bufs[1] = uPlane;
ps_inp_raw_buf->apv_bufs[2] = vPlane;
- ps_inp_raw_buf->au4_wd[0] = input->width();
- ps_inp_raw_buf->au4_wd[1] = input->width() / 2;
- ps_inp_raw_buf->au4_wd[2] = input->width() / 2;
+ ps_inp_raw_buf->au4_wd[0] = mSize->width;
+ ps_inp_raw_buf->au4_wd[1] = mSize->width / 2;
+ ps_inp_raw_buf->au4_wd[2] = mSize->width / 2;
- ps_inp_raw_buf->au4_ht[0] = input->height();
- ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
- ps_inp_raw_buf->au4_ht[2] = input->height() / 2;
+ ps_inp_raw_buf->au4_ht[0] = mSize->height;
+ ps_inp_raw_buf->au4_ht[1] = mSize->height / 2;
+ ps_inp_raw_buf->au4_ht[2] = mSize->height / 2;
ps_inp_raw_buf->au4_strd[0] = yStride;
ps_inp_raw_buf->au4_strd[1] = uStride;
@@ -1425,18 +1477,20 @@
break;
}
- case IV_YUV_420SP_UV:
case IV_YUV_420SP_VU:
+ uPlane = vPlane;
+ [[fallthrough]];
+ case IV_YUV_420SP_UV:
default:
{
ps_inp_raw_buf->apv_bufs[0] = yPlane;
ps_inp_raw_buf->apv_bufs[1] = uPlane;
- ps_inp_raw_buf->au4_wd[0] = input->width();
- ps_inp_raw_buf->au4_wd[1] = input->width();
+ ps_inp_raw_buf->au4_wd[0] = mSize->width;
+ ps_inp_raw_buf->au4_wd[1] = mSize->width / 2;
- ps_inp_raw_buf->au4_ht[0] = input->height();
- ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+ ps_inp_raw_buf->au4_ht[0] = mSize->height;
+ ps_inp_raw_buf->au4_ht[1] = mSize->height / 2;
ps_inp_raw_buf->au4_strd[0] = yStride;
ps_inp_raw_buf->au4_strd[1] = uStride;
@@ -1502,15 +1556,17 @@
}
// while (!mSawOutputEOS && !outQueue.empty()) {
c2_status_t error;
- ive_video_encode_ip_t s_encode_ip;
- ive_video_encode_op_t s_encode_op;
- memset(&s_encode_op, 0, sizeof(s_encode_op));
+ ih264e_video_encode_ip_t s_video_encode_ip = {};
+ ih264e_video_encode_op_t s_video_encode_op = {};
+ ive_video_encode_ip_t *ps_encode_ip = &s_video_encode_ip.s_ive_ip;
+ ive_video_encode_op_t *ps_encode_op = &s_video_encode_op.s_ive_op;
+ memset(ps_encode_op, 0, sizeof(*ps_encode_op));
if (!mSpsPpsHeaderReceived) {
constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE;
uint8_t header[kHeaderLength];
error = setEncodeArgs(
- &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, workIndex);
+ ps_encode_ip, ps_encode_op, nullptr, header, kHeaderLength, workIndex);
if (error != C2_OK) {
ALOGE("setEncodeArgs failed: %d", error);
mSignalledError = true;
@@ -1518,22 +1574,22 @@
work->workletsProcessed = 1u;
return;
}
- status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+ status = ive_api_function(mCodecCtx, ps_encode_ip, ps_encode_op);
if (IV_SUCCESS != status) {
ALOGE("Encode header failed = 0x%x\n",
- s_encode_op.u4_error_code);
+ ps_encode_op->u4_error_code);
work->workletsProcessed = 1u;
return;
} else {
ALOGV("Bytes Generated in header %d\n",
- s_encode_op.s_out_buf.u4_bytes);
+ ps_encode_op->s_out_buf.u4_bytes);
}
mSpsPpsHeaderReceived = true;
std::unique_ptr<C2StreamInitDataInfo::output> csd =
- C2StreamInitDataInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u);
+ C2StreamInitDataInfo::output::AllocUnique(ps_encode_op->s_out_buf.u4_bytes, 0u);
if (!csd) {
ALOGE("CSD allocation failed");
mSignalledError = true;
@@ -1541,7 +1597,7 @@
work->workletsProcessed = 1u;
return;
}
- memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
+ memcpy(csd->m.value, header, ps_encode_op->s_out_buf.u4_bytes);
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
DUMP_TO_FILE(
@@ -1635,7 +1691,7 @@
}
error = setEncodeArgs(
- &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), workIndex);
+ ps_encode_ip, ps_encode_op, view.get(), wView.base(), wView.capacity(), workIndex);
if (error != C2_OK) {
ALOGE("setEncodeArgs failed : %d", error);
mSignalledError = true;
@@ -1652,17 +1708,17 @@
/* Compute time elapsed between end of previous decode()
* to start of current decode() */
TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
- status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+ status = ive_api_function(mCodecCtx, &s_video_encode_ip, &s_video_encode_op);
if (IV_SUCCESS != status) {
- if ((s_encode_op.u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) {
+ if ((ps_encode_op->u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) {
// TODO: use IVE_CMD_CTL_GETBUFINFO for proper max input size?
mOutBufferSize *= 2;
mOutBlock.reset();
continue;
}
ALOGE("Encode Frame failed = 0x%x\n",
- s_encode_op.u4_error_code);
+ ps_encode_op->u4_error_code);
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
@@ -1672,7 +1728,7 @@
// Hold input buffer reference
if (inputBuffer) {
- mBuffers[s_encode_ip.s_inp_buf.apv_bufs[0]] = inputBuffer;
+ mBuffers[ps_encode_ip->s_inp_buf.apv_bufs[0]] = inputBuffer;
}
GETTIME(&mTimeEnd, nullptr);
@@ -1680,9 +1736,9 @@
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
- s_encode_op.s_out_buf.u4_bytes);
+ ps_encode_op->s_out_buf.u4_bytes);
- void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+ void *freed = ps_encode_op->s_inp_buf.apv_bufs[0];
/* If encoder frees up an input buffer, mark it as free */
if (freed != nullptr) {
if (mBuffers.count(freed) == 0u) {
@@ -1694,17 +1750,17 @@
}
}
- if (s_encode_op.output_present) {
- if (!s_encode_op.s_out_buf.u4_bytes) {
+ if (ps_encode_op->output_present) {
+ if (!ps_encode_op->s_out_buf.u4_bytes) {
ALOGE("Error: Output present but bytes generated is zero");
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return;
}
- uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
- s_encode_op.u4_timestamp_low;
- finishWork(workId, work, &s_encode_op);
+ uint64_t workId = ((uint64_t)ps_encode_op->u4_timestamp_high << 32) |
+ ps_encode_op->u4_timestamp_low;
+ finishWork(workId, work, ps_encode_op);
}
if (mSawInputEOS) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
@@ -1744,9 +1800,11 @@
ALOGE("graphic view map failed %d", wView.error());
return C2_CORRUPTED;
}
- ive_video_encode_ip_t s_encode_ip;
- ive_video_encode_op_t s_encode_op;
- if (C2_OK != setEncodeArgs(&s_encode_ip, &s_encode_op, nullptr,
+ ih264e_video_encode_ip_t s_video_encode_ip = {};
+ ih264e_video_encode_op_t s_video_encode_op = {};
+ ive_video_encode_ip_t *ps_encode_ip = &s_video_encode_ip.s_ive_ip;
+ ive_video_encode_op_t *ps_encode_op = &s_video_encode_op.s_ive_op;
+ if (C2_OK != setEncodeArgs(ps_encode_ip, ps_encode_op, nullptr,
wView.base(), wView.capacity(), 0)) {
ALOGE("setEncodeArgs failed for drainInternal");
mSignalledError = true;
@@ -1754,9 +1812,9 @@
work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
- (void)ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+ (void)ive_api_function(mCodecCtx, &s_video_encode_ip, &s_video_encode_op);
- void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+ void *freed = ps_encode_op->s_inp_buf.apv_bufs[0];
/* If encoder frees up an input buffer, mark it as free */
if (freed != nullptr) {
if (mBuffers.count(freed) == 0u) {
@@ -1768,10 +1826,10 @@
}
}
- if (s_encode_op.output_present) {
- uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
- s_encode_op.u4_timestamp_low;
- finishWork(workId, work, &s_encode_op);
+ if (ps_encode_op->output_present) {
+ uint64_t workId = ((uint64_t)ps_encode_op->u4_timestamp_high << 32) |
+ ps_encode_op->u4_timestamp_low;
+ finishWork(workId, work, ps_encode_op);
} else {
if (work->workletsProcessed != 1u) {
work->worklets.front()->output.flags = work->input.flags;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index ee6d47a..673a282 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -24,8 +24,7 @@
#include <SimpleC2Component.h>
#include "ih264_typedefs.h"
-#include "iv2.h"
-#include "ive2.h"
+#include "ih264e.h"
namespace android {
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index a374dfa..6bcf3a2 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -26,7 +26,6 @@
#include <SimpleC2Interface.h>
#include "C2SoftHevcDec.h"
-#include "ihevcd_cxa.h"
namespace android {
@@ -380,12 +379,14 @@
}
while (true) {
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
+ ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
+ ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_hevcd_decode_op.s_ivd_video_decode_op_t;
- setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- if (0 == s_decode_op.u4_output_present) {
+ setDecodeArgs(ps_decode_ip, ps_decode_op, nullptr, nullptr, 0, 0, 0);
+ (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ if (0 == ps_decode_op->u4_output_present) {
resetPlugin();
break;
}
@@ -400,8 +401,8 @@
}
status_t C2SoftHevcDec::createDecoder() {
- ivdext_create_ip_t s_create_ip;
- ivdext_create_op_t s_create_op;
+ ivdext_create_ip_t s_create_ip = {};
+ ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
@@ -427,8 +428,8 @@
}
status_t C2SoftHevcDec::setNumCores() {
- ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
- ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+ ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+ ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -447,22 +448,26 @@
}
status_t C2SoftHevcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
- ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
- ivd_ctl_set_config_op_t s_set_dyn_params_op;
+ ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {};
+ ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {};
+ ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+ &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+ ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+ &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
- s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
- s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
- s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
- s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
- s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
- s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
- s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
- s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+ ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t);
+ ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+ ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ ps_set_dyn_params_ip->u4_disp_wd = (UWORD32) stride;
+ ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+ ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+ ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t);
IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
- &s_set_dyn_params_ip,
- &s_set_dyn_params_op);
+ ps_set_dyn_params_ip,
+ ps_set_dyn_params_op);
if (status != IV_SUCCESS) {
- ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+ ALOGE("error in %s: 0x%x", __func__, ps_set_dyn_params_op->u4_error_code);
return UNKNOWN_ERROR;
}
@@ -470,8 +475,8 @@
}
status_t C2SoftHevcDec::getVersion() {
- ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
- ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+ ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip = {};
+ ivd_ctl_getversioninfo_op_t s_get_versioninfo_op = {};
UWORD8 au1_buf[512];
s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
@@ -529,7 +534,7 @@
if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
}
- ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
ps_decode_ip->u4_ts = tsMarker;
@@ -558,15 +563,15 @@
ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferFlush + lumaSize + chromaSize;
}
ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
- ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+ ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t);
ps_decode_op->u4_output_present = 0;
return true;
}
bool C2SoftHevcDec::getVuiParams() {
- ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip;
- ivdext_ctl_get_vui_params_op_t s_get_vui_params_op;
+ ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip = {};
+ ivdext_ctl_get_vui_params_op_t s_get_vui_params_op = {};
s_get_vui_params_ip.u4_size = sizeof(ivdext_ctl_get_vui_params_ip_t);
s_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -614,8 +619,8 @@
}
status_t C2SoftHevcDec::setFlushMode() {
- ivd_ctl_flush_ip_t s_set_flush_ip;
- ivd_ctl_flush_op_t s_set_flush_op;
+ ivd_ctl_flush_ip_t s_set_flush_ip = {};
+ ivd_ctl_flush_op_t s_set_flush_op = {};
s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -633,8 +638,8 @@
}
status_t C2SoftHevcDec::resetDecoder() {
- ivd_ctl_reset_ip_t s_reset_ip;
- ivd_ctl_reset_op_t s_reset_op;
+ ivd_ctl_reset_ip_t s_reset_ip = {};
+ ivd_ctl_reset_op_t s_reset_op = {};
s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
@@ -662,8 +667,8 @@
status_t C2SoftHevcDec::deleteDecoder() {
if (mDecHandle) {
- ivdext_delete_ip_t s_delete_ip;
- ivdext_delete_op_t s_delete_op;
+ ivdext_delete_ip_t s_delete_ip = {};
+ ivdext_delete_op_t s_delete_op = {};
s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
@@ -835,9 +840,11 @@
work->result = wView.error();
return;
}
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
- if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+ ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
+ ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_hevcd_decode_op.s_ivd_video_decode_op_t;
+ if (!setDecodeArgs(ps_decode_ip, ps_decode_op, &rView, &wView,
inOffset + inPos, inSize - inPos, workIndex)) {
mSignalledError = true;
work->workletsProcessed = 1u;
@@ -852,26 +859,26 @@
WORD32 delay;
GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
WORD32 decodeTime;
GETTIME(&mTimeEnd, nullptr);
TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay,
- s_decode_op.u4_num_bytes_consumed);
- if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ ps_decode_op->u4_num_bytes_consumed);
+ if (IVD_MEM_ALLOC_FAILED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGE("allocation failure in decoder");
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
} else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED ==
- (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
- } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+ } else if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
ALOGV("resolution changed");
drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
resetDecoder();
@@ -880,16 +887,16 @@
/* Decode header and get new dimensions */
setParams(mStride, IVD_DECODE_HEADER);
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) {
- ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code);
+ (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ } else if (IS_IVD_FATAL_ERROR(ps_decode_op->u4_error_code)) {
+ ALOGE("Fatal error in decoder 0x%x", ps_decode_op->u4_error_code);
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
- if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
- mOutputDelay = s_decode_op.i4_reorder_depth;
+ if (ps_decode_op->i4_reorder_depth >= 0 && mOutputDelay != ps_decode_op->i4_reorder_depth) {
+ mOutputDelay = ps_decode_op->i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
@@ -907,15 +914,15 @@
return;
}
}
- if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+ if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN32(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN32(ps_decode_op->u4_pic_wd), IVD_DECODE_FRAME);
}
- if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
- mWidth = s_decode_op.u4_pic_wd;
- mHeight = s_decode_op.u4_pic_ht;
- CHECK_EQ(0u, s_decode_op.u4_output_present);
+ if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) {
+ mWidth = ps_decode_op->u4_pic_wd;
+ mHeight = ps_decode_op->u4_pic_ht;
+ CHECK_EQ(0u, ps_decode_op->u4_output_present);
C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
std::vector<std::unique_ptr<C2SettingResult>> failures;
@@ -935,15 +942,15 @@
}
}
(void) getVuiParams();
- hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
- if (s_decode_op.u4_output_present) {
- finishWork(s_decode_op.u4_ts, work);
+ hasPicture |= (1 == ps_decode_op->u4_frame_decoded_flag);
+ if (ps_decode_op->u4_output_present) {
+ finishWork(ps_decode_op->u4_ts, work);
}
- if (0 == s_decode_op.u4_num_bytes_consumed) {
+ if (0 == ps_decode_op->u4_num_bytes_consumed) {
ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
break;
}
- inPos += s_decode_op.u4_num_bytes_consumed;
+ inPos += ps_decode_op->u4_num_bytes_consumed;
if (hasPicture && (inSize - inPos)) {
ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
(int)inSize - (int)inPos);
@@ -985,16 +992,18 @@
ALOGE("graphic view map failed %d", wView.error());
return C2_CORRUPTED;
}
- ivd_video_decode_ip_t s_decode_ip;
- ivd_video_decode_op_t s_decode_op;
- if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+ ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
+ ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_hevcd_decode_op.s_ivd_video_decode_op_t;
+ if (!setDecodeArgs(ps_decode_ip, ps_decode_op, nullptr, &wView, 0, 0, 0)) {
mSignalledError = true;
work->workletsProcessed = 1u;
return C2_CORRUPTED;
}
- (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
- if (s_decode_op.u4_output_present) {
- finishWork(s_decode_op.u4_ts, work);
+ (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ if (ps_decode_op->u4_output_present) {
+ finishWork(ps_decode_op->u4_ts, work);
} else {
fillEmptyWork(work);
break;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index 600d7c1..b9b0a48 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -23,8 +23,7 @@
#include <SimpleC2Component.h>
#include "ihevc_typedefs.h"
-#include "iv.h"
-#include "ivd.h"
+#include "ihevcd_cxa.h"
namespace android {
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index 3a47ae9..1f95eaf 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -39,8 +39,44 @@
static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
+struct CompToURL {
+ std::string mime;
+ std::string mURL;
+ std::string info;
+};
+
+std::vector<CompToURL> kCompToURL = {
+ {"mp4a-latm",
+ "bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.info"},
+ {"mp4a-latm",
+ "bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"},
+ {"audio/mpeg",
+ "bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.info"},
+ {"audio/mpeg",
+ "bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"},
+ {"3gpp",
+ "sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.info"},
+ {"3gpp",
+ "sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"},
+ {"amr-wb",
+ "bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.info"},
+ {"amr-wb",
+ "bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"},
+ {"vorbis",
+ "bbb_vorbis_stereo_128kbps_48000hz.vorbis", "bbb_vorbis_stereo_128kbps_48000hz.info"},
+ {"opus",
+ "bbb_opus_stereo_128kbps_48000hz.opus", "bbb_opus_stereo_128kbps_48000hz.info"},
+ {"g711-alaw",
+ "bbb_g711alaw_1ch_8khz.raw", "bbb_g711alaw_1ch_8khz.info"},
+ {"g711-mlaw",
+ "bbb_g711mulaw_1ch_8khz.raw", "bbb_g711mulaw_1ch_8khz.info"},
+ {"gsm",
+ "bbb_gsm_1ch_8khz_13kbps.raw", "bbb_gsm_1ch_8khz_13kbps.info"},
+ {"raw",
+ "bbb_raw_1ch_8khz_s32le.raw", "bbb_raw_1ch_8khz_s32le.info"},
+ {"flac",
+ "bbb_flac_stereo_680kbps_48000hz.flac", "bbb_flac_stereo_680kbps_48000hz.info"},
+};
class LinearBuffer : public C2Buffer {
public:
@@ -76,33 +112,17 @@
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
- const StringToName kStringToName[] = {
- {"xaac", xaac}, {"mp3", mp3}, {"amrnb", amrnb},
- {"amrwb", amrwb}, {"aac", aac}, {"vorbis", vorbis},
- {"opus", opus}, {"pcm", pcm}, {"g711.alaw", g711alaw},
- {"g711.mlaw", g711mlaw}, {"gsm", gsm}, {"raw", raw},
- {"flac", flac},
- };
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK, &queried);
+ ASSERT_GT(queried.size(), 0);
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::input*)queried[0].get())->m.value;
+
mEos = false;
mFramesReceived = 0;
mTimestampUs = 0u;
mWorkResult = C2_OK;
mTimestampDevTest = false;
- if (mCompName == unknown_comp) mDisableTest = true;
if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
}
@@ -119,6 +139,8 @@
virtual void validateTimestampList(int32_t* bitStreamInfo);
+ void GetURLForComponent(char* mURL, char* info, size_t streamIndex = 0);
+
struct outputMetaData {
uint64_t timestampUs;
uint32_t rangeLength;
@@ -158,29 +180,12 @@
}
}
- enum standardComp {
- xaac,
- mp3,
- amrnb,
- amrwb,
- aac,
- vorbis,
- opus,
- pcm,
- g711alaw,
- g711mlaw,
- gsm,
- raw,
- flac,
- unknown_comp,
- };
-
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
bool mEos;
bool mDisableTest;
bool mTimestampDevTest;
- standardComp mCompName;
int32_t mWorkResult;
uint64_t mTimestampUs;
@@ -217,7 +222,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -244,13 +249,6 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2AudioDecHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
@@ -271,7 +269,7 @@
// parsing the header of elementary stream. Client needs to collect this
// information and reconfigure
void getInputChannelInfo(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, int32_t* bitStreamInfo) {
+ std::string mime, int32_t* bitStreamInfo) {
// query nSampleRate and nChannels
std::initializer_list<C2Param::Index> indices{
C2StreamSampleRateInfo::output::PARAM_TYPE,
@@ -288,89 +286,29 @@
C2Param* param = inParams[i].get();
bitStreamInfo[i] = *(int32_t*)((uint8_t*)param + offset);
}
- switch (compName) {
- case Codec2AudioDecHidlTest::amrnb: {
- ASSERT_EQ(bitStreamInfo[0], 8000);
- ASSERT_EQ(bitStreamInfo[1], 1);
- break;
- }
- case Codec2AudioDecHidlTest::amrwb: {
- ASSERT_EQ(bitStreamInfo[0], 16000);
- ASSERT_EQ(bitStreamInfo[1], 1);
- break;
- }
- case Codec2AudioDecHidlTest::gsm: {
- ASSERT_EQ(bitStreamInfo[0], 8000);
- break;
- }
- default:
- break;
+ if (mime.find("3gpp") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 8000);
+ ASSERT_EQ(bitStreamInfo[1], 1);
+ } else if (mime.find("amr-wb") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 16000);
+ ASSERT_EQ(bitStreamInfo[1], 1);
+ } else if (mime.find("gsm") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 8000);
}
}
}
-// number of elementary streams per component
-#define STREAM_COUNT 2
-
// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, char* info,
- size_t streamIndex = 0) {
- struct CompToURL {
- Codec2AudioDecHidlTest::standardComp comp;
- const char mURL[STREAM_COUNT][512];
- const char info[STREAM_COUNT][512];
- };
- ASSERT_TRUE(streamIndex < STREAM_COUNT);
-
- static const CompToURL kCompToURL[] = {
- {Codec2AudioDecHidlTest::standardComp::xaac,
- {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::mp3,
- {"bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.mp3"},
- {"bbb_mp3_stereo_192kbps_48000hz.info",
- "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::aac,
- {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrnb,
- {"sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.amrnb"},
- {"sine_amrnb_1ch_12kbps_8000hz.info",
- "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrwb,
- {"bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.amrwb"},
- {"bbb_amrwb_1ch_14kbps_16000hz.info",
- "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::vorbis,
- {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""},
- {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::opus,
- {"bbb_opus_stereo_128kbps_48000hz.opus", ""},
- {"bbb_opus_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711alaw,
- {"bbb_g711alaw_1ch_8khz.raw", ""},
- {"bbb_g711alaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711mlaw,
- {"bbb_g711mulaw_1ch_8khz.raw", ""},
- {"bbb_g711mulaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::gsm,
- {"bbb_gsm_1ch_8khz_13kbps.raw", ""},
- {"bbb_gsm_1ch_8khz_13kbps.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::raw,
- {"bbb_raw_1ch_8khz_s32le.raw", ""},
- {"bbb_raw_1ch_8khz_s32le.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::flac,
- {"bbb_flac_stereo_680kbps_48000hz.flac", ""},
- {"bbb_flac_stereo_680kbps_48000hz.info", ""}},
- };
-
- for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
- if (kCompToURL[i].comp == comp) {
- strcat(mURL, kCompToURL[i].mURL[streamIndex]);
- strcat(info, kCompToURL[i].info[streamIndex]);
- return;
+void Codec2AudioDecHidlTestBase::GetURLForComponent(char* mURL, char* info, size_t streamIndex) {
+ int streamCount = 0;
+ for (size_t i = 0; i < kCompToURL.size(); ++i) {
+ if (mMime.find(kCompToURL[i].mime) != std::string::npos) {
+ if (streamCount == streamIndex) {
+ strcat(mURL, kCompToURL[i].mURL.c_str());
+ strcat(info, kCompToURL[i].info.c_str());
+ return;
+ }
+ streamCount++;
}
}
}
@@ -461,7 +399,7 @@
void Codec2AudioDecHidlTestBase::validateTimestampList(int32_t* bitStreamInfo) {
uint32_t samplesReceived = 0;
// Update SampleRate and ChannelCount
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
int32_t nSampleRate = bitStreamInfo[0];
int32_t nChannels = bitStreamInfo[1];
std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
@@ -486,7 +424,7 @@
TEST_P(Codec2AudioDecHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid audio component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -495,7 +433,7 @@
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ASSERT_EQ(mComponent->start(), C2_OK);
int32_t bitStreamInfo[2] = {0};
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
setupConfigParam(mComponent, bitStreamInfo);
ASSERT_EQ(mComponent->stop(), C2_OK);
}
@@ -523,7 +461,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info, streamIndex);
+ GetURLForComponent(mURL, info, streamIndex);
if (!strcmp(mURL, sResourceDir.c_str())) {
ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
return;
@@ -536,11 +474,11 @@
mFramesReceived = 0;
mTimestampUs = 0;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -591,17 +529,17 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -683,17 +621,17 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -768,7 +706,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
eleInfo.open(info);
ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
@@ -798,11 +736,11 @@
}
eleInfo.close();
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -853,7 +791,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
if (!strcmp(mURL, sResourceDir.c_str())) {
ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
return;
@@ -864,11 +802,11 @@
ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -951,6 +889,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER);
for (auto params : kTestParameters) {
kDecodeTestParameters.push_back(
@@ -968,15 +907,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index e3a4f68..5e8809e 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -38,9 +38,6 @@
static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
kEncodeTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
-
class LinearBuffer : public C2Buffer {
public:
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
@@ -75,30 +72,17 @@
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
- const StringToName kStringToName[] = {
- {"aac", aac}, {"flac", flac}, {"opus", opus}, {"amrnb", amrnb}, {"amrwb", amrwb},
- };
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::output::PARAM_TYPE}, C2_DONT_BLOCK,
+ &queried);
+ ASSERT_GT(queried.size(), 0);
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::output*)queried[0].get())->m.value;
mEos = false;
mCsd = false;
mFramesReceived = 0;
mWorkResult = C2_OK;
mOutputSize = 0u;
- if (mCompName == unknown_comp) mDisableTest = true;
- if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
getInputMaxBufSize();
}
@@ -113,6 +97,8 @@
// Get the test parameters from GetParam call.
virtual void getParams() {}
+ void GetURLForComponent(char* mURL);
+
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
@@ -133,21 +119,13 @@
}
}
}
- enum standardComp {
- aac,
- flac,
- opus,
- amrnb,
- amrwb,
- unknown_comp,
- };
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
bool mEos;
bool mCsd;
bool mDisableTest;
- standardComp mCompName;
int32_t mWorkResult;
uint32_t mFramesReceived;
@@ -202,7 +180,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioEncHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -229,13 +207,6 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2AudioEncHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
@@ -253,56 +224,49 @@
}
// Get config params for a component
-bool getConfigParams(Codec2AudioEncHidlTest::standardComp compName, int32_t* nChannels,
- int32_t* nSampleRate, int32_t* samplesPerFrame) {
- switch (compName) {
- case Codec2AudioEncHidlTest::aac:
- *nChannels = 2;
- *nSampleRate = 48000;
- *samplesPerFrame = 1024;
- break;
- case Codec2AudioEncHidlTest::flac:
- *nChannels = 2;
- *nSampleRate = 48000;
- *samplesPerFrame = 1152;
- break;
- case Codec2AudioEncHidlTest::opus:
- *nChannels = 2;
- *nSampleRate = 48000;
- *samplesPerFrame = 960;
- break;
- case Codec2AudioEncHidlTest::amrnb:
- *nChannels = 1;
- *nSampleRate = 8000;
- *samplesPerFrame = 160;
- break;
- case Codec2AudioEncHidlTest::amrwb:
- *nChannels = 1;
- *nSampleRate = 16000;
- *samplesPerFrame = 160;
- break;
- default:
- return false;
- }
+bool getConfigParams(std::string mime, int32_t* nChannels, int32_t* nSampleRate,
+ int32_t* samplesPerFrame) {
+ if (mime.find("mp4a-latm") != std::string::npos) {
+ *nChannels = 2;
+ *nSampleRate = 48000;
+ *samplesPerFrame = 1024;
+ } else if (mime.find("flac") != std::string::npos) {
+ *nChannels = 2;
+ *nSampleRate = 48000;
+ *samplesPerFrame = 1152;
+ } else if (mime.find("opus") != std::string::npos) {
+ *nChannels = 2;
+ *nSampleRate = 48000;
+ *samplesPerFrame = 960;
+ } else if (mime.find("3gpp") != std::string::npos) {
+ *nChannels = 1;
+ *nSampleRate = 8000;
+ *samplesPerFrame = 160;
+ } else if (mime.find("amr-wb") != std::string::npos) {
+ *nChannels = 1;
+ *nSampleRate = 16000;
+ *samplesPerFrame = 160;
+ } else return false;
+
return true;
}
// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) {
+void Codec2AudioEncHidlTestBase::GetURLForComponent(char* mURL) {
struct CompToURL {
- Codec2AudioEncHidlTest::standardComp comp;
+ std::string mime;
const char* mURL;
};
static const CompToURL kCompToURL[] = {
- {Codec2AudioEncHidlTest::standardComp::aac, "bbb_raw_2ch_48khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::amrnb, "bbb_raw_1ch_8khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::amrwb, "bbb_raw_1ch_16khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::flac, "bbb_raw_2ch_48khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::opus, "bbb_raw_2ch_48khz_s16le.raw"},
+ {"mp4a-latm", "bbb_raw_2ch_48khz_s16le.raw"},
+ {"3gpp", "bbb_raw_1ch_8khz_s16le.raw"},
+ {"amr-wb", "bbb_raw_1ch_16khz_s16le.raw"},
+ {"flac", "bbb_raw_2ch_48khz_s16le.raw"},
+ {"opus", "bbb_raw_2ch_48khz_s16le.raw"},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
- if (kCompToURL[i].comp == comp) {
+ if (mMime.find(kCompToURL[i].mime) != std::string::npos) {
strcat(mURL, kCompToURL[i].mURL);
return;
}
@@ -395,7 +359,7 @@
TEST_P(Codec2AudioEncHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid audio component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -414,7 +378,7 @@
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512];
strcpy(mURL, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL);
+ GetURLForComponent(mURL);
bool signalEOS = !std::get<2>(GetParam()).compare("true");
// Ratio w.r.t to mInputMaxBufSize
int32_t inputMaxBufRatio = std::stoi(std::get<3>(GetParam()));
@@ -423,8 +387,8 @@
int32_t nSampleRate;
int32_t samplesPerFrame;
- if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
- std::cout << "Failed to get the config params for " << mCompName << " component\n";
+ if (!getConfigParams(mMime, &nChannels, &nSampleRate, &samplesPerFrame)) {
+ std::cout << "Failed to get the config params for " << mComponentName << "\n";
std::cout << "[ WARN ] Test Skipped \n";
return;
}
@@ -464,11 +428,10 @@
ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
ASSERT_TRUE(false);
}
- if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
- if (!mCsd) {
- ALOGE("CSD buffer missing");
- ASSERT_TRUE(false);
- }
+ if ((mMime.find("flac") != std::string::npos) ||
+ (mMime.find("opus") != std::string::npos) ||
+ (mMime.find("mp4a-latm") != std::string::npos)) {
+ ASSERT_TRUE(mCsd) << "CSD buffer missing";
}
ASSERT_EQ(mEos, true);
ASSERT_EQ(mComponent->stop(), C2_OK);
@@ -522,15 +485,15 @@
char mURL[512];
strcpy(mURL, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL);
+ GetURLForComponent(mURL);
mFlushedIndices.clear();
int32_t nChannels;
int32_t nSampleRate;
int32_t samplesPerFrame;
- if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
- std::cout << "Failed to get the config params for " << mCompName << " component\n";
+ if (!getConfigParams(mMime, &nChannels, &nSampleRate, &samplesPerFrame)) {
+ std::cout << "Failed to get the config params for " << mComponentName << "\n";
std::cout << "[ WARN ] Test Skipped \n";
return;
}
@@ -587,7 +550,7 @@
char mURL[512];
strcpy(mURL, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL);
+ GetURLForComponent(mURL);
std::ifstream eleStream;
eleStream.open(mURL, std::ifstream::binary);
@@ -600,8 +563,8 @@
int32_t numFrames = 16;
int32_t maxChannelCount = 8;
- if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
- std::cout << "Failed to get the config params for " << mCompName << " component\n";
+ if (!getConfigParams(mMime, &nChannels, &nSampleRate, &samplesPerFrame)) {
+ std::cout << "Failed to get the config params for " << mComponentName << "\n";
std::cout << "[ WARN ] Test Skipped \n";
return;
}
@@ -611,7 +574,7 @@
// Looping through the maximum number of channel count supported by encoder
for (nChannels = 1; nChannels < maxChannelCount; nChannels++) {
- ALOGV("Configuring %u encoder for channel count = %d", mCompName, nChannels);
+ ALOGV("Configuring encoder %s for channel count = %d", mComponentName.c_str(), nChannels);
if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
std::cout << "[ WARN ] Test Skipped \n";
return;
@@ -668,7 +631,9 @@
ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
ASSERT_TRUE(false);
}
- if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
+ if ((mMime.find("flac") != std::string::npos) ||
+ (mMime.find("opus") != std::string::npos) ||
+ (mMime.find("mp4a-latm") != std::string::npos)) {
ASSERT_TRUE(mCsd) << "CSD buffer missing";
}
ASSERT_TRUE(mEos);
@@ -687,7 +652,7 @@
char mURL[512];
strcpy(mURL, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL);
+ GetURLForComponent(mURL);
std::ifstream eleStream;
eleStream.open(mURL, std::ifstream::binary);
@@ -699,8 +664,8 @@
int32_t nChannels;
int32_t numFrames = 16;
- if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
- std::cout << "Failed to get the config params for " << mCompName << " component\n";
+ if (!getConfigParams(mMime, &nChannels, &nSampleRate, &samplesPerFrame)) {
+ std::cout << "Failed to get the config params for " << mComponentName << "\n";
std::cout << "[ WARN ] Test Skipped \n";
return;
}
@@ -711,7 +676,7 @@
uint32_t prevSampleRate = 0u;
for (int32_t nSampleRate : sampleRateValues) {
- ALOGV("Configuring %u encoder for SampleRate = %d", mCompName, nSampleRate);
+ ALOGV("Configuring encoder %s for SampleRate = %d", mComponentName.c_str(), nSampleRate);
if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
std::cout << "[ WARN ] Test Skipped \n";
return;
@@ -772,7 +737,9 @@
ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
ASSERT_TRUE(false);
}
- if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
+ if ((mMime.find("flac") != std::string::npos) ||
+ (mMime.find("opus") != std::string::npos) ||
+ (mMime.find("mp4a-latm") != std::string::npos)) {
ASSERT_TRUE(mCsd) << "CSD buffer missing";
}
ASSERT_TRUE(mEos);
@@ -797,6 +764,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_ENCODER);
for (auto params : kTestParameters) {
kEncodeTestParameters.push_back(
@@ -809,15 +777,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "2"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index 0251ec2..de34705 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -22,6 +22,48 @@
#include <android/hardware/media/c2/1.0/IComponentStore.h>
+std::string sResourceDir = "";
+
+std::string sComponentNamePrefix = "";
+
+static constexpr struct option kArgOptions[] = {
+ {"res", required_argument, 0, 'P'},
+ {"prefix", required_argument, 0, 'p'},
+ {"help", required_argument, 0, 'h'},
+ {nullptr, 0, nullptr, 0},
+};
+
+void printUsage(char *me) {
+ std::cerr << "VTS tests to test codec2 components \n";
+ std::cerr << "Usage: " << me << " [options] \n";
+ std::cerr << "\t -P, --res: Mandatory path to a folder that contains test resources \n";
+ std::cerr << "\t -p, --prefix: Optional prefix to select component/s to be tested \n";
+ std::cerr << "\t All codecs are tested by default \n";
+ std::cerr << "\t Eg: c2.android - test codecs starting with c2.android \n";
+ std::cerr << "\t Eg: c2.android.aac.decoder - test a specific codec \n";
+ std::cerr << "\t -h, --help: Print usage \n";
+}
+
+void parseArgs(int argc, char** argv) {
+ int arg;
+ int option_index;
+ while ((arg = getopt_long(argc, argv, ":P:p:h", kArgOptions, &option_index)) != -1) {
+ switch (arg) {
+ case 'P':
+ sResourceDir = optarg;
+ break;
+ case 'p':
+ sComponentNamePrefix = optarg;
+ break;
+ case 'h':
+ printUsage(argv[0]);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
@@ -157,11 +199,18 @@
(traits.domain != domain || traits.kind != kind)) {
continue;
}
-
+ if (traits.name.rfind(sComponentNamePrefix, 0) != 0) {
+ ALOGD("Skipping tests for %s. Prefix specified is %s", traits.name.c_str(),
+ sComponentNamePrefix.c_str());
+ continue;
+ }
parameters.push_back(std::make_tuple(instance, traits.name));
}
}
+ if (parameters.empty()) {
+ ALOGE("No test parameters added. Verify component prefix passed to the test");
+ }
return parameters;
}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 50e3ac5..a2f1561 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -42,6 +42,12 @@
static std::vector<std::tuple<std::string, std::string>> kTestParameters;
+// Resource directory
+extern std::string sResourceDir;
+
+// Component name prefix
+extern std::string sComponentNamePrefix;
+
struct FrameInfo {
int bytesCount;
uint32_t flags;
@@ -105,6 +111,8 @@
std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
};
+void parseArgs(int argc, char** argv);
+
// Return all test parameters, a list of tuple of <instance, component>.
const std::vector<std::tuple<std::string, std::string>>& getTestParameters();
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
index 6122225..0648dd9 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
@@ -360,6 +360,7 @@
// TODO: Add test for Invalid work,
// TODO: Add test for Invalid states
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters();
for (auto params : kTestParameters) {
kInputTestParameters.push_back(
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index b520c17..f29da0e 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -45,8 +45,51 @@
static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
+struct CompToURL {
+ std::string mime;
+ std::string mURL;
+ std::string info;
+ std::string chksum;
+};
+std::vector<CompToURL> kCompToURL = {
+ {"avc",
+ "bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_176x144_300kbps_60fps.info",
+ "bbb_avc_176x144_300kbps_60fps_chksum.md5"},
+ {"avc",
+ "bbb_avc_640x360_768kbps_30fps.h264", "bbb_avc_640x360_768kbps_30fps.info",
+ "bbb_avc_640x360_768kbps_30fps_chksum.md5"},
+ {"hevc",
+ "bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_176x144_176kbps_60fps.info",
+ "bbb_hevc_176x144_176kbps_60fps_chksum.md5"},
+ {"hevc",
+ "bbb_hevc_640x360_1600kbps_30fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.info",
+ "bbb_hevc_640x360_1600kbps_30fps_chksum.md5"},
+ {"mpeg2",
+ "bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_176x144_105kbps_25fps.info", ""},
+ {"mpeg2",
+ "bbb_mpeg2_352x288_1mbps_60fps.m2v","bbb_mpeg2_352x288_1mbps_60fps.info", ""},
+ {"3gpp",
+ "bbb_h263_352x288_300kbps_12fps.h263", "bbb_h263_352x288_300kbps_12fps.info", ""},
+ {"mp4v-es",
+ "bbb_mpeg4_352x288_512kbps_30fps.m4v", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
+ {"vp8",
+ "bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_176x144_240kbps_60fps.info", ""},
+ {"vp8",
+ "bbb_vp8_640x360_2mbps_30fps.vp8", "bbb_vp8_640x360_2mbps_30fps.info",
+ "bbb_vp8_640x360_2mbps_30fps_chksm.md5"},
+ {"vp9",
+ "bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_176x144_285kbps_60fps.info", ""},
+ {"vp9",
+ "bbb_vp9_640x360_1600kbps_30fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.info",
+ "bbb_vp9_640x360_1600kbps_30fps_chksm.md5"},
+ {"vp9",
+ "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9",
+ "bbb_vp9_704x480_280kbps_24fps_altref_2.info", ""},
+ {"av01",
+ "bbb_av1_640_360.av1", "bbb_av1_640_360.info", "bbb_av1_640_360_chksum.md5"},
+ {"av01",
+ "bbb_av1_176_144.av1", "bbb_av1_176_144.info", "bbb_av1_176_144_chksm.md5"},
+};
class LinearBuffer : public C2Buffer {
public:
@@ -85,26 +128,11 @@
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK, &queried);
+ ASSERT_GT(queried.size(), 0);
- const StringToName kStringToName[] = {
- {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
- };
-
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
-
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::input*)queried[0].get())->m.value;
mEos = false;
mFramesReceived = 0;
mTimestampUs = 0u;
@@ -114,11 +142,11 @@
mMd5Offset = 0;
mMd5Enable = false;
mRefMd5 = nullptr;
- if (mCompName == unknown_comp) mDisableTest = true;
C2SecureModeTuning secureModeTuning{};
mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
- if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+ if (secureModeTuning.value == C2Config::SM_READ_PROTECTED ||
+ secureModeTuning.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED) {
mDisableTest = true;
}
@@ -136,6 +164,9 @@
// Get the test parameters from GetParam call.
virtual void getParams() {}
+ void GetURLChksmForComponent(char* mURL, char* info, char* chksum, size_t streamIndex);
+ void GetURLForComponent(char* mURL, char* info, size_t streamIndex = 0);
+
/* Calculate the CKSUM for the data in inbuf */
void calc_md5_cksum(uint8_t* pu1_inbuf, uint32_t u4_stride, uint32_t u4_width,
uint32_t u4_height, uint8_t* pu1_cksum_p) {
@@ -267,18 +298,7 @@
}
}
- enum standardComp {
- h263,
- avc,
- mpeg2,
- mpeg4,
- hevc,
- vp8,
- vp9,
- av1,
- unknown_comp,
- };
-
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
@@ -291,7 +311,6 @@
char* mRefMd5;
std::list<uint64_t> mTimestampUslist;
std::list<uint64_t> mFlushedIndices;
- standardComp mCompName;
int32_t mWorkResult;
int32_t mReorderDepth;
@@ -324,7 +343,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -351,83 +370,32 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2VideoDecHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
// number of elementary streams per component
#define STREAM_COUNT 3
// LookUpTable of clips, metadata and chksum for component testing
-void GetURLChksmForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
- char* chksum, size_t streamIndex = 1) {
- struct CompToURL {
- Codec2VideoDecHidlTest::standardComp comp;
- const char mURL[STREAM_COUNT][512];
- const char info[STREAM_COUNT][512];
- const char chksum[STREAM_COUNT][512];
- };
- ASSERT_TRUE(streamIndex < STREAM_COUNT);
-
- static const CompToURL kCompToURL[] = {
- {Codec2VideoDecHidlTest::standardComp::avc,
- {"bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_640x360_768kbps_30fps.h264", ""},
- {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info", ""},
- {"bbb_avc_176x144_300kbps_60fps_chksum.md5",
- "bbb_avc_640x360_768kbps_30fps_chksum.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::hevc,
- {"bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.hevc", ""},
- {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info", ""},
- {"bbb_hevc_176x144_176kbps_60fps_chksum.md5",
- "bbb_hevc_640x360_1600kbps_30fps_chksum.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::mpeg2,
- {"bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_352x288_1mbps_60fps.m2v", ""},
- {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::h263,
- {"", "bbb_h263_352x288_300kbps_12fps.h263", ""},
- {"", "bbb_h263_352x288_300kbps_12fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::mpeg4,
- {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v", ""},
- {"", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::vp8,
- {"bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_640x360_2mbps_30fps.vp8", ""},
- {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info", ""},
- {"", "bbb_vp8_640x360_2mbps_30fps_chksm.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::vp9,
- {"bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.vp9",
- "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9"},
- {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info",
- "bbb_vp9_704x480_280kbps_24fps_altref_2.info"},
- {"", "bbb_vp9_640x360_1600kbps_30fps_chksm.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::av1,
- {"bbb_av1_640_360.av1", "bbb_av1_176_144.av1", ""},
- {"bbb_av1_640_360.info", "bbb_av1_176_144.info", ""},
- {"bbb_av1_640_360_chksum.md5", "bbb_av1_176_144_chksm.md5", ""}},
- };
-
- for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
- if (kCompToURL[i].comp == comp) {
- strcat(mURL, kCompToURL[i].mURL[streamIndex]);
- strcat(info, kCompToURL[i].info[streamIndex]);
- strcat(chksum, kCompToURL[i].chksum[streamIndex]);
- return;
+void Codec2VideoDecHidlTestBase::GetURLChksmForComponent(char* mURL, char* info, char* chksum,
+ size_t streamIndex) {
+ int streamCount = 0;
+ for (size_t i = 0; i < kCompToURL.size(); ++i) {
+ if (mMime.find(kCompToURL[i].mime) != std::string::npos) {
+ if (streamCount == streamIndex) {
+ strcat(mURL, kCompToURL[i].mURL.c_str());
+ strcat(info, kCompToURL[i].info.c_str());
+ strcat(chksum, kCompToURL[i].chksum.c_str());
+ return;
+ }
+ streamCount++;
}
}
}
-void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
- size_t streamIndex = 1) {
+void Codec2VideoDecHidlTestBase::GetURLForComponent(char* mURL, char* info, size_t streamIndex) {
char chksum[512];
strcpy(chksum, sResourceDir.c_str());
- GetURLChksmForComponent(comp, mURL, info, chksum, streamIndex);
+ GetURLChksmForComponent(mURL, info, chksum, streamIndex);
}
void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
@@ -517,7 +485,7 @@
TEST_P(Codec2VideoDecHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid video component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -599,7 +567,7 @@
strcpy(info, sResourceDir.c_str());
strcpy(chksum, sResourceDir.c_str());
- GetURLChksmForComponent(mCompName, mURL, info, chksum, streamIndex);
+ GetURLChksmForComponent(mURL, info, chksum, streamIndex);
if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
ALOGV("Skipping Test, Stream not available");
return;
@@ -688,9 +656,11 @@
TEST_P(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
description("Adaptive Decode Test");
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
- if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 || mCompName == vp9 ||
- mCompName == mpeg2))
+ if (!(strcasestr(mMime.c_str(), "avc") || strcasestr(mMime.c_str(), "hevc") ||
+ strcasestr(mMime.c_str(), "vp8") || strcasestr(mMime.c_str(), "vp9") ||
+ strcasestr(mMime.c_str(), "mpeg2"))) {
return;
+ }
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
@@ -705,7 +675,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info, i % STREAM_COUNT);
+ GetURLForComponent(mURL, info, i % STREAM_COUNT);
if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
ALOGV("Stream not available, skipping this index");
continue;
@@ -801,7 +771,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
@@ -888,7 +858,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
mFlushedIndices.clear();
@@ -964,7 +934,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
eleInfo.open(info);
ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
@@ -1038,7 +1008,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
@@ -1137,6 +1107,7 @@
// TODO : Video specific configuration Test
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER);
for (auto params : kTestParameters) {
kDecodeTestParameters.push_back(
@@ -1158,15 +1129,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 5bcea5b..e116fe1 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -46,9 +46,6 @@
static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
kEncodeResolutionTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
-
namespace {
class Codec2VideoEncHidlTestBase : public ::testing::Test {
@@ -78,26 +75,13 @@
mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator, mBlockPoolId++);
ASSERT_NE(mGraphicPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::output::PARAM_TYPE}, C2_DONT_BLOCK,
+ &queried);
+ ASSERT_GT(queried.size(), 0);
- const StringToName kStringToName[] = {
- {"h263", h263}, {"avc", avc}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9},
- };
-
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
-
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::output*)queried[0].get())->m.value;
+ std::cout << "mime : " << mMime << "\n";
mEos = false;
mCsd = false;
mConfigBPictures = false;
@@ -106,11 +90,11 @@
mTimestampUs = 0u;
mOutputSize = 0u;
mTimestampDevTest = false;
- if (mCompName == unknown_comp) mDisableTest = true;
C2SecureModeTuning secureModeTuning{};
mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
- if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+ if (secureModeTuning.value == C2Config::SM_READ_PROTECTED ||
+ secureModeTuning.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED) {
mDisableTest = true;
}
@@ -187,16 +171,7 @@
}
}
- enum standardComp {
- h263,
- avc,
- mpeg4,
- hevc,
- vp8,
- vp9,
- unknown_comp,
- };
-
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
bool mEos;
@@ -204,7 +179,6 @@
bool mDisableTest;
bool mConfigBPictures;
bool mTimestampDevTest;
- standardComp mCompName;
uint32_t mFramesReceived;
uint32_t mFailedWorkReceived;
uint64_t mTimestampUs;
@@ -241,7 +215,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2VideoEncHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -268,13 +242,6 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2VideoEncHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
@@ -405,7 +372,7 @@
TEST_P(Codec2VideoEncHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid video component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -517,9 +484,10 @@
ASSERT_TRUE(false);
}
- if (mCompName == vp8 || mCompName == h263) {
+ if ((mMime.find("vp8") != std::string::npos) ||
+ (mMime.find("3gpp") != std::string::npos)) {
ASSERT_FALSE(mCsd) << "CSD Buffer not expected";
- } else if (mCompName != vp9) {
+ } else if (mMime.find("vp9") == std::string::npos) {
ASSERT_TRUE(mCsd) << "CSD Buffer not received";
}
@@ -841,6 +809,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER);
for (auto params : kTestParameters) {
constexpr char const* kBoolString[] = { "false", "true" };
@@ -866,15 +835,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "1400", "442"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/plugin/FilterWrapper.cpp b/media/codec2/hidl/plugin/FilterWrapper.cpp
index 0b38bc1..bed8aeb 100644
--- a/media/codec2/hidl/plugin/FilterWrapper.cpp
+++ b/media/codec2/hidl/plugin/FilterWrapper.cpp
@@ -19,7 +19,6 @@
#include <android-base/logging.h>
#include <set>
-#include <sstream>
#include <dlfcn.h>
@@ -383,6 +382,9 @@
// Configure the next interface with the params.
std::vector<C2Param *> configParams;
for (size_t i = 0; i < heapParams.size(); ++i) {
+ if (!heapParams[i]) {
+ continue;
+ }
if (heapParams[i]->forStream()) {
heapParams[i] = C2Param::CopyAsStream(
*heapParams[i], false /* output */, heapParams[i]->stream());
@@ -782,10 +784,7 @@
if (C2_OK != mStore->createComponent(filter.traits.name, &comp)) {
return {};
}
- if (C2_OK != mStore->createInterface(filter.traits.name, &intf)) {
- return {};
- }
- filters.push_back({comp, intf, filter.traits, filter.desc});
+ filters.push_back({comp, comp->intf(), filter.traits, filter.desc});
}
return filters;
}
@@ -869,7 +868,7 @@
}
std::vector<Component> filters = createFilters();
std::shared_ptr wrapped = std::make_shared<WrappedDecoder>(
- comp, std::move(filters), weak_from_this());
+ comp, std::vector(filters), weak_from_this());
{
std::unique_lock lock(mWrappedComponentsMutex);
std::vector<std::weak_ptr<const C2Component>> &components =
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 02f7cb8..63ae5cd 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -997,7 +997,15 @@
// needed for decoders.
if (!(config->mDomain & Config::IS_ENCODER)) {
if (surface == nullptr) {
- format = flexPixelFormat.value_or(COLOR_FormatYUV420Flexible);
+ const char *prefix = "";
+ if (flexSemiPlanarPixelFormat) {
+ format = COLOR_FormatYUV420SemiPlanar;
+ prefix = "semi-";
+ } else {
+ format = COLOR_FormatYUV420Planar;
+ }
+ ALOGD("Client requested ByteBuffer mode decoder w/o color format set: "
+ "using default %splanar color format", prefix);
} else {
format = COLOR_FormatSurface;
}
@@ -1935,6 +1943,12 @@
params->removeEntryAt(params->findEntryByName(KEY_BIT_RATE));
}
+ int32_t syncId = 0;
+ if (params->findInt32("audio-hw-sync", &syncId)
+ || params->findInt32("hw-av-sync-id", &syncId)) {
+ configureTunneledVideoPlayback(comp, nullptr, params);
+ }
+
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
const std::unique_ptr<Config> &config = *configLocked;
@@ -2258,6 +2272,10 @@
return UNKNOWN_ERROR;
}
+ if (sidebandHandle == nullptr) {
+ return OK;
+ }
+
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE}, C2_DONT_BLOCK, ¶ms);
if (c2err == C2_OK && params.size() == 1u) {
@@ -2311,7 +2329,13 @@
return;
}
- ALOGW("previous call to %s exceeded timeout", name.c_str());
+ C2String compName;
+ {
+ Mutexed<State>::Locked state(mState);
+ compName = state->comp->getName();
+ }
+ ALOGW("[%s] previous call to %s exceeded timeout", compName.c_str(), name.c_str());
+
initiateRelease(false);
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
}
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index ad28545..7969a6f 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -1174,11 +1174,14 @@
bool changed = false;
if (domain & mInputDomain) {
- sp<AMessage> oldFormat = mInputFormat->dup();
+ sp<AMessage> oldFormat = mInputFormat;
+ mInputFormat = mInputFormat->dup(); // trigger format changed
mInputFormat->extend(getFormatForDomain(reflected, mInputDomain));
if (mInputFormat->countEntries() != oldFormat->countEntries()
|| mInputFormat->changesFrom(oldFormat)->countEntries() > 0) {
changed = true;
+ } else {
+ mInputFormat = oldFormat; // no change
}
}
if (domain & mOutputDomain) {
diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp
index 74e7ef1..2f4d6b1 100644
--- a/media/codec2/sfplugin/utils/Android.bp
+++ b/media/codec2/sfplugin/utils/Android.bp
@@ -33,11 +33,13 @@
"libcodec2_vndk",
"libcutils",
"liblog",
+ "libnativewindow",
"libstagefright_foundation",
"libutils",
],
static_libs: [
+ "libarect",
"libyuv_static",
],
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index bf2a07e..a78d811 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -23,6 +23,7 @@
#include <list>
#include <mutex>
+#include <android/hardware_buffer.h>
#include <media/hardware/HardwareAPI.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -121,32 +122,69 @@
if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
return BAD_VALUE;
}
- if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) {
- // Take shortcuts to use libyuv functions between NV12 and I420 conversion.
- const uint8_t* src_y = view.data()[0];
- const uint8_t* src_u = view.data()[1];
- const uint8_t* src_v = view.data()[2];
- int32_t src_stride_y = view.layout().planes[0].rowInc;
- int32_t src_stride_u = view.layout().planes[1].rowInc;
- int32_t src_stride_v = view.layout().planes[2].rowInc;
- uint8_t* dst_y = imgBase + img->mPlane[0].mOffset;
- uint8_t* dst_u = imgBase + img->mPlane[1].mOffset;
- uint8_t* dst_v = imgBase + img->mPlane[2].mOffset;
- int32_t dst_stride_y = img->mPlane[0].mRowInc;
- int32_t dst_stride_u = img->mPlane[1].mRowInc;
- int32_t dst_stride_v = img->mPlane[2].mRowInc;
- if (IsNV12(view) && IsI420(img)) {
+ const uint8_t* src_y = view.data()[0];
+ const uint8_t* src_u = view.data()[1];
+ const uint8_t* src_v = view.data()[2];
+ int32_t src_stride_y = view.layout().planes[0].rowInc;
+ int32_t src_stride_u = view.layout().planes[1].rowInc;
+ int32_t src_stride_v = view.layout().planes[2].rowInc;
+ uint8_t* dst_y = imgBase + img->mPlane[0].mOffset;
+ uint8_t* dst_u = imgBase + img->mPlane[1].mOffset;
+ uint8_t* dst_v = imgBase + img->mPlane[2].mOffset;
+ int32_t dst_stride_y = img->mPlane[0].mRowInc;
+ int32_t dst_stride_u = img->mPlane[1].mRowInc;
+ int32_t dst_stride_v = img->mPlane[2].mRowInc;
+ int width = view.crop().width;
+ int height = view.crop().height;
+
+ if (IsNV12(view)) {
+ if (IsNV12(img)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
+ return OK;
+ } else if (IsNV21(img)) {
+ if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_u, src_stride_u,
+ dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ } else if (IsI420(img)) {
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
- dst_u, dst_stride_u, dst_v, dst_stride_v, view.crop().width,
- view.crop().height)) {
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
}
- } else {
+ }
+ } else if (IsNV21(view)) {
+ if (IsNV12(img)) {
+ if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_v, src_stride_v,
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
+ return OK;
+ }
+ } else if (IsNV21(img)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height / 2);
+ return OK;
+ } else if (IsI420(img)) {
+ if (!libyuv::NV21ToI420(src_y, src_stride_y, src_v, src_stride_v, dst_y, dst_stride_y,
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ }
+ } else if (IsI420(view)) {
+ if (IsNV12(img)) {
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
- dst_y, dst_stride_y, dst_u, dst_stride_u, view.crop().width,
- view.crop().height)) {
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
+ } else if (IsNV21(img)) {
+ if (!libyuv::I420ToNV21(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+ dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ } else if (IsI420(img)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
+ return OK;
}
}
return _ImageCopy<true>(view, img, imgBase);
@@ -156,32 +194,68 @@
if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
return BAD_VALUE;
}
- if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) {
- // Take shortcuts to use libyuv functions between NV12 and I420 conversion.
- const uint8_t* src_y = imgBase + img->mPlane[0].mOffset;
- const uint8_t* src_u = imgBase + img->mPlane[1].mOffset;
- const uint8_t* src_v = imgBase + img->mPlane[2].mOffset;
- int32_t src_stride_y = img->mPlane[0].mRowInc;
- int32_t src_stride_u = img->mPlane[1].mRowInc;
- int32_t src_stride_v = img->mPlane[2].mRowInc;
- uint8_t* dst_y = view.data()[0];
- uint8_t* dst_u = view.data()[1];
- uint8_t* dst_v = view.data()[2];
- int32_t dst_stride_y = view.layout().planes[0].rowInc;
- int32_t dst_stride_u = view.layout().planes[1].rowInc;
- int32_t dst_stride_v = view.layout().planes[2].rowInc;
- if (IsNV12(img) && IsI420(view)) {
+ const uint8_t* src_y = imgBase + img->mPlane[0].mOffset;
+ const uint8_t* src_u = imgBase + img->mPlane[1].mOffset;
+ const uint8_t* src_v = imgBase + img->mPlane[2].mOffset;
+ int32_t src_stride_y = img->mPlane[0].mRowInc;
+ int32_t src_stride_u = img->mPlane[1].mRowInc;
+ int32_t src_stride_v = img->mPlane[2].mRowInc;
+ uint8_t* dst_y = view.data()[0];
+ uint8_t* dst_u = view.data()[1];
+ uint8_t* dst_v = view.data()[2];
+ int32_t dst_stride_y = view.layout().planes[0].rowInc;
+ int32_t dst_stride_u = view.layout().planes[1].rowInc;
+ int32_t dst_stride_v = view.layout().planes[2].rowInc;
+ int width = view.crop().width;
+ int height = view.crop().height;
+ if (IsNV12(img)) {
+ if (IsNV12(view)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
+ return OK;
+ } else if (IsNV21(view)) {
+ if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_u, src_stride_u,
+ dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ } else if (IsI420(view)) {
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
- dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(),
- view.height())) {
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
}
- } else {
+ }
+ } else if (IsNV21(img)) {
+ if (IsNV12(view)) {
+ if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_v, src_stride_v,
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
+ return OK;
+ }
+ } else if (IsNV21(view)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height / 2);
+ return OK;
+ } else if (IsI420(view)) {
+ if (!libyuv::NV21ToI420(src_y, src_stride_y, src_v, src_stride_v, dst_y, dst_stride_y,
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ }
+ } else if (IsI420(img)) {
+ if (IsNV12(view)) {
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
- dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(),
- view.height())) {
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
+ } else if (IsNV21(view)) {
+ if (!libyuv::I420ToNV21(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+ dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
+ return OK;
+ }
+ } else if (IsI420(view)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
+ return OK;
}
}
return _ImageCopy<false>(view, img, imgBase);
@@ -225,6 +299,20 @@
&& layout.planes[layout.PLANE_V].offset == 1);
}
+bool IsNV21(const C2GraphicView &view) {
+ if (!IsYUV420(view)) {
+ return false;
+ }
+ const C2PlanarLayout &layout = view.layout();
+ return (layout.rootPlanes == 2
+ && layout.planes[layout.PLANE_U].colInc == 2
+ && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_V
+ && layout.planes[layout.PLANE_U].offset == 1
+ && layout.planes[layout.PLANE_V].colInc == 2
+ && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_V
+ && layout.planes[layout.PLANE_V].offset == 0);
+}
+
bool IsI420(const C2GraphicView &view) {
if (!IsYUV420(view)) {
return false;
@@ -261,6 +349,15 @@
&& (img->mPlane[2].mOffset - img->mPlane[1].mOffset == 1));
}
+bool IsNV21(const MediaImage2 *img) {
+ if (!IsYUV420(img)) {
+ return false;
+ }
+ return (img->mPlane[1].mColInc == 2
+ && img->mPlane[2].mColInc == 2
+ && (img->mPlane[1].mOffset - img->mPlane[2].mOffset == 1));
+}
+
bool IsI420(const MediaImage2 *img) {
if (!IsYUV420(img)) {
return false;
@@ -270,6 +367,76 @@
&& img->mPlane[2].mOffset > img->mPlane[1].mOffset);
}
+FlexLayout GetYuv420FlexibleLayout() {
+ static FlexLayout sLayout = []{
+ AHardwareBuffer_Desc desc = {
+ 16, // width
+ 16, // height
+ 1, // layers
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ 0, // stride
+ 0, // rfu0
+ 0, // rfu1
+ };
+ AHardwareBuffer *buffer = nullptr;
+ int ret = AHardwareBuffer_allocate(&desc, &buffer);
+ if (ret != 0) {
+ return FLEX_LAYOUT_UNKNOWN;
+ }
+ class AutoCloser {
+ public:
+ AutoCloser(AHardwareBuffer *buffer) : mBuffer(buffer), mLocked(false) {}
+ ~AutoCloser() {
+ if (mLocked) {
+ AHardwareBuffer_unlock(mBuffer, nullptr);
+ }
+ AHardwareBuffer_release(mBuffer);
+ }
+
+ void setLocked() { mLocked = true; }
+
+ private:
+ AHardwareBuffer *mBuffer;
+ bool mLocked;
+ } autoCloser(buffer);
+ AHardwareBuffer_Planes planes;
+ ret = AHardwareBuffer_lockPlanes(
+ buffer,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ -1, // fence
+ nullptr, // rect
+ &planes);
+ if (ret != 0) {
+ AHardwareBuffer_release(buffer);
+ return FLEX_LAYOUT_UNKNOWN;
+ }
+ autoCloser.setLocked();
+ if (planes.planeCount != 3) {
+ return FLEX_LAYOUT_UNKNOWN;
+ }
+ if (planes.planes[0].pixelStride != 1) {
+ return FLEX_LAYOUT_UNKNOWN;
+ }
+ if (planes.planes[1].pixelStride == 1 && planes.planes[2].pixelStride == 1) {
+ return FLEX_LAYOUT_PLANAR;
+ }
+ if (planes.planes[1].pixelStride == 2 && planes.planes[2].pixelStride == 2) {
+ ssize_t uvDist =
+ static_cast<uint8_t *>(planes.planes[2].data) -
+ static_cast<uint8_t *>(planes.planes[1].data);
+ if (uvDist == 1) {
+ return FLEX_LAYOUT_SEMIPLANAR_UV;
+ } else if (uvDist == -1) {
+ return FLEX_LAYOUT_SEMIPLANAR_VU;
+ }
+ return FLEX_LAYOUT_UNKNOWN;
+ }
+ return FLEX_LAYOUT_UNKNOWN;
+ }();
+ return sLayout;
+}
+
MediaImage2 CreateYUV420PlanarMediaImage2(
uint32_t width, uint32_t height, uint32_t stride, uint32_t vstride) {
return MediaImage2 {
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.h b/media/codec2/sfplugin/utils/Codec2BufferUtils.h
index afadf00..af29e81 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.h
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.h
@@ -96,6 +96,11 @@
bool IsNV12(const C2GraphicView &view);
/**
+ * Returns true iff a view has a NV21 layout.
+ */
+bool IsNV21(const C2GraphicView &view);
+
+/**
* Returns true iff a view has a I420 layout.
*/
bool IsI420(const C2GraphicView &view);
@@ -111,10 +116,26 @@
bool IsNV12(const MediaImage2 *img);
/**
+ * Returns true iff a MediaImage2 has a NV21 layout.
+ */
+bool IsNV21(const MediaImage2 *img);
+
+/**
* Returns true iff a MediaImage2 has a I420 layout.
*/
bool IsI420(const MediaImage2 *img);
+enum FlexLayout {
+ FLEX_LAYOUT_UNKNOWN,
+ FLEX_LAYOUT_PLANAR,
+ FLEX_LAYOUT_SEMIPLANAR_UV,
+ FLEX_LAYOUT_SEMIPLANAR_VU,
+};
+/**
+ * Returns layout of YCBCR_420_888 pixel format.
+ */
+FlexLayout GetYuv420FlexibleLayout();
+
/**
* A raw memory block to use for internal buffers.
*
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index bee6b7f..4ffa3f1 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -42,7 +42,9 @@
* Usage mask that is passed through from gralloc to Codec 2.0 usage.
*/
PASSTHROUGH_USAGE_MASK =
- ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+ ~static_cast<uint64_t>(GRALLOC_USAGE_SW_READ_MASK |
+ GRALLOC_USAGE_SW_WRITE_MASK |
+ GRALLOC_USAGE_PROTECTED)
};
// verify that passthrough mask is within the platform mask
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index 12f4027..a8528df 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -417,7 +417,7 @@
buffer = -1;
}
}
- return new Impl(ionFd, size, bufferFd, buffer, id, ret);
+ return new Impl(ionFd, allocSize, bufferFd, buffer, id, ret);
} else {
ret = ion_alloc_fd(ionFd, allocSize, align, heapMask, flags, &bufferFd);
@@ -425,7 +425,7 @@
"returned (%d) ; bufferFd = %d",
ionFd, allocSize, align, heapMask, flags, ret, bufferFd);
- return new ImplV2(ionFd, size, bufferFd, id, ret);
+ return new ImplV2(ionFd, allocSize, bufferFd, id, ret);
}
}
diff --git a/media/codec2/vndk/C2DmaBufAllocator.cpp b/media/codec2/vndk/C2DmaBufAllocator.cpp
index 7c8999b..6d8552a 100644
--- a/media/codec2/vndk/C2DmaBufAllocator.cpp
+++ b/media/codec2/vndk/C2DmaBufAllocator.cpp
@@ -252,20 +252,9 @@
int bufferFd = -1;
int ret = 0;
- // NOTE: read this property directly from the property as this code has to run on
- // Android Q, but the sysprop was only introduced in Android S.
- static size_t sPadding =
- base::GetUintProperty("media.c2.dmabuf.padding", (uint32_t)0, MAX_PADDING);
- if (sPadding > SIZE_MAX - size) {
- // size would overflow
- ALOGD("dmabuf_alloc: size #%zx cannot accommodate padding #%zx", size, sPadding);
- ret = -ENOMEM;
- } else {
- size_t allocSize = size + sPadding;
- bufferFd = alloc.Alloc(heap_name, allocSize, flags);
- if (bufferFd < 0) {
- ret = bufferFd;
- }
+ bufferFd = alloc.Alloc(heap_name, size, flags);
+ if (bufferFd < 0) {
+ ret = bufferFd;
}
// this may be a non-working handle if bufferFd is negative
@@ -377,8 +366,22 @@
return ret;
}
+ // TODO: should we pad before mapping usage?
+
+ // NOTE: read this property directly from the property as this code has to run on
+ // Android Q, but the sysprop was only introduced in Android S.
+ static size_t sPadding =
+ base::GetUintProperty("media.c2.dmabuf.padding", (uint32_t)0, MAX_PADDING);
+ if (sPadding > SIZE_MAX - capacity) {
+ // size would overflow
+ ALOGD("dmabuf_alloc: size #%x cannot accommodate padding #%zx", capacity, sPadding);
+ return C2_NO_MEMORY;
+ }
+
+ size_t allocSize = (size_t)capacity + sPadding;
+ // TODO: should we align allocation size to mBlockSize to reflect the true allocation size?
std::shared_ptr<C2DmaBufAllocation> alloc = std::make_shared<C2DmaBufAllocation>(
- mBufferAllocator, capacity, heap_name, flags, getId());
+ mBufferAllocator, allocSize, heap_name, flags, getId());
ret = alloc->status();
if (ret == C2_OK) {
*allocation = alloc;
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 8b53b24..2944925 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -777,6 +777,7 @@
}
if (oldGeneration != mGeneration) {
ALOGV("cannot migrate stale buffer");
+ return -1;
}
if (mTransfer) {
// either transferred or detached.
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 314a822..b1d72e8 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -2345,7 +2345,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4);
if (mPath.size() >= 2
@@ -2427,7 +2427,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_CSD_AVC, buffer.get(), chunk_data_size);
break;
@@ -2449,7 +2449,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_CSD_HEVC, buffer.get(), chunk_data_size);
*offset += chunk_size;
@@ -4021,13 +4021,13 @@
// custom genre string
buffer[size] = '\0';
- AMediaFormat_setString(mFileMetaData,
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
} else {
buffer[size] = '\0';
- AMediaFormat_setString(mFileMetaData,
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
}
@@ -4568,6 +4568,9 @@
if (objectTypeIndication == 0x6B || objectTypeIndication == 0x69) {
// mp3 audio
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
AMediaFormat_setString(mLastTrack->meta,AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG);
return OK;
}
@@ -4658,6 +4661,10 @@
if (offset >= csd_size || csd[offset] != 0x01) {
return ERROR_MALFORMED;
}
+
+ if (mLastTrack == NULL) {
+ return ERROR_MALFORMED;
+ }
// formerly kKeyVorbisInfo
AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_CSD_0, &csd[offset], len1);
@@ -6187,9 +6194,13 @@
if (newBuffer) {
if (mIsPcm) {
// The twos' PCM block reader assumes that all samples has the same size.
-
- uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk()
- - mCurrentSampleIndex + 1;
+ uint32_t lastSampleIndexInChunk = mSampleTable->getLastSampleIndexInChunk();
+ if (lastSampleIndexInChunk < mCurrentSampleIndex) {
+ mBuffer->release();
+ mBuffer = nullptr;
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ uint32_t samplesToRead = lastSampleIndexInChunk - mCurrentSampleIndex + 1;
if (samplesToRead > kMaxPcmFrameSize) {
samplesToRead = kMaxPcmFrameSize;
}
@@ -6198,13 +6209,17 @@
samplesToRead, size, mCurrentSampleIndex,
mSampleTable->getLastSampleIndexInChunk());
- size_t totalSize = samplesToRead * size;
+ size_t totalSize = samplesToRead * size;
+ if (mBuffer->size() < totalSize) {
+ mBuffer->release();
+ mBuffer = nullptr;
+ return AMEDIA_ERROR_UNKNOWN;
+ }
uint8_t* buf = (uint8_t *)mBuffer->data();
ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize);
if (bytesRead < (ssize_t)totalSize) {
mBuffer->release();
mBuffer = NULL;
-
return AMEDIA_ERROR_IO;
}
@@ -6258,7 +6273,19 @@
if (isSyncSample) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
-
+
+ AMediaFormat_setInt64(
+ meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/,
+ offset);
+
+ if (mSampleTable != nullptr &&
+ mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
+ AMediaFormat_setInt64(
+ meta,
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ mSampleTable->getLastSampleIndexInChunk());
+ }
+
++mCurrentSampleIndex;
}
}
@@ -6408,6 +6435,17 @@
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
+ AMediaFormat_setInt64(
+ meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/, offset);
+
+ if (mSampleTable != nullptr &&
+ mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
+ AMediaFormat_setInt64(
+ meta,
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ mSampleTable->getLastSampleIndexInChunk());
+ }
+
++mCurrentSampleIndex;
*out = mBuffer;
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 22cf254..3333925 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -74,8 +74,9 @@
* The nominal range of the data is [-1.0f, 1.0f).
* Values outside that range may be clipped.
*
- * See also 'floatData' at
- * https://developer.android.com/reference/android/media/AudioTrack#write(float[],%20int,%20int,%20int)
+ * See also the float Data in
+ * <a href="/reference/android/media/AudioTrack#write(float[],%20int,%20int,%20int)">
+ * write(float[], int, int, int)</a>.
*/
AAUDIO_FORMAT_PCM_FLOAT,
@@ -196,21 +197,69 @@
};
typedef int32_t aaudio_result_t;
+/**
+ * AAudio Stream states, for details, refer to
+ * <a href="/ndk/guides/audio/aaudio/aaudio#using-streams">Using an Audio Stream</a>
+ */
enum
{
+
+ /**
+ * The stream is created but not initialized yet.
+ */
AAUDIO_STREAM_STATE_UNINITIALIZED = 0,
+ /**
+ * The stream is in an unrecognized state.
+ */
AAUDIO_STREAM_STATE_UNKNOWN,
+
+ /**
+ * The stream is open and ready to use.
+ */
AAUDIO_STREAM_STATE_OPEN,
+ /**
+ * The stream is just starting up.
+ */
AAUDIO_STREAM_STATE_STARTING,
+ /**
+ * The stream has started.
+ */
AAUDIO_STREAM_STATE_STARTED,
+ /**
+ * The stream is pausing.
+ */
AAUDIO_STREAM_STATE_PAUSING,
+ /**
+ * The stream has paused, could be restarted or flushed.
+ */
AAUDIO_STREAM_STATE_PAUSED,
+ /**
+ * The stream is being flushed.
+ */
AAUDIO_STREAM_STATE_FLUSHING,
+ /**
+ * The stream is flushed, ready to be restarted.
+ */
AAUDIO_STREAM_STATE_FLUSHED,
+ /**
+ * The stream is stopping.
+ */
AAUDIO_STREAM_STATE_STOPPING,
+ /**
+ * The stream has been stopped.
+ */
AAUDIO_STREAM_STATE_STOPPED,
+ /**
+ * The stream is closing.
+ */
AAUDIO_STREAM_STATE_CLOSING,
+ /**
+ * The stream has been closed.
+ */
AAUDIO_STREAM_STATE_CLOSED,
+ /**
+ * The stream is disconnected from audio device.
+ */
AAUDIO_STREAM_STATE_DISCONNECTED
};
typedef int32_t aaudio_stream_state_t;
@@ -260,7 +309,8 @@
* This information is used by certain platforms or routing policies
* to make more refined volume or routing decisions.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 28.
@@ -361,7 +411,8 @@
* an audio book application) this information might be used by the audio framework to
* enforce audio focus.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 28.
@@ -441,7 +492,8 @@
/**
* Specifying if audio may or may not be captured by other apps or the system.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 29.
@@ -453,10 +505,11 @@
* For privacy, the following usages can not be recorded: AAUDIO_VOICE_COMMUNICATION*,
* AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and {@link #AAUDIO_USAGE_ASSISTANT}.
*
- * On {@link android.os.Build.VERSION_CODES#Q}, this means only {@link #AAUDIO_USAGE_MEDIA}
- * and {@link #AAUDIO_USAGE_GAME} may be captured.
+ * On <a href="/reference/android/os/Build.VERSION_CODES#Q">Build.VERSION_CODES</a>,
+ * this means only {@link #AAUDIO_USAGE_MEDIA} and {@link #AAUDIO_USAGE_GAME} may be captured.
*
- * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
+ * See <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_ALL">
+ * ALLOW_CAPTURE_BY_ALL</a>.
*/
AAUDIO_ALLOW_CAPTURE_BY_ALL = 1,
/**
@@ -464,8 +517,9 @@
*
* System apps can capture for many purposes like accessibility, user guidance...
* but have strong restriction. See
- * {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM} for what the system apps
- * can do with the capture audio.
+ * <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_SYSTEM">
+ * ALLOW_CAPTURE_BY_SYSTEM</a>
+ * for what the system apps can do with the capture audio.
*/
AAUDIO_ALLOW_CAPTURE_BY_SYSTEM = 2,
/**
@@ -473,7 +527,8 @@
*
* It is encouraged to use {@link #AAUDIO_ALLOW_CAPTURE_BY_SYSTEM} instead of this value as system apps
* provide significant and useful features for the user (eg. accessibility).
- * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
+ * See <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_NONE">
+ * ALLOW_CAPTURE_BY_NONE</a>.
*/
AAUDIO_ALLOW_CAPTURE_BY_NONE = 3,
};
@@ -803,7 +858,9 @@
* The default is {@link #AAUDIO_ALLOW_CAPTURE_BY_ALL}.
*
* Note that an application can also set its global policy, in which case the most restrictive
- * policy is always applied. See {@link android.media.AudioAttributes#setAllowedCapturePolicy(int)}
+ * policy is always applied. See
+ * <a href="/reference/android/media/AudioManager#setAllowedCapturePolicy(int)">
+ * setAllowedCapturePolicy(int)</a>
*
* Available since API level 29.
*
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 5d311fc..1bbe443 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -268,7 +268,7 @@
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
- result = systemStopFromCallback();
+ result = systemStopInternal();
break;
}
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index b81e5e4..3f17e6b 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -301,7 +301,7 @@
}
} else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
- result = systemStopFromCallback();
+ result = systemStopInternal();
break;
}
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 53523c5..e8f71be 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -143,13 +143,13 @@
}
aaudio_result_t AudioStream::systemStart() {
- std::lock_guard<std::mutex> lock(mStreamLock);
-
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
+
switch (getState()) {
// Is this a good time to start?
case AAUDIO_STREAM_STATE_OPEN:
@@ -187,7 +187,6 @@
}
aaudio_result_t AudioStream::systemPause() {
- std::lock_guard<std::mutex> lock(mStreamLock);
if (!isPauseSupported()) {
return AAUDIO_ERROR_UNIMPLEMENTED;
@@ -198,6 +197,7 @@
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
switch (getState()) {
// Proceed with pausing.
case AAUDIO_STREAM_STATE_STARTING:
@@ -242,12 +242,12 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("stream cannot be flushed from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
aaudio_result_t result = AAudio_isFlushAllowed(getState());
if (result != AAUDIO_OK) {
return result;
@@ -256,7 +256,7 @@
return requestFlush_l();
}
-aaudio_result_t AudioStream::systemStopFromCallback() {
+aaudio_result_t AudioStream::systemStopInternal() {
std::lock_guard<std::mutex> lock(mStreamLock);
aaudio_result_t result = safeStop_l();
if (result == AAUDIO_OK) {
@@ -267,17 +267,12 @@
}
aaudio_result_t AudioStream::systemStopFromApp() {
- std::lock_guard<std::mutex> lock(mStreamLock);
+ // This check can and should be done outside the lock.
if (collidesWithCallback()) {
ALOGE("stream cannot be stopped by calling from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_result_t result = safeStop_l();
- if (result == AAUDIO_OK) {
- // We only call this for logging in "dumpsys audio". So ignore return code.
- (void) mPlayerBase->stopWithStatus();
- }
- return result;
+ return systemStopInternal();
}
aaudio_result_t AudioStream::safeStop_l() {
@@ -316,12 +311,12 @@
}
aaudio_result_t AudioStream::safeRelease() {
- // This may get temporarily unlocked in the MMAP release() when joining callback threads.
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
+ // This may get temporarily unlocked in the MMAP release() when joining callback threads.
+ std::lock_guard<std::mutex> lock(mStreamLock);
if (getState() == AAUDIO_STREAM_STATE_CLOSING) { // already released?
return AAUDIO_OK;
}
@@ -329,17 +324,14 @@
}
aaudio_result_t AudioStream::safeReleaseClose() {
- // This get temporarily unlocked in the MMAP release() when joining callback threads.
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
- releaseCloseFinal_l();
- return AAUDIO_OK;
+ return safeReleaseCloseInternal();
}
-aaudio_result_t AudioStream::safeReleaseCloseFromCallback() {
+aaudio_result_t AudioStream::safeReleaseCloseInternal() {
// This get temporarily unlocked in the MMAP release() when joining callback threads.
std::lock_guard<std::mutex> lock(mStreamLock);
releaseCloseFinal_l();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 333e665..abf62f3 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -408,7 +408,7 @@
/**
* This is called internally when an app callback returns AAUDIO_CALLBACK_RESULT_STOP.
*/
- aaudio_result_t systemStopFromCallback();
+ aaudio_result_t systemStopInternal();
/**
* Safely RELEASE a stream after taking mStreamLock and checking
@@ -424,7 +424,7 @@
*/
aaudio_result_t safeReleaseClose();
- aaudio_result_t safeReleaseCloseFromCallback();
+ aaudio_result_t safeReleaseCloseInternal();
protected:
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index fdaa2ab..60eb73a 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -124,7 +124,7 @@
__func__, callbackResult);
}
audioBuffer->size = 0;
- systemStopFromCallback();
+ systemStopInternal();
// Disable the callback just in case the system keeps trying to call us.
mCallbackEnabled.store(false);
}
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 62c9b46..f9eebd7 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -250,3 +250,16 @@
"libutils",
],
}
+
+
+cc_test {
+ name: "test_disconnect_race",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_disconnect_race.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_disconnect_race.cpp b/media/libaaudio/tests/test_disconnect_race.cpp
new file mode 100644
index 0000000..6dbe165
--- /dev/null
+++ b/media/libaaudio/tests/test_disconnect_race.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+/**
+ * Test whether an error callback is joined before the close finishes.
+ *
+ * Start a stream with a callback.
+ * The callback just sleeps for a long time.
+ * While the callback is sleeping, close() the stream from the main thread.
+ * Then check to make sure the callback was joined before the close() returns.
+ *
+ * This can hang if there are deadlocks. So make sure you get a PASSED result.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+// Sleep long enough that the foreground has a chance to call close.
+static constexpr int kCallbackSleepMillis = 1000;
+static constexpr int kPollSleepMillis = 100;
+
+static int sErrorCount = 0;
+
+#define MY_ASSERT_TRUE(statement) \
+ if (!(statement)) { \
+ printf("ERROR line:%d - " #statement "\n", __LINE__); \
+ sErrorCount++; \
+ return false; \
+ }
+
+#define MY_ASSERT_EQ(aa,bb) MY_ASSERT_TRUE(((aa) == (bb)))
+#define MY_ASSERT_NE(aa,bb) MY_ASSERT_TRUE(((aa) != (bb)))
+
+class AudioEngine {
+public:
+
+ // Check for a crash or late callback if we close without stopping.
+ bool checkCloseJoins(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode,
+ bool callStopFromCallback) {
+ mCallStopFromCallback = callStopFromCallback;
+
+ if (!startStreamForStall(direction, perfMode)) return false;
+
+ printf("--------------------------------------------------------\n");
+ printf("%s() - direction = %d, perfMode = %d, callStop = %d\n",
+ __func__, direction, perfMode, callStopFromCallback);
+
+ // When the callback starts it will go to sleep.
+ if (!waitForCallbackToStart()) return false;
+
+ printf("call AAudioStream_close()\n");
+ MY_ASSERT_TRUE(!mCallbackFinished); // Still sleeping?
+ aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
+ if (mCallbackStarted) {
+ MY_ASSERT_TRUE(mCallbackFinished);
+ }
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+ printf("AAudioStream_close() returned %d\n", result);
+
+ MY_ASSERT_EQ(AAUDIO_ERROR_DISCONNECTED, mError.load());
+ if (mCallStopFromCallback) {
+ // Did calling stop() from callback fail? It should have.
+ MY_ASSERT_NE(AAUDIO_OK, mStopResult.load());
+ }
+
+ return true;
+ }
+
+private:
+ bool startStreamForStall(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode) {
+ AAudioStreamBuilder* builder = nullptr;
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ result = AAudio_createStreamBuilder(&builder);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ // Request stream properties.
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ AAudioStreamBuilder_delete(builder);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ // Check to see what kind of stream we actually got.
+ int32_t deviceId = AAudioStream_getDeviceId(mStream);
+ aaudio_performance_mode_t
+ actualPerfMode = AAudioStream_getPerformanceMode(mStream);
+ printf("-------- opened: deviceId = %3d, perfMode = %d\n",
+ deviceId,
+ actualPerfMode);
+
+ // Start stream.
+ result = AAudioStream_requestStart(mStream);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ return true;
+ }
+
+ bool waitForCallbackToStart() {
+ // Wait for callback to say it has been called.
+ int countDown = 10 * 1000 / kPollSleepMillis;
+ while (!mCallbackStarted && countDown > 0) {
+ if ((countDown % 5) == 0) {
+ printf("===== Please PLUG or UNPLUG headphones! ======= %d\n", countDown);
+ }
+ usleep(kPollSleepMillis * 1000);
+ countDown--;
+ }
+ MY_ASSERT_TRUE(countDown > 0);
+ MY_ASSERT_TRUE(mCallbackStarted);
+ return true;
+ }
+
+// Callback function that fills the audio output buffer.
+ static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream * /* stream */,
+ void * /* userData */,
+ void * /* audioData */,
+ int32_t /* numFrames */
+ ) {
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ }
+
+ static void s_myErrorCallbackProc(
+ AAudioStream * stream,
+ void *userData,
+ aaudio_result_t error) {
+ AudioEngine *engine = (AudioEngine *)userData;
+ engine->mError = error;
+ engine->mCallbackStarted = true;
+ usleep(kCallbackSleepMillis * 1000);
+ // it is illegal to call stop() from the callback. It should
+ // return an error and not hang.
+ if (engine->mCallStopFromCallback) {
+ engine->mStopResult = AAudioStream_requestStop(stream);
+ }
+ engine->mCallbackFinished = true;
+ }
+
+ AAudioStream* mStream = nullptr;
+
+ std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
+ std::atomic<bool> mCallStopFromCallback{false};
+ std::atomic<bool> mCallbackStarted{false}; // written by error callback
+ std::atomic<bool> mCallbackFinished{false}; // written by error callback
+ std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
+};
+
+int main(int, char **) {
+ // Parameters to test.
+ static aaudio_direction_t directions[] = {AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_DIRECTION_INPUT};
+ static aaudio_performance_mode_t perfModes[] =
+ {AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_PERFORMANCE_MODE_NONE};
+ static bool callStops[] = { false, true };
+
+ // 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 Disconnect Race V1.0\n");
+ printf("\n");
+
+ for (auto callStop : callStops) {
+ for (auto direction : directions) {
+ for (auto perfMode : perfModes) {
+ AudioEngine engine;
+ engine.checkCloseJoins(direction, perfMode, callStop);
+ }
+ }
+ }
+
+ printf("Error Count = %d, %s\n", sErrorCount,
+ ((sErrorCount == 0) ? "PASS" : "FAIL"));
+}
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 05ba55f..c77aeeb 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -1918,6 +1918,9 @@
convertRange(aidl.channelMasks.begin(), aidl.channelMasks.end(), legacy.channel_masks,
aidl2legacy_int32_t_audio_channel_mask_t));
legacy.num_channel_masks = aidl.channelMasks.size();
+
+ legacy.encapsulation_type = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(aidl.encapsulationType));
return legacy;
}
@@ -1941,6 +1944,10 @@
convertRange(legacy.channel_masks, legacy.channel_masks + legacy.num_channel_masks,
std::back_inserter(aidl.channelMasks),
legacy2aidl_audio_channel_mask_t_int32_t));
+
+ aidl.encapsulationType = VALUE_OR_RETURN(
+ legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ legacy.encapsulation_type));
return aidl;
}
@@ -1989,6 +1996,15 @@
aidl2legacy_AudioProfile_audio_profile));
legacy.num_audio_profiles = aidl.profiles.size();
+ if (aidl.extraAudioDescriptors.size() > std::size(legacy.extra_audio_descriptors)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(aidl.extraAudioDescriptors.begin(), aidl.extraAudioDescriptors.end(),
+ legacy.extra_audio_descriptors,
+ aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor));
+ legacy.num_extra_audio_descriptors = aidl.extraAudioDescriptors.size();
+
if (aidl.gains.size() > std::size(legacy.gains)) {
return unexpected(BAD_VALUE);
}
@@ -2018,6 +2034,15 @@
std::back_inserter(aidl.profiles),
legacy2aidl_audio_profile_AudioProfile));
+ if (legacy.num_extra_audio_descriptors > std::size(legacy.extra_audio_descriptors)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.extra_audio_descriptors,
+ legacy.extra_audio_descriptors + legacy.num_extra_audio_descriptors,
+ std::back_inserter(aidl.extraAudioDescriptors),
+ legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor));
+
if (legacy.num_gains > std::size(legacy.gains)) {
return unexpected(BAD_VALUE);
}
@@ -2218,4 +2243,84 @@
return aidl;
}
+ConversionResult<audio_standard_t>
+aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl) {
+ switch (aidl) {
+ case media::AudioStandard::NONE:
+ return AUDIO_STANDARD_NONE;
+ case media::AudioStandard::EDID:
+ return AUDIO_STANDARD_EDID;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioStandard>
+legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy) {
+ switch (legacy) {
+ case AUDIO_STANDARD_NONE:
+ return media::AudioStandard::NONE;
+ case AUDIO_STANDARD_EDID:
+ return media::AudioStandard::EDID;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_extra_audio_descriptor>
+aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(
+ const media::ExtraAudioDescriptor& aidl) {
+ audio_extra_audio_descriptor legacy;
+ legacy.standard = VALUE_OR_RETURN(aidl2legacy_AudioStandard_audio_standard_t(aidl.standard));
+ if (aidl.audioDescriptor.size() > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ return unexpected(BAD_VALUE);
+ }
+ legacy.descriptor_length = aidl.audioDescriptor.size();
+ std::copy(aidl.audioDescriptor.begin(), aidl.audioDescriptor.end(),
+ std::begin(legacy.descriptor));
+ legacy.encapsulation_type =
+ VALUE_OR_RETURN(aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ aidl.encapsulationType));
+ return legacy;
+}
+
+ConversionResult<media::ExtraAudioDescriptor>
+legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ const audio_extra_audio_descriptor& legacy) {
+ media::ExtraAudioDescriptor aidl;
+ aidl.standard = VALUE_OR_RETURN(legacy2aidl_audio_standard_t_AudioStandard(legacy.standard));
+ if (legacy.descriptor_length > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ return unexpected(BAD_VALUE);
+ }
+ aidl.audioDescriptor.resize(legacy.descriptor_length);
+ std::copy(legacy.descriptor, legacy.descriptor + legacy.descriptor_length,
+ aidl.audioDescriptor.begin());
+ aidl.encapsulationType =
+ VALUE_OR_RETURN(legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ legacy.encapsulation_type));
+ return aidl;
+}
+
+ConversionResult<audio_encapsulation_type_t>
+aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ const media::AudioEncapsulationType& aidl) {
+ switch (aidl) {
+ case media::AudioEncapsulationType::NONE:
+ return AUDIO_ENCAPSULATION_TYPE_NONE;
+ case media::AudioEncapsulationType::IEC61937:
+ return AUDIO_ENCAPSULATION_TYPE_IEC61937;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioEncapsulationType>
+legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ const audio_encapsulation_type_t & legacy) {
+ switch (legacy) {
+ case AUDIO_ENCAPSULATION_TYPE_NONE:
+ return media::AudioEncapsulationType::NONE;
+ case AUDIO_ENCAPSULATION_TYPE_IEC61937:
+ return media::AudioEncapsulationType::IEC61937;
+ }
+ return unexpected(BAD_VALUE);
+}
+
} // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index d25597d..19d68a0 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -311,6 +311,7 @@
"aidl/android/media/AudioDualMonoMode.aidl",
"aidl/android/media/AudioEncapsulationMode.aidl",
"aidl/android/media/AudioEncapsulationMetadataType.aidl",
+ "aidl/android/media/AudioEncapsulationType.aidl",
"aidl/android/media/AudioFlag.aidl",
"aidl/android/media/AudioGain.aidl",
"aidl/android/media/AudioGainConfig.aidl",
@@ -341,12 +342,15 @@
"aidl/android/media/AudioPortType.aidl",
"aidl/android/media/AudioProfile.aidl",
"aidl/android/media/AudioSourceType.aidl",
+ "aidl/android/media/AudioStandard.aidl",
"aidl/android/media/AudioStreamType.aidl",
"aidl/android/media/AudioTimestampInternal.aidl",
"aidl/android/media/AudioUniqueIdUse.aidl",
"aidl/android/media/AudioUsage.aidl",
"aidl/android/media/AudioUuid.aidl",
+ "aidl/android/media/AudioVibratorInfo.aidl",
"aidl/android/media/EffectDescriptor.aidl",
+ "aidl/android/media/ExtraAudioDescriptor.aidl",
],
imports: [
"audio_common-aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index f476b7d..0bc592d 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -2258,6 +2258,15 @@
return NO_ERROR;
}
+status_t AudioSystem::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->setVibratorInfos(vibratorInfos);
+}
+
// ---------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 4103630..0feafc5 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -733,6 +733,11 @@
return statusTFromBinderStatus(mDelegate->setAudioHalPids(pidsAidl));
}
+status_t AudioFlingerClientAdapter::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ return statusTFromBinderStatus(mDelegate->setVibratorInfos(vibratorInfos));
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// AudioFlingerServerAdapter
@@ -1174,4 +1179,9 @@
return Status::ok();
}
+Status AudioFlingerServerAdapter::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ return Status::fromStatusT(mDelegate->setVibratorInfos(vibratorInfos));
+}
+
} // namespace android
diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..b08a604
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.media;
+
+/**
+ * Audio encapsulation type is used to describe if the audio data should be sent with a particular
+ * encapsulation type or not.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioEncapsulationType {
+ NONE = 0,
+ IEC61937 = 1,
+}
\ No newline at end of file
diff --git a/media/libaudioclient/aidl/android/media/AudioPort.aidl b/media/libaudioclient/aidl/android/media/AudioPort.aidl
index 123aeb0..bf0e5b7 100644
--- a/media/libaudioclient/aidl/android/media/AudioPort.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPort.aidl
@@ -22,6 +22,7 @@
import android.media.AudioPortRole;
import android.media.AudioPortType;
import android.media.AudioProfile;
+import android.media.ExtraAudioDescriptor;
/**
* {@hide}
@@ -36,6 +37,11 @@
@utf8InCpp String name;
/** AudioProfiles supported by this port (format, Rates, Channels). */
AudioProfile[] profiles;
+ /**
+ * ExtraAudioDescriptors supported by this port. The format is not unrecognized to the
+ * platform. The audio capability is described by a hardware descriptor.
+ */
+ ExtraAudioDescriptor[] extraAudioDescriptors;
/** Gain controllers. */
AudioGain[] gains;
/** Current audio port configuration. */
diff --git a/media/libaudioclient/aidl/android/media/AudioProfile.aidl b/media/libaudioclient/aidl/android/media/AudioProfile.aidl
index e5e8812..afb288f 100644
--- a/media/libaudioclient/aidl/android/media/AudioProfile.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioProfile.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.media.AudioEncapsulationType;
import android.media.audio.common.AudioFormat;
/**
@@ -31,4 +32,5 @@
boolean isDynamicFormat;
boolean isDynamicChannels;
boolean isDynamicRate;
+ AudioEncapsulationType encapsulationType;
}
diff --git a/media/libaudioclient/aidl/android/media/AudioStandard.aidl b/media/libaudioclient/aidl/android/media/AudioStandard.aidl
new file mode 100644
index 0000000..e131d0d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioStandard.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+package android.media;
+
+/**
+ * The audio standard that describe audio playback/capture capabilites.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioStandard {
+ NONE = 0,
+ EDID = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
new file mode 100644
index 0000000..f88fc3c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ * A class for vibrator information. The information will be used in HapticGenerator effect.
+ */
+parcelable AudioVibratorInfo {
+ int id;
+ float resonantFrequency;
+ float qFactor;
+}
diff --git a/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl b/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl
new file mode 100644
index 0000000..ec5b67a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.media;
+
+import android.media.AudioEncapsulationType;
+import android.media.AudioStandard;
+
+/**
+ * The audio descriptor that descibes playback/capture capabilities according to
+ * a particular standard.
+ *
+ * {@hide}
+ */
+parcelable ExtraAudioDescriptor {
+ AudioStandard standard;
+ byte[] audioDescriptor;
+ AudioEncapsulationType encapsulationType;
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index e63f391..abbced5 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -23,6 +23,7 @@
import android.media.AudioStreamType;
import android.media.AudioUniqueIdUse;
import android.media.AudioUuid;
+import android.media.AudioVibratorInfo;
import android.media.CreateEffectRequest;
import android.media.CreateEffectResponse;
import android.media.CreateRecordRequest;
@@ -202,4 +203,8 @@
MicrophoneInfoData[] getMicrophones();
void setAudioHalPids(in int[] /* pid_t[] */ pids);
+
+ // Set vibrators' information.
+ // The value will be used to initialize HapticGenerator.
+ void setVibratorInfos(in AudioVibratorInfo[] vibratorInfos);
}
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index fd87dc2..1dd9d60 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -28,6 +28,7 @@
#include <android/media/AudioDualMonoMode.h>
#include <android/media/AudioEncapsulationMode.h>
#include <android/media/AudioEncapsulationMetadataType.h>
+#include <android/media/AudioEncapsulationType.h>
#include <android/media/AudioFlag.h>
#include <android/media/AudioGain.h>
#include <android/media/AudioGainMode.h>
@@ -48,6 +49,7 @@
#include <android/media/AudioTimestampInternal.h>
#include <android/media/AudioUniqueIdUse.h>
#include <android/media/EffectDescriptor.h>
+#include <android/media/ExtraAudioDescriptor.h>
#include <android/media/SharedFileRegion.h>
#include <binder/IMemory.h>
@@ -386,4 +388,25 @@
ConversionResult<media::AudioPlaybackRate>
legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy);
+ConversionResult<audio_standard_t>
+aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl);
+ConversionResult<media::AudioStandard>
+legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy);
+
+ConversionResult<audio_extra_audio_descriptor>
+aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(
+ const media::ExtraAudioDescriptor& aidl);
+ConversionResult<media::ExtraAudioDescriptor>
+legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ const audio_extra_audio_descriptor& legacy);
+
+ConversionResult<audio_encapsulation_type_t>
+aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ const media::AudioEncapsulationType& aidl);
+ConversionResult<media::AudioEncapsulationType>
+legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ const audio_encapsulation_type_t & legacy);
+
+
+
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index c63d29f..4c99dbd 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -19,9 +19,10 @@
#include <sys/types.h>
-#include <android/media/permission/Identity.h>
+#include <android/media/AudioVibratorInfo.h>
#include <android/media/BnAudioFlingerClient.h>
#include <android/media/BnAudioPolicyServiceClient.h>
+#include <android/media/permission/Identity.h>
#include <media/AidlConversionUtil.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioPolicy.h>
@@ -553,6 +554,8 @@
static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
+ static status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
+
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public media::BnAudioFlingerClient
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index efd7fed..7f7ca85 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -35,6 +35,7 @@
#include <string>
#include <vector>
+#include <android/media/AudioVibratorInfo.h>
#include <android/media/BnAudioFlingerService.h>
#include <android/media/BpAudioFlingerService.h>
#include <android/media/permission/Identity.h>
@@ -331,6 +332,11 @@
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
virtual status_t setAudioHalPids(const std::vector<pid_t>& pids) = 0;
+
+ // Set vibrators' information.
+ // The values will be used to initialize HapticGenerator.
+ virtual status_t setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) = 0;
};
/**
@@ -422,6 +428,7 @@
size_t frameCountHAL(audio_io_handle_t ioHandle) const override;
status_t getMicrophones(std::vector<media::MicrophoneInfo>* microphones) override;
status_t setAudioHalPids(const std::vector<pid_t>& pids) override;
+ status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
private:
const sp<media::IAudioFlingerService> mDelegate;
@@ -504,6 +511,7 @@
GET_MASTER_BALANCE = media::BnAudioFlingerService::TRANSACTION_getMasterBalance,
SET_EFFECT_SUSPENDED = media::BnAudioFlingerService::TRANSACTION_setEffectSuspended,
SET_AUDIO_HAL_PIDS = media::BnAudioFlingerService::TRANSACTION_setAudioHalPids,
+ SET_VIBRATOR_INFOS = media::BnAudioFlingerService::TRANSACTION_setVibratorInfos,
};
/**
@@ -605,6 +613,7 @@
Status frameCountHAL(int32_t ioHandle, int64_t* _aidl_return) override;
Status getMicrophones(std::vector<media::MicrophoneInfoData>* _aidl_return) override;
Status setAudioHalPids(const std::vector<int32_t>& pids) override;
+ Status setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
private:
const sp<AudioFlingerServerAdapter::Delegate> mDelegate;
diff --git a/media/libaudiofoundation/AudioPort.cpp b/media/libaudiofoundation/AudioPort.cpp
index 20d8632..fafabd9 100644
--- a/media/libaudiofoundation/AudioPort.cpp
+++ b/media/libaudiofoundation/AudioPort.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "AudioPort"
#include <algorithm>
+#include <utility>
+#include <android/media/ExtraAudioDescriptor.h>
#include <android-base/stringprintf.h>
#include <media/AudioPort.h>
#include <utils/Log.h>
@@ -46,11 +48,26 @@
port.audio_profiles->num_channel_masks),
SampleRateSet(port.audio_profiles[i].sample_rates,
port.audio_profiles[i].sample_rates +
- port.audio_profiles[i].num_sample_rates));
+ port.audio_profiles[i].num_sample_rates),
+ port.audio_profiles[i].encapsulation_type);
if (!mProfiles.contains(profile)) {
addAudioProfile(profile);
}
}
+
+ for (size_t i = 0; i < port.num_extra_audio_descriptors; ++i) {
+ auto convertedResult = legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ port.extra_audio_descriptors[i]);
+ if (!convertedResult.ok()) {
+ ALOGE("%s, failed to convert extra audio descriptor", __func__);
+ continue;
+ }
+ if (std::find(mExtraAudioDescriptors.begin(),
+ mExtraAudioDescriptors.end(),
+ convertedResult.value()) == mExtraAudioDescriptors.end()) {
+ mExtraAudioDescriptors.push_back(std::move(convertedResult.value()));
+ }
+ }
}
void AudioPort::toAudioPort(struct audio_port *port) const {
@@ -98,7 +115,7 @@
channelMasks.size() > AUDIO_PORT_MAX_CHANNEL_MASKS ||
port->num_audio_profiles >= AUDIO_PORT_MAX_AUDIO_PROFILES) {
ALOGE("%s: bailing out: cannot export profiles to port config", __func__);
- return;
+ break;
}
auto& dstProfile = port->audio_profiles[port->num_audio_profiles++];
@@ -109,8 +126,25 @@
dstProfile.num_channel_masks = channelMasks.size();
std::copy(channelMasks.begin(), channelMasks.end(),
std::begin(dstProfile.channel_masks));
+ dstProfile.encapsulation_type = profile->getEncapsulationType();
}
}
+
+ port->num_extra_audio_descriptors = 0;
+ for (const auto& desc : mExtraAudioDescriptors) {
+ if (port->num_extra_audio_descriptors >= AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+ ALOGE("%s: bailing out: cannot export extra audio descriptor to port config", __func__);
+ return;
+ }
+
+ auto convertedResult = aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(desc);
+ if (!convertedResult.ok()) {
+ ALOGE("%s: failed to convert extra audio descriptor", __func__);
+ continue;
+ }
+ port->extra_audio_descriptors[port->num_extra_audio_descriptors++] =
+ std::move(convertedResult.value());
+ }
}
void AudioPort::dump(std::string *dst, int spaces, bool verbose) const {
@@ -121,6 +155,22 @@
std::string profilesStr;
mProfiles.dump(&profilesStr, spaces);
dst->append(profilesStr);
+ if (!mExtraAudioDescriptors.empty()) {
+ dst->append(base::StringPrintf("%*s- extra audio descriptors: \n", spaces, ""));
+ const int eadSpaces = spaces + 4;
+ const int descSpaces = eadSpaces + 4;
+ for (size_t i = 0; i < mExtraAudioDescriptors.size(); i++) {
+ dst->append(
+ base::StringPrintf("%*s extra audio descriptor %zu:\n", eadSpaces, "", i));
+ dst->append(base::StringPrintf(
+ "%*s- standard: %u\n", descSpaces, "", mExtraAudioDescriptors[i].standard));
+ dst->append(base::StringPrintf("%*s- descriptor:", descSpaces, ""));
+ for (auto v : mExtraAudioDescriptors[i].audioDescriptor) {
+ dst->append(base::StringPrintf(" %02x", v));
+ }
+ dst->append("\n");
+ }
+ }
if (mGains.size() != 0) {
dst->append(base::StringPrintf("%*s- gains:\n", spaces, ""));
@@ -145,7 +195,8 @@
mName.compare(other->getName()) == 0 &&
mType == other->getType() &&
mRole == other->getRole() &&
- mProfiles.equals(other->getAudioProfiles());
+ mProfiles.equals(other->getAudioProfiles()) &&
+ mExtraAudioDescriptors == other->getExtraAudioDescriptors();
}
status_t AudioPort::writeToParcel(Parcel *parcel) const
@@ -160,6 +211,7 @@
parcelable->type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_type_t_AudioPortType(mType));
parcelable->role = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_role_t_AudioPortRole(mRole));
parcelable->profiles = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioProfileVector(mProfiles));
+ parcelable->extraAudioDescriptors = mExtraAudioDescriptors;
parcelable->gains = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioGains(mGains));
return OK;
}
@@ -175,6 +227,7 @@
mType = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortType_audio_port_type_t(parcelable.type));
mRole = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortRole_audio_port_role_t(parcelable.role));
mProfiles = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioProfileVector(parcelable.profiles));
+ mExtraAudioDescriptors = parcelable.extraAudioDescriptors;
mGains = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioGains(parcelable.gains));
return OK;
}
diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp
index 65f7388..8ac3f73 100644
--- a/media/libaudiofoundation/AudioProfile.cpp
+++ b/media/libaudiofoundation/AudioProfile.cpp
@@ -58,10 +58,18 @@
AudioProfile::AudioProfile(audio_format_t format,
const ChannelMaskSet &channelMasks,
const SampleRateSet &samplingRateCollection) :
+ AudioProfile(format, channelMasks, samplingRateCollection,
+ AUDIO_ENCAPSULATION_TYPE_NONE) {}
+
+AudioProfile::AudioProfile(audio_format_t format,
+ const ChannelMaskSet &channelMasks,
+ const SampleRateSet &samplingRateCollection,
+ audio_encapsulation_type_t encapsulationType) :
mName(""),
mFormat(format),
mChannelMasks(channelMasks),
- mSamplingRates(samplingRateCollection) {}
+ mSamplingRates(samplingRateCollection),
+ mEncapsulationType(encapsulationType) {}
void AudioProfile::setChannels(const ChannelMaskSet &channelMasks)
{
@@ -116,6 +124,9 @@
}
dst->append("\n");
}
+
+ dst->append(base::StringPrintf(
+ "%*s- encapsulation type: %#x\n", spaces, "", mEncapsulationType));
}
bool AudioProfile::equals(const sp<AudioProfile>& other) const
@@ -127,7 +138,8 @@
mSamplingRates == other->getSampleRates() &&
mIsDynamicFormat == other->isDynamicFormat() &&
mIsDynamicChannels == other->isDynamicChannels() &&
- mIsDynamicRate == other->isDynamicRate();
+ mIsDynamicRate == other->isDynamicRate() &&
+ mEncapsulationType == other->getEncapsulationType();
}
AudioProfile& AudioProfile::operator=(const AudioProfile& other) {
@@ -135,6 +147,7 @@
mFormat = other.mFormat;
mChannelMasks = other.mChannelMasks;
mSamplingRates = other.mSamplingRates;
+ mEncapsulationType = other.mEncapsulationType;
mIsDynamicFormat = other.mIsDynamicFormat;
mIsDynamicChannels = other.mIsDynamicChannels;
mIsDynamicRate = other.mIsDynamicRate;
@@ -160,6 +173,8 @@
parcelable.isDynamicFormat = mIsDynamicFormat;
parcelable.isDynamicChannels = mIsDynamicChannels;
parcelable.isDynamicRate = mIsDynamicRate;
+ parcelable.encapsulationType = VALUE_OR_RETURN(
+ legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(mEncapsulationType));
return parcelable;
}
@@ -186,6 +201,9 @@
legacy->mIsDynamicFormat = parcelable.isDynamicFormat;
legacy->mIsDynamicChannels = parcelable.isDynamicChannels;
legacy->mIsDynamicRate = parcelable.isDynamicRate;
+ legacy->mEncapsulationType = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ parcelable.encapsulationType));
return legacy;
}
diff --git a/media/libaudiofoundation/include/media/AudioPort.h b/media/libaudiofoundation/include/media/AudioPort.h
index 633e4e3..1cee1c9 100644
--- a/media/libaudiofoundation/include/media/AudioPort.h
+++ b/media/libaudiofoundation/include/media/AudioPort.h
@@ -21,6 +21,7 @@
#include <android/media/AudioPort.h>
#include <android/media/AudioPortConfig.h>
+#include <android/media/ExtraAudioDescriptor.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <media/AudioGain.h>
@@ -67,6 +68,14 @@
void setAudioProfiles(const AudioProfileVector &profiles) { mProfiles = profiles; }
AudioProfileVector &getAudioProfiles() { return mProfiles; }
+ void setExtraAudioDescriptors(
+ const std::vector<media::ExtraAudioDescriptor> extraAudioDescriptors) {
+ mExtraAudioDescriptors = extraAudioDescriptors;
+ }
+ std::vector<media::ExtraAudioDescriptor> &getExtraAudioDescriptors() {
+ return mExtraAudioDescriptors;
+ }
+
virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
virtual void importAudioPort(const audio_port_v7& port);
@@ -102,6 +111,10 @@
audio_port_type_t mType;
audio_port_role_t mRole;
AudioProfileVector mProfiles; // AudioProfiles supported by this port (format, Rates, Channels)
+
+ // Audio capabilities that are defined by hardware descriptors when the format is unrecognized
+ // by the platform, e.g. short audio descriptor in EDID for HDMI.
+ std::vector<media::ExtraAudioDescriptor> mExtraAudioDescriptors;
private:
template <typename T, std::enable_if_t<std::is_same<T, struct audio_port>::value
|| std::is_same<T, struct audio_port_v7>::value, int> = 0>
diff --git a/media/libaudiofoundation/include/media/AudioProfile.h b/media/libaudiofoundation/include/media/AudioProfile.h
index 5051233..20c7ab9 100644
--- a/media/libaudiofoundation/include/media/AudioProfile.h
+++ b/media/libaudiofoundation/include/media/AudioProfile.h
@@ -38,6 +38,10 @@
AudioProfile(audio_format_t format,
const ChannelMaskSet &channelMasks,
const SampleRateSet &samplingRateCollection);
+ AudioProfile(audio_format_t format,
+ const ChannelMaskSet &channelMasks,
+ const SampleRateSet &samplingRateCollection,
+ audio_encapsulation_type_t encapsulationType);
audio_format_t getFormat() const { return mFormat; }
const ChannelMaskSet &getChannels() const { return mChannelMasks; }
@@ -68,6 +72,11 @@
bool isDynamic() { return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; }
+ audio_encapsulation_type_t getEncapsulationType() const { return mEncapsulationType; }
+ void setEncapsulationType(audio_encapsulation_type_t encapsulationType) {
+ mEncapsulationType = encapsulationType;
+ }
+
void dump(std::string *dst, int spaces) const;
bool equals(const sp<AudioProfile>& other) const;
@@ -79,6 +88,7 @@
static ConversionResult<sp<AudioProfile>> fromParcelable(const media::AudioProfile& parcelable);
private:
+
std::string mName;
audio_format_t mFormat; // The format for an audio profile should only be set when initialized.
ChannelMaskSet mChannelMasks;
@@ -88,6 +98,8 @@
bool mIsDynamicChannels = false;
bool mIsDynamicRate = false;
+ audio_encapsulation_type_t mEncapsulationType;
+
AudioProfile() = default;
AudioProfile& operator=(const AudioProfile& other);
};
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index 03a0d86..ca4f663 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -354,7 +354,8 @@
return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch));
}
-status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+template <typename HalPort>
+status_t DeviceHalHidl::getAudioPortImpl(HalPort *port) {
if (mDevice == 0) return NO_INIT;
AudioPort hidlPort;
HidlUtils::audioPortFromHal(*port, &hidlPort);
@@ -370,31 +371,28 @@
return processReturn("getAudioPort", ret, retval);
}
+status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+ return getAudioPortImpl(port);
+}
+
status_t DeviceHalHidl::getAudioPort(struct audio_port_v7 *port) {
- if (mDevice == 0) return NO_INIT;
- status_t status = NO_ERROR;
#if MAJOR_VERSION >= 7
- AudioPort hidlPort;
- HidlUtils::audioPortFromHal(*port, &hidlPort);
- Result retval;
- Return<void> ret = mDevice->getAudioPort(
- hidlPort,
- [&](Result r, const AudioPort& p) {
- retval = r;
- if (retval == Result::OK) {
- HidlUtils::audioPortToHal(p, port);
- }
- });
- status = processReturn("getAudioPort", ret, retval);
+ return getAudioPortImpl(port);
#else
struct audio_port audioPort = {};
- audio_populate_audio_port(port, &audioPort);
- status = getAudioPort(&audioPort);
+ status_t result = NO_ERROR;
+ if (!audio_populate_audio_port(port, &audioPort)) {
+ ALOGE("Failed to populate legacy audio port from audio_port_v7");
+ result = BAD_VALUE;
+ }
+ status_t status = getAudioPort(&audioPort);
if (status == NO_ERROR) {
audio_populate_audio_port_v7(&audioPort, port);
+ } else {
+ result = status;
}
+ return result;
#endif
- return status;
}
status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) {
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index abd4ad5..2c847cf 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -131,6 +131,8 @@
// The destructor automatically closes the device.
virtual ~DeviceHalHidl();
+
+ template <typename HalPort> status_t getAudioPortImpl(HalPort *port);
};
} // namespace CPP_VERSION
diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp
index aa9e477..af7dc1a 100644
--- a/media/libaudiohal/impl/DeviceHalLocal.cpp
+++ b/media/libaudiohal/impl/DeviceHalLocal.cpp
@@ -181,6 +181,12 @@
}
status_t DeviceHalLocal::getAudioPort(struct audio_port_v7 *port) {
+#if MAJOR_VERSION >= 7
+ if (version() >= AUDIO_DEVICE_API_VERSION_3_2) {
+ // get_audio_port_v7 is mandatory if legacy HAL support this API version.
+ return mDev->get_audio_port_v7(mDev, port);
+ }
+#endif
struct audio_port audioPort = {};
audio_populate_audio_port(port, &audioPort);
status_t status = getAudioPort(&audioPort);
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 2a3e2b6..539a149 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -57,8 +57,7 @@
// Note: This assumes channel mask, format, and sample rate do not change after creation.
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
if (/* mStreamPowerLog.isUserDebugOrEngBuild() && */
- StreamHalHidl::getAudioProperties(
- &config.sample_rate, &config.channel_mask, &config.format) == NO_ERROR) {
+ StreamHalHidl::getAudioProperties(&config) == NO_ERROR) {
mStreamPowerLog.init(config.sample_rate, config.channel_mask, config.format);
}
}
@@ -69,14 +68,6 @@
hardware::IPCThreadState::self()->flushCommands();
}
-// Note: this method will be removed
-status_t StreamHalHidl::getSampleRate(uint32_t *rate) {
- audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
- status_t status = getAudioProperties(&config.sample_rate, &config.channel_mask, &config.format);
- *rate = config.sample_rate;
- return status;
-}
-
status_t StreamHalHidl::getBufferSize(size_t *size) {
if (!mStream) return NO_INIT;
status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size);
@@ -86,48 +77,28 @@
return status;
}
-// Note: this method will be removed
-status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
- audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
- status_t status = getAudioProperties(&config.sample_rate, &config.channel_mask, &config.format);
- *mask = config.channel_mask;
- return status;
-}
-
-// Note: this method will be removed
-status_t StreamHalHidl::getFormat(audio_format_t *format) {
- audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
- status_t status = getAudioProperties(&config.sample_rate, &config.channel_mask, &config.format);
- *format = config.format;
- return status;
-}
-
-status_t StreamHalHidl::getAudioProperties(
- uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+status_t StreamHalHidl::getAudioProperties(audio_config_base_t *configBase) {
+ *configBase = AUDIO_CONFIG_BASE_INITIALIZER;
if (!mStream) return NO_INIT;
#if MAJOR_VERSION <= 6
Return<void> ret = mStream->getAudioProperties(
[&](uint32_t sr, auto m, auto f) {
- *sampleRate = sr;
- *mask = static_cast<audio_channel_mask_t>(m);
- *format = static_cast<audio_format_t>(f);
+ configBase->sample_rate = sr;
+ configBase->channel_mask = static_cast<audio_channel_mask_t>(m);
+ configBase->format = static_cast<audio_format_t>(f);
});
return processReturn("getAudioProperties", ret);
#else
Result retval;
status_t conversionStatus = BAD_VALUE;
- audio_config_base_t halConfig = AUDIO_CONFIG_BASE_INITIALIZER;
Return<void> ret = mStream->getAudioProperties(
[&](Result r, const AudioConfigBase& config) {
retval = r;
if (retval == Result::OK) {
- conversionStatus = HidlUtils::audioConfigBaseToHal(config, &halConfig);
+ conversionStatus = HidlUtils::audioConfigBaseToHal(config, configBase);
}
});
if (status_t status = processReturn("getAudioProperties", ret, retval); status == NO_ERROR) {
- *sampleRate = halConfig.sample_rate;
- *mask = halConfig.channel_mask;
- *format = halConfig.format;
return conversionStatus;
} else {
return status;
diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h
index c6db6d6..970903b 100644
--- a/media/libaudiohal/impl/StreamHalHidl.h
+++ b/media/libaudiohal/impl/StreamHalHidl.h
@@ -49,21 +49,14 @@
class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl
{
public:
- // Return the sampling rate in Hz - eg. 44100.
- virtual status_t getSampleRate(uint32_t *rate);
-
// Return size of input/output buffer in bytes for this stream - eg. 4800.
virtual status_t getBufferSize(size_t *size);
- // Return the channel mask.
- virtual status_t getChannelMask(audio_channel_mask_t *mask);
-
- // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
- virtual status_t getFormat(audio_format_t *format);
-
- // Convenience method.
- virtual status_t getAudioProperties(
- uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+ // Return the base configuration of the stream:
+ // - channel mask;
+ // - format - e.g. AUDIO_FORMAT_PCM_16_BIT;
+ // - sampling rate in Hz - eg. 44100.
+ virtual status_t getAudioProperties(audio_config_base_t *configBase);
// Set audio stream parameters.
virtual status_t setParameters(const String8& kvPairs);
diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp
index e89b288..d0c375e 100644
--- a/media/libaudiohal/impl/StreamHalLocal.cpp
+++ b/media/libaudiohal/impl/StreamHalLocal.cpp
@@ -45,31 +45,15 @@
mDevice.clear();
}
-status_t StreamHalLocal::getSampleRate(uint32_t *rate) {
- *rate = mStream->get_sample_rate(mStream);
- return OK;
-}
-
status_t StreamHalLocal::getBufferSize(size_t *size) {
*size = mStream->get_buffer_size(mStream);
return OK;
}
-status_t StreamHalLocal::getChannelMask(audio_channel_mask_t *mask) {
- *mask = mStream->get_channels(mStream);
- return OK;
-}
-
-status_t StreamHalLocal::getFormat(audio_format_t *format) {
- *format = mStream->get_format(mStream);
- return OK;
-}
-
-status_t StreamHalLocal::getAudioProperties(
- uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
- *sampleRate = mStream->get_sample_rate(mStream);
- *mask = mStream->get_channels(mStream);
- *format = mStream->get_format(mStream);
+status_t StreamHalLocal::getAudioProperties(audio_config_base_t *configBase) {
+ configBase->sample_rate = mStream->get_sample_rate(mStream);
+ configBase->channel_mask = mStream->get_channels(mStream);
+ configBase->format = mStream->get_format(mStream);
return OK;
}
diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h
index e228104..b260495 100644
--- a/media/libaudiohal/impl/StreamHalLocal.h
+++ b/media/libaudiohal/impl/StreamHalLocal.h
@@ -28,21 +28,14 @@
class StreamHalLocal : public virtual StreamHalInterface
{
public:
- // Return the sampling rate in Hz - eg. 44100.
- virtual status_t getSampleRate(uint32_t *rate);
-
// Return size of input/output buffer in bytes for this stream - eg. 4800.
virtual status_t getBufferSize(size_t *size);
- // Return the channel mask.
- virtual status_t getChannelMask(audio_channel_mask_t *mask);
-
- // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
- virtual status_t getFormat(audio_format_t *format);
-
- // Convenience method.
- virtual status_t getAudioProperties(
- uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+ // Return the base configuration of the stream:
+ // - channel mask;
+ // - format - e.g. AUDIO_FORMAT_PCM_16_BIT;
+ // - sampling rate in Hz - eg. 44100.
+ virtual status_t getAudioProperties(audio_config_base_t *configBase);
// Set audio stream parameters.
virtual status_t setParameters(const String8& kvPairs);
diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
index b47f536..2be12fb 100644
--- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
@@ -31,25 +31,27 @@
class StreamHalInterface : public virtual RefBase
{
public:
- // TODO(mnaganov): Remove
- // Return the sampling rate in Hz - eg. 44100.
- virtual status_t getSampleRate(uint32_t *rate) = 0;
-
// Return size of input/output buffer in bytes for this stream - eg. 4800.
virtual status_t getBufferSize(size_t *size) = 0;
- // TODO(mnaganov): Remove
- // Return the channel mask.
- virtual status_t getChannelMask(audio_channel_mask_t *mask) = 0;
+ // Return the base configuration of the stream:
+ // - channel mask;
+ // - format - e.g. AUDIO_FORMAT_PCM_16_BIT;
+ // - sampling rate in Hz - eg. 44100.
+ virtual status_t getAudioProperties(audio_config_base_t *configBase) = 0;
- // TODO(mnaganov): Remove
- // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
- virtual status_t getFormat(audio_format_t *format) = 0;
-
- // TODO(mnaganov): Change to use audio_config_base_t
// Convenience method.
- virtual status_t getAudioProperties(
- uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) = 0;
+ inline status_t getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+ audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+ const status_t result = getAudioProperties(&config);
+ if (result == NO_ERROR) {
+ if (sampleRate != nullptr) *sampleRate = config.sample_rate;
+ if (mask != nullptr) *mask = config.channel_mask;
+ if (format != nullptr) *format = config.format;
+ }
+ return result;
+ }
// Set audio stream parameters.
virtual status_t setParameters(const String8& kvPairs) = 0;
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index f2245b1..65a20a7 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -26,11 +26,16 @@
#include <errno.h>
#include <inttypes.h>
+#include <math.h>
#include <audio_effects/effect_hapticgenerator.h>
#include <audio_utils/format.h>
#include <system/audio.h>
+static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
+static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
+static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
+
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
@@ -101,11 +106,11 @@
context->param.audioChannelCount = 0;
context->param.maxHapticIntensity = os::HapticScale::MUTE;
- context->param.resonantFrequency = 150.0f;
+ context->param.resonantFrequency = DEFAULT_RESONANT_FREQUENCY;
context->param.bpfQ = 1.0f;
context->param.slowEnvNormalizationPower = -0.8f;
- context->param.bsfZeroQ = 8.0f;
- context->param.bsfPoleQ = 4.0f;
+ context->param.bsfZeroQ = DEFAULT_BSF_ZERO_Q;
+ context->param.bsfPoleQ = DEFAULT_BSF_POLE_Q;
context->param.distortionCornerFrequency = 300.0f;
context->param.distortionInputGain = 0.3f;
context->param.distortionCubeThreshold = 0.1f;
@@ -173,6 +178,7 @@
addBiquadFilter(processingChain, processorsRecord, lpf);
auto bpf = createBPF(param->resonantFrequency, param->bpfQ, sampleRate, channelCount);
+ processorsRecord.bpf = bpf;
addBiquadFilter(processingChain, processorsRecord, bpf);
float normalizationPower = param->slowEnvNormalizationPower;
@@ -191,6 +197,7 @@
auto bsf = createBSF(
param->resonantFrequency, param->bsfZeroQ, param->bsfPoleQ, sampleRate, channelCount);
+ processorsRecord.bsf = bsf;
addBiquadFilter(processingChain, processorsRecord, bsf);
// The process chain captures the shared pointer of the Distortion in lambda. It will
@@ -279,7 +286,32 @@
}
break;
}
+ case HG_PARAM_VIBRATOR_INFO: {
+ if (value == nullptr || size != 2 * sizeof(float)) {
+ return -EINVAL;
+ }
+ const float resonantFrequency = *(float*) value;
+ const float qFactor = *((float *) value + 1);
+ context->param.resonantFrequency =
+ isnan(resonantFrequency) ? DEFAULT_RESONANT_FREQUENCY : resonantFrequency;
+ context->param.bsfZeroQ = isnan(qFactor) ? DEFAULT_BSF_POLE_Q : qFactor;
+ context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
+ if (context->processorsRecord.bpf != nullptr) {
+ context->processorsRecord.bpf->setCoefficients(
+ bpfCoefs(context->param.resonantFrequency,
+ context->param.bpfQ,
+ context->config.inputCfg.samplingRate));
+ }
+ if (context->processorsRecord.bsf != nullptr) {
+ context->processorsRecord.bsf->setCoefficients(
+ bsfCoefs(context->param.resonantFrequency,
+ context->param.bsfZeroQ,
+ context->param.bsfPoleQ,
+ context->config.inputCfg.samplingRate));
+ }
+ HapticGenerator_Reset(context);
+ } break;
default:
ALOGW("Unknown param: %d", param);
return -EINVAL;
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
index d2d7afe..96b744a 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
@@ -69,6 +69,11 @@
std::vector<std::shared_ptr<Ramp>> ramps;
std::vector<std::shared_ptr<SlowEnvelope>> slowEnvs;
std::vector<std::shared_ptr<Distortion>> distortions;
+
+ // Cache band-pass filter and band-stop filter for updating parameters
+ // according to vibrator info
+ std::shared_ptr<HapticBiquadFilter> bpf;
+ std::shared_ptr<HapticBiquadFilter> bsf;
};
// A structure to keep all the context for HapticGenerator.
diff --git a/media/libeffects/hapticgenerator/Processors.cpp b/media/libeffects/hapticgenerator/Processors.cpp
index 79a4e2c..4fe3a75 100644
--- a/media/libeffects/hapticgenerator/Processors.cpp
+++ b/media/libeffects/hapticgenerator/Processors.cpp
@@ -211,9 +211,9 @@
}
BiquadFilterCoefficients bsfCoefs(const float ringingFrequency,
- const float sampleRate,
const float zq,
- const float pq) {
+ const float pq,
+ const float sampleRate) {
BiquadFilterCoefficients coefficient;
const auto [zeroReal, zeroImg] = getComplexPoleZ(ringingFrequency, zq, sampleRate);
float zeroCoeff1 = -2 * zeroReal;
@@ -275,7 +275,7 @@
const float pq,
const float sampleRate,
const size_t channelCount) {
- BiquadFilterCoefficients coefficient = bsfCoefs(ringingFrequency, sampleRate, zq, pq);
+ BiquadFilterCoefficients coefficient = bsfCoefs(ringingFrequency, zq, pq, sampleRate);
return std::make_shared<HapticBiquadFilter>(channelCount, coefficient);
}
diff --git a/media/libeffects/hapticgenerator/Processors.h b/media/libeffects/hapticgenerator/Processors.h
index 452a985..74ca77d 100644
--- a/media/libeffects/hapticgenerator/Processors.h
+++ b/media/libeffects/hapticgenerator/Processors.h
@@ -102,9 +102,9 @@
const float sampleRate);
BiquadFilterCoefficients bsfCoefs(const float ringingFrequency,
- const float sampleRate,
const float zq,
- const float pq);
+ const float pq,
+ const float sampleRate);
std::shared_ptr<HapticBiquadFilter> createLPF(const float cornerFrequency,
const float sampleRate,
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp
index 20058a1..4eea04f 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.cpp
@@ -23,6 +23,7 @@
#include <system/audio.h>
#include "LVM_Private.h"
+#include "ScalarArithmetic.h"
#include "VectorArithmetic.h"
#include "LVM_Coeffs.h"
@@ -178,6 +179,9 @@
* Apply the filter
*/
pInstance->pTEBiquad->process(pProcessed, pProcessed, NrFrames);
+ for (auto i = 0; i < NrChannels * NrFrames; i++) {
+ pProcessed[i] = LVM_Clamp(pProcessed[i]);
+ }
}
/*
* Volume balance
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
index df7ca5a..7571a24 100755
--- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
@@ -53,16 +53,16 @@
flags_arr=(
"-csE"
"-eqE"
- "-tE"
- "-csE -tE -eqE"
+ "-tE -trebleLvl:15"
+ "-csE -tE -trebleLvl:15 -eqE"
"-bE -M"
- "-csE -tE"
- "-csE -eqE" "-tE -eqE"
- "-csE -tE -bE -M -eqE"
- "-tE -eqE -vcBal:96 -M"
- "-tE -eqE -vcBal:-96 -M"
- "-tE -eqE -vcBal:0 -M"
- "-tE -eqE -bE -vcBal:30 -M"
+ "-csE -tE -trebleLvl:15"
+ "-csE -eqE" "-tE -trebleLvl:15 -eqE"
+ "-csE -tE -trebleLvl:15 -bE -M -eqE"
+ "-tE -trebleLvl:15 -eqE -vcBal:96 -M"
+ "-tE -trebleLvl:15 -eqE -vcBal:-96 -M"
+ "-tE -trebleLvl:15 -eqE -vcBal:0 -M"
+ "-tE -trebleLvl:15 -eqE -bE -vcBal:30 -M"
)
fs_arr=(
diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp
index e484a1a..e65228c 100644
--- a/media/libeffects/lvm/tests/lvmtest.cpp
+++ b/media/libeffects/lvm/tests/lvmtest.cpp
@@ -79,6 +79,7 @@
int bassEffectLevel = 0;
int eqPresetLevel = 0;
int frameLength = 256;
+ int trebleEffectLevel = 0;
LVM_BE_Mode_en bassEnable = LVM_BE_OFF;
LVM_TE_Mode_en trebleEnable = LVM_TE_OFF;
LVM_EQNB_Mode_en eqEnable = LVM_EQNB_OFF;
@@ -303,10 +304,6 @@
params->PSA_Enable = LVM_PSA_OFF;
params->PSA_PeakDecayRate = LVM_PSA_SPEED_MEDIUM;
- /* TE Control parameters */
- params->TE_OperatingMode = LVM_TE_OFF;
- params->TE_EffectLevel = 0;
-
/* Activate the initial settings */
LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, params);
@@ -445,6 +442,7 @@
/* Treble Enhancement parameters */
params->TE_OperatingMode = plvmConfigParams->trebleEnable;
+ params->TE_EffectLevel = plvmConfigParams->trebleEffectLevel;
/* PSA Control parameters */
params->PSA_Enable = LVM_PSA_ON;
@@ -604,6 +602,15 @@
return -1;
}
lvmConfigParams.eqPresetLevel = eqPresetLevel;
+ } else if (!strncmp(argv[i], "-trebleLvl:", 11)) {
+ const int trebleEffectLevel = atoi(argv[i] + 11);
+ if (trebleEffectLevel > LVM_TE_MAX_EFFECTLEVEL ||
+ trebleEffectLevel < LVM_TE_MIN_EFFECTLEVEL) {
+ printf("Error: Unsupported Treble Effect Level : %d\n", trebleEffectLevel);
+ printUsage();
+ return -1;
+ }
+ lvmConfigParams.trebleEffectLevel = trebleEffectLevel;
} else if (!strcmp(argv[i], "-bE")) {
lvmConfigParams.bassEnable = LVM_BE_ON;
} else if (!strcmp(argv[i], "-eqE")) {
diff --git a/media/libmedia/tests/fuzzer/Android.bp b/media/libmedia/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..c03b5b1
--- /dev/null
+++ b/media/libmedia/tests/fuzzer/Android.bp
@@ -0,0 +1,19 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_libmedia_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_libmedia_license"],
+}
+
+cc_fuzz {
+ name: "libmedia_metadata_fuzzer",
+ srcs: [
+ "libmedia_metadata_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libmedia",
+ "libbinder",
+ ],
+}
diff --git a/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp b/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp
new file mode 100644
index 0000000..058e4e5
--- /dev/null
+++ b/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp
@@ -0,0 +1,52 @@
+//This program fuzzes Metadata.cpp
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/Metadata.h>
+#include <binder/Parcel.h>
+
+using namespace android;
+using namespace media;
+
+static const float want_prob = 0.5;
+
+bool bytesRemain(FuzzedDataProvider *fdp);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ Parcel p;
+ Metadata md = Metadata(&p);
+
+ md.appendHeader();
+ while (bytesRemain(&fdp)) {
+
+ float got_prob = fdp.ConsumeProbability<float>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+
+ if (got_prob < want_prob) {
+ int32_t key_bool = fdp.ConsumeIntegral<int32_t>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+ bool val_bool = fdp.ConsumeBool();
+ md.appendBool(key_bool, val_bool);
+ } else {
+ int32_t key_int32 = fdp.ConsumeIntegral<int32_t>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+ bool val_int32 = fdp.ConsumeIntegral<int32_t>();
+ md.appendInt32(key_int32, val_int32);
+ }
+ md.updateLength();
+ }
+ md.resetParcel();
+ return 0;
+}
+
+bool bytesRemain(FuzzedDataProvider *fdp){
+ return fdp -> remaining_bytes() > 0;
+}
\ No newline at end of file
diff --git a/media/libmediaformatshaper/Android.bp b/media/libmediaformatshaper/Android.bp
index 731ff4c..3107e12 100644
--- a/media/libmediaformatshaper/Android.bp
+++ b/media/libmediaformatshaper/Android.bp
@@ -16,6 +16,15 @@
// these headers include the structure of needed function pointers
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
cc_library_headers {
name: "libmediaformatshaper_headers",
export_include_dirs: ["include"],
@@ -36,6 +45,7 @@
name: "libmediaformatshaper_defaults",
srcs: [
"CodecProperties.cpp",
+ "CodecSeeding.cpp",
"FormatShaper.cpp",
"ManageShapingCodecs.cpp",
"VideoShaper.cpp",
diff --git a/media/libmediaformatshaper/CodecProperties.cpp b/media/libmediaformatshaper/CodecProperties.cpp
index dccfd95..e6b3c46 100644
--- a/media/libmediaformatshaper/CodecProperties.cpp
+++ b/media/libmediaformatshaper/CodecProperties.cpp
@@ -19,13 +19,19 @@
#include <utils/Log.h>
#include <string>
+#include <stdlib.h>
#include <media/formatshaper/CodecProperties.h>
+
+// we aren't going to mess with shaping points dimensions beyond this
+static const int32_t DIMENSION_LIMIT = 16384;
+
namespace android {
namespace mediaformatshaper {
CodecProperties::CodecProperties(std::string name, std::string mediaType) {
+ ALOGV("CodecProperties(%s, %s)", name.c_str(), mediaType.c_str());
mName = name;
mMediaType = mediaType;
}
@@ -58,6 +64,209 @@
return mApi;
}
+void CodecProperties::setFeatureValue(std::string key, int32_t value) {
+ ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
+ mFeatures.insert({key, value});
+
+ if (!strcmp(key.c_str(), "qp-bounds")) { // official key
+ setSupportsQp(1);
+ } else if (!strcmp(key.c_str(), "vq-supports-qp")) { // key from prototyping
+ setSupportsQp(1);
+ } else if (!strcmp(key.c_str(), "vq-minimum-quality")) {
+ setSupportedMinimumQuality(1);
+ }
+}
+
+bool CodecProperties::getFeatureValue(std::string key, int32_t *valuep) {
+ ALOGV("getFeatureValue(%s)", key.c_str());
+ if (valuep == nullptr) {
+ return false;
+ }
+ auto mapped = mFeatures.find(key);
+ if (mapped != mFeatures.end()) {
+ *valuep = mapped->second;
+ return true;
+ }
+ return false;
+}
+
+// Tuning values (which differ from Features)
+// this is where we set up things like target bitrates and QP ranges
+// NB the tuning values arrive as a string, allowing us to convert it into an appropriate
+// format (int, float, ranges, other combinations)
+//
+void CodecProperties::setTuningValue(std::string key, std::string value) {
+ ALOGD("setTuningValue(%s,%s)", key.c_str(), value.c_str());
+ mTunings.insert({key, value});
+
+ bool legal = false;
+ // NB: old school strtol() because std::stoi() throws exceptions
+ if (!strcmp(key.c_str(), "vq-target-qpmax")) {
+ const char *p = value.c_str();
+ char *q;
+ int32_t iValue = strtol(p, &q, 0);
+ if (q != p) {
+ setTargetQpMax(iValue);
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-target-bpp")) {
+ const char *p = value.c_str();
+ char *q;
+ double bpp = strtod(p, &q);
+ if (q != p) {
+ setBpp(bpp);
+ legal = true;
+ }
+ } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) {
+ std::string resolution = key.substr(strlen("vq-target-bpp-"));
+ if (bppPoint(resolution, value)) {
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
+ // legacy, prototyping
+ const char *p = value.c_str();
+ char *q;
+ int32_t iValue = strtol(p, &q, 0);
+ if (q != p) {
+ double bpp = iValue / 100.0;
+ setBpp(bpp);
+ legal = true;
+ }
+ } else {
+ legal = true;
+ }
+
+ if (!legal) {
+ ALOGW("setTuningValue() unable to apply tuning '%s' with value '%s'",
+ key.c_str(), value.c_str());
+ }
+ return;
+}
+
+bool CodecProperties::getTuningValue(std::string key, std::string &value) {
+ ALOGV("getTuningValue(%s)", key.c_str());
+ auto mapped = mFeatures.find(key);
+ if (mapped != mFeatures.end()) {
+ value = mapped->second;
+ return true;
+ }
+ return false;
+}
+
+bool CodecProperties::bppPoint(std::string resolution, std::string value) {
+
+ int32_t width = 0;
+ int32_t height = 0;
+ double bpp = -1;
+
+ // resolution is "WxH", "W*H" or a standard name like "720p"
+ if (resolution == "1080p") {
+ width = 1080; height = 1920;
+ } else if (resolution == "720p") {
+ width = 720; height = 1280;
+ } else if (resolution == "540p") {
+ width = 540; height = 960;
+ } else if (resolution == "480p") {
+ width = 480; height = 854;
+ } else {
+ size_t sep = resolution.find('x');
+ if (sep == std::string::npos) {
+ sep = resolution.find('*');
+ }
+ if (sep == std::string::npos) {
+ ALOGW("unable to parse resolution: '%s'", resolution.c_str());
+ return false;
+ }
+ std::string w = resolution.substr(0, sep);
+ std::string h = resolution.substr(sep+1);
+
+ char *q;
+ const char *p = w.c_str();
+ width = strtol(p, &q, 0);
+ if (q == p) {
+ width = -1;
+ }
+ p = h.c_str();
+ height = strtol(p, &q, 0);
+ if (q == p) {
+ height = -1;
+ }
+ if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
+ ALOGW("unparseable: width, height '%s'", resolution.c_str());
+ return false;
+ }
+ }
+
+ const char *p = value.c_str();
+ char *q;
+ bpp = strtod(p, &q);
+ if (q == p) {
+ ALOGW("unparseable bpp '%s'", value.c_str());
+ return false;
+ }
+
+ struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point));
+ if (point == nullptr) {
+ ALOGW("unable to allocate memory for bpp point");
+ return false;
+ }
+
+ point->pixels = width * height;
+ point->width = width;
+ point->height = height;
+ point->bpp = bpp;
+
+ if (mBppPoints == nullptr) {
+ point->next = nullptr;
+ mBppPoints = point;
+ } else if (point->pixels < mBppPoints->pixels) {
+ // at the front
+ point->next = mBppPoints;
+ mBppPoints = point;
+ } else {
+ struct bpp_point *after = mBppPoints;
+ while (after->next) {
+ if (point->pixels > after->next->pixels) {
+ after = after->next;
+ continue;
+ }
+
+ // insert before after->next
+ point->next = after->next;
+ after->next = point;
+ break;
+ }
+ if (after->next == nullptr) {
+ // hasn't gone in yet
+ point->next = nullptr;
+ after->next = point;
+ }
+ }
+
+ return true;
+}
+
+double CodecProperties::getBpp(int32_t width, int32_t height) {
+ // look in the per-resolution list
+
+ int32_t pixels = width * height;
+
+ if (mBppPoints) {
+ struct bpp_point *point = mBppPoints;
+ while (point && point->pixels < pixels) {
+ point = point->next;
+ }
+ if (point) {
+ ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d",
+ width, height, point->bpp, point->width, point->height);
+ return point->bpp;
+ }
+ }
+
+ ALOGV("defaulting to %f bpp", mBpp);
+ return mBpp;
+}
+
std::string CodecProperties::getMapping(std::string key, std::string kind) {
ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
//play with mMappings
diff --git a/media/libmediaformatshaper/CodecSeeding.cpp b/media/libmediaformatshaper/CodecSeeding.cpp
new file mode 100644
index 0000000..a7fcc66
--- /dev/null
+++ b/media/libmediaformatshaper/CodecSeeding.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2021, 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 "CodecSeeding"
+#include <utils/Log.h>
+
+#include <string>
+
+#include <media/formatshaper/CodecProperties.h>
+
+namespace android {
+namespace mediaformatshaper {
+
+/*
+ * a block of pre-loaded tunings for codecs.
+ *
+ * things the library seeds into the codecproperties based
+ * on the mediaType.
+ * XXX: parsing from a file is likely better than embedding in code.
+ */
+typedef struct {
+ bool overrideable;
+ const char *key;
+ const char *value;
+} preloadTuning_t;
+
+typedef struct {
+ const char *mediaType;
+ preloadTuning_t *features;
+} preloadTunings_t;
+
+/*
+ * 240 = 2.4 bits per pixel-per-second == 5mbps@1080, 2.3mbps@720p, which is about where
+ * we want our initial floor for now.
+ */
+
+static preloadTuning_t featuresAvc[] = {
+ {true, "vq-target-bpp", "2.45"},
+ {true, "vq-target-bpp-1080p", "2.40"},
+ {true, "vq-target-bpp-540p", "2.60"},
+ {true, "vq-target-bpp-480p", "3.00"},
+ {true, "vq-target-qpmax", "40"},
+ {true, nullptr, 0}
+};
+
+static preloadTuning_t featuresHevc[] = {
+ {true, "vq-target-bpp", "2.30"},
+ {true, "vq-target-qpmax", "40"}, // nop, since hevc codecs don't declare qp support
+ {true, nullptr, 0}
+};
+
+static preloadTuning_t featuresGenericVideo[] = {
+ {true, "vq-target-bpp", "2.40"},
+ {true, nullptr, 0}
+};
+
+static preloadTunings_t preloadTunings[] = {
+ { "video/avc", featuresAvc},
+ { "video/hevc", &featuresHevc[0]},
+
+ // wildcard for any video format not already captured
+ { "video/*", &featuresGenericVideo[0]},
+
+ { nullptr, nullptr}
+};
+
+void CodecProperties::addMediaDefaults(bool overrideable) {
+ ALOGD("Seed: codec %s, mediatype %s, overrideable %d",
+ mName.c_str(), mMediaType.c_str(), overrideable);
+
+ // load me up with initial configuration data
+ int count = 0;
+ for (int i = 0; ; i++) {
+ preloadTunings_t *p = &preloadTunings[i];
+ if (p->mediaType == nullptr) {
+ break;
+ }
+ bool found = false;
+ if (strcmp(p->mediaType, mMediaType.c_str()) == 0) {
+ found = true;
+ }
+ const char *r;
+ if (!found && (r = strchr(p->mediaType, '*')) != NULL) {
+ // wildcard; check the prefix
+ size_t len = r - p->mediaType;
+ if (strncmp(p->mediaType, mMediaType.c_str(), len) == 0) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ continue;
+ }
+ ALOGV("seeding from mediaType '%s'", p->mediaType);
+
+ // walk through, filling things
+ if (p->features != nullptr) {
+ for (int j=0;; j++) {
+ preloadTuning_t *q = &p->features[j];
+ if (q->key == nullptr) {
+ break;
+ }
+ if (q->overrideable != overrideable) {
+ continue;
+ }
+ setTuningValue(q->key, q->value);
+ count++;
+ }
+ break;
+ }
+ }
+ ALOGV("loaded %d preset values", count);
+}
+
+// a chance, as we create the codec to inject any default behaviors we want.
+// XXX: consider whether we need pre/post or just post. it affects what can be
+// overridden by way of the codec XML
+//
+void CodecProperties::Seed() {
+ ALOGV("Seed: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());
+ addMediaDefaults(true);
+}
+
+void CodecProperties::Finish() {
+ ALOGV("Finish: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());
+ addMediaDefaults(false);
+}
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/FormatShaper.cpp b/media/libmediaformatshaper/FormatShaper.cpp
index ca4dc72..42502e0 100644
--- a/media/libmediaformatshaper/FormatShaper.cpp
+++ b/media/libmediaformatshaper/FormatShaper.cpp
@@ -93,19 +93,26 @@
return -1;
}
- if (!strcmp(feature, "vq-minimum-quality")) {
- codec->setSupportedMinimumQuality(value);
- } else if (!strcmp(feature, "vq-supports-qp")) {
- codec->setSupportsQp(value != 0);
- } else if (!strcmp(feature, "vq-target-qpmax")) {
- codec->setTargetQpMax(value);
- } else if (!strcmp(feature, "vq-target-bppx100")) {
- double bpp = value / 100.0;
- codec->setBpp(bpp);
- } else {
- // changed nothing, don't mark as configured
- return 0;
+ // save a map of all features
+ codec->setFeatureValue(feature, value);
+
+ return 0;
+}
+
+int setTuning(shaperHandle_t shaper, const char *tuning, const char *value) {
+ ALOGV("setTuning: tuning %s value %s", tuning, value);
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return -1;
}
+ // must not yet be registered
+ if (codec->isRegistered()) {
+ return -1;
+ }
+
+ // save a map of all features
+ codec->setTuningValue(tuning, value);
+
return 0;
}
@@ -120,6 +127,9 @@
shaperHandle_t createShaper(const char *codecName, const char *mediaType) {
CodecProperties *codec = new CodecProperties(codecName, mediaType);
+ if (codec != nullptr) {
+ codec->Seed();
+ }
return (shaperHandle_t) codec;
}
@@ -134,6 +144,12 @@
return nullptr;
}
+ // any final cleanup for the parameters. This allows us to override
+ // bad parameters from a devices XML file.
+ codec->Finish();
+
+ // may return a different codec, if we lost a race.
+ // if so, registerCodec() reclaims the one we tried to register for us.
codec = registerCodec(codec, codecName, mediaType);
return (shaperHandle_t) codec;
}
@@ -177,6 +193,8 @@
.shapeFormat = shapeFormat,
.getMappings = getMappings,
.getReverseMappings = getReverseMappings,
+
+ .setTuning = setTuning,
};
} // namespace mediaformatshaper
diff --git a/media/libmediaformatshaper/VQApply.cpp b/media/libmediaformatshaper/VQApply.cpp
index 6f6f33c..08e23cc 100644
--- a/media/libmediaformatshaper/VQApply.cpp
+++ b/media/libmediaformatshaper/VQApply.cpp
@@ -44,76 +44,116 @@
#define AMEDIAFORMAT_VIDEO_QP_P_MAX "video-qp-p-max"
#define AMEDIAFORMAT_VIDEO_QP_P_MIN "video-qp-p-min"
+// defined in the SDK, but not in the NDK
+//
+static const int BITRATE_MODE_VBR = 1;
+
+
+// constants we use within the calculations
+//
+constexpr double BITRATE_LEAVE_UNTOUCHED = 2.0;
+constexpr double BITRATE_QP_UNAVAILABLE = 1.20;
+// 10% didn't work so hot on bonito (with no QP support)
+// 15% is next.. still leaves a few short
+// 20% ? this is on the edge of what I want do do
+
//
// Caller retains ownership of and responsibility for inFormat
//
int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
- if (codec->supportedMinimumQuality() > 0) {
- // allow the codec provided minimum quality behavior to work at it
- ALOGD("minquality(codec): codec says %d", codec->supportedMinimumQuality());
+ int32_t bitRateMode = -1;
+ if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
+ && bitRateMode != BITRATE_MODE_VBR) {
+ ALOGD("minquality: applies only to VBR encoding");
return 0;
}
- ALOGD("considering other ways to improve quality...");
+ if (codec->supportedMinimumQuality() > 0) {
+ // allow the codec provided minimum quality behavior to work at it
+ ALOGD("minquality: codec claims to implement minquality=%d",
+ codec->supportedMinimumQuality());
+ return 0;
+ }
//
- // apply any and all tools that we have.
+ // consider any and all tools available
// -- qp
// -- minimum bits-per-pixel
//
- if (codec->supportsQp()) {
- // use a (configurable) QP value to force better quality
- //
- // XXX: augment this so that we don't lower an existing QP setting
- // (e.g. if user set it to 40, we don't want to set it back to 45)
- int qpmax = codec->targetQpMax();
- if (qpmax <= 0) {
- qpmax = 45;
- ALOGD("use default substitute QpMax == %d", qpmax);
- }
- ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
- AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
+ int64_t bitrateChosen = 0;
+ int32_t qpChosen = INT32_MAX;
- // force spreading the QP across frame types, since we imposing a value
- qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
- } else {
- ALOGD("codec %s: no qp bounding", codec->getName().c_str());
+ int64_t bitrateConfigured = 0;
+ int32_t bitrateConfiguredTmp = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
+ bitrateConfigured = bitrateConfiguredTmp;
+ bitrateChosen = bitrateConfigured;
+
+ int32_t width = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
+ int32_t height = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
+ int64_t pixels = ((int64_t)width) * height;
+ double minimumBpp = codec->getBpp(width, height);
+
+ int64_t bitrateFloor = pixels * minimumBpp;
+ if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
+
+ // if we are far enough above the target bpp, leave it alone
+ //
+ ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
+ if (bitrateConfigured >= BITRATE_LEAVE_UNTOUCHED * bitrateFloor) {
+ ALOGV("high enough bitrate: configured %" PRId64 " >= %f * floor %" PRId64,
+ bitrateConfigured, BITRATE_LEAVE_UNTOUCHED, bitrateFloor);
+ return 0;
}
- double bpp = codec->getBpp();
- if (bpp > 0.0) {
- // if we've decided to use bits-per-pixel (per second) to drive the quality
- //
- // (properly phrased as 'bits per second per pixel' so that it's resolution
- // and framerate agnostic
- //
- // all of these is structured so that a missing value cleanly gets us to a
- // non-faulting value of '0' for the minimum bits-per-pixel.
- //
- int32_t width = 0;
- (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
- int32_t height = 0;
- (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
- int32_t bitrateConfigured = 0;
- (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured);
+ // raise anything below the bitrate floor
+ if (bitrateConfigured < bitrateFloor) {
+ ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
+ bitrateConfigured, bitrateFloor);
+ bitrateChosen = bitrateFloor;
+ }
- int64_t pixels = ((int64_t)width) * height;
- int64_t bitrateFloor = pixels * bpp;
+ bool qpPresent = hasQp(inFormat);
- if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
-
- ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)",
- bitrateConfigured, bitrateFloor, codec->getBpp(), height, width);
-
- if (bitrateConfigured < bitrateFloor) {
- ALOGD("minquality/target bitrate raised from %d to %" PRId64 " to maintain quality",
- bitrateConfigured, bitrateFloor);
- AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor);
+ // add QP, if not already present
+ if (!qpPresent) {
+ int32_t qpmax = codec->targetQpMax();
+ if (qpmax != INT32_MAX) {
+ ALOGV("choosing qp=%d", qpmax);
+ qpChosen = qpmax;
}
}
+ // if QP is desired but not supported, compensate with additional bits
+ if (!codec->supportsQp()) {
+ if (qpPresent || qpChosen != INT32_MAX) {
+ ALOGD("minquality: desired QP, but unsupported, boost bitrate %" PRId64 " to %" PRId64,
+ bitrateChosen, (int64_t)(bitrateChosen * BITRATE_QP_UNAVAILABLE));
+ bitrateChosen = bitrateChosen * BITRATE_QP_UNAVAILABLE;
+ qpChosen = INT32_MAX;
+ }
+ }
+
+ // apply our chosen values
+ //
+ if (qpChosen != INT32_MAX) {
+ ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
+ AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
+
+ // force spreading the QP across frame types, since we are imposing a value
+ qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
+ }
+
+ if (bitrateChosen != bitrateConfigured) {
+ ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
+ bitrateConfigured, bitrateChosen);
+ AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
+ }
+
return 0;
}
@@ -121,16 +161,16 @@
bool hasQpPerFrameType(AMediaFormat *format) {
int32_t value;
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
- || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
return true;
}
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
- || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
return true;
}
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
- || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
return true;
}
return false;
@@ -138,8 +178,8 @@
bool hasQp(AMediaFormat *format) {
int32_t value;
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
- || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
return true;
}
return hasQpPerFrameType(format);
diff --git a/media/libmediaformatshaper/VideoShaper.cpp b/media/libmediaformatshaper/VideoShaper.cpp
index fecd3a1..f772a66 100644
--- a/media/libmediaformatshaper/VideoShaper.cpp
+++ b/media/libmediaformatshaper/VideoShaper.cpp
@@ -83,7 +83,7 @@
// apply any quality transforms in here..
(void) VQApply(codec, info, inFormat, flags);
- // We must always spread and map any QP parameters.
+ // We must always spread any QP parameters.
// Sometimes it's something we inserted here, sometimes it's a value that the user injected.
//
qpSpreadPerFrameType(inFormat, info->qpDelta, info->qpMin, info->qpMax, /* override */ false);
diff --git a/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h b/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
index f7177a4..ff7051f 100644
--- a/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
+++ b/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
@@ -21,6 +21,8 @@
#include <mutex>
#include <string>
+#include <inttypes.h>
+
#include <utils/RefBase.h>
namespace android {
@@ -31,6 +33,12 @@
public:
CodecProperties(std::string name, std::string mediaType);
+ // seed the codec with some preconfigured values
+ // (e.g. mediaType-granularity defaults)
+ // runs from the constructor
+ void Seed();
+ void Finish();
+
std::string getName();
std::string getMediaType();
@@ -46,8 +54,13 @@
// and 'reverse' describes which strings are to be on which side.
const char **getMappings(std::string kind, bool reverse);
- // debugging of what's in the mapping dictionary
- void showMappings();
+ // keep a map of all features and their parameters
+ void setFeatureValue(std::string key, int32_t value);
+ bool getFeatureValue(std::string key, int32_t *valuep);
+
+ // keep a map of all tunings and their parameters
+ void setTuningValue(std::string key, std::string value);
+ bool getTuningValue(std::string key, std::string &value);
// does the codec support the Android S minimum quality rules
void setSupportedMinimumQuality(int vmaf);
@@ -62,7 +75,7 @@
// This is used to calculate a minimum bitrate for any particular resolution.
// A 1080p (1920*1080 = 2073600 pixels) to be encoded at 5Mbps has a bpp == 2.41
void setBpp(double bpp) { mBpp = bpp;}
- double getBpp() {return mBpp;}
+ double getBpp(int32_t width, int32_t height);
// Does this codec support QP bounding
// The getMapping() methods provide any needed mapping to non-standard keys.
@@ -81,17 +94,37 @@
std::string mMediaType;
int mApi = 0;
int mMinimumQuality = 0;
- int mTargetQpMax = 0;
+ int mTargetQpMax = INT32_MAX;
bool mSupportsQp = false;
double mBpp = 0.0;
+ // allow different target bits-per-pixel based on resolution
+ // similar to codec 'performance points'
+ // uses 'next largest' (by pixel count) point as minimum bpp
+ struct bpp_point {
+ struct bpp_point *next;
+ int32_t pixels;
+ int32_t width, height;
+ double bpp;
+ };
+ struct bpp_point *mBppPoints = nullptr;
+ bool bppPoint(std::string resolution, std::string value);
+
std::mutex mMappingLock;
// XXX figure out why I'm having problems getting compiler to like GUARDED_BY
std::map<std::string, std::string> mMappings /*GUARDED_BY(mMappingLock)*/ ;
- std::map<std::string, std::string> mUnMappings /*GUARDED_BY(mMappingLock)*/ ;
+
+ std::map<std::string, int32_t> mFeatures /*GUARDED_BY(mMappingLock)*/ ;
+ std::map<std::string, std::string> mTunings /*GUARDED_BY(mMappingLock)*/ ;
+
+ // Seed() and Finish() use this as the underlying implementation
+ void addMediaDefaults(bool overrideable);
bool mIsRegistered = false;
+ // debugging of what's in the mapping dictionary
+ void showMappings();
+
// DISALLOW_EVIL_CONSTRUCTORS(CodecProperties);
};
diff --git a/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h b/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h
index 8ad81cd..a1747cc 100644
--- a/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h
+++ b/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h
@@ -84,6 +84,12 @@
typedef int (*setFeature_t)(shaperHandle_t shaper, const char *feature, int value);
/*
+ * establishes that codec "codecName" encoding for "mediaType" supports the indicated
+ * tuning at the indicated value
+ */
+typedef int (*setTuning_t)(shaperHandle_t shaper, const char *feature, const char * value);
+
+/*
* The expectation is that the client will implement a flow similar to the following when
* setting up an encoding.
*
@@ -118,6 +124,10 @@
shapeFormat_t shapeFormat;
getMappings_t getMappings;
getMappings_t getReverseMappings;
+
+ setTuning_t setTuning;
+
+ // additions happen at the end of the structure
} FormatShaperOps_t;
// versioninf information
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index d250976..287317d 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -38,6 +38,7 @@
"media_permission-aidl-cpp",
"libaudioclient_aidl_conversion",
"libbase",
+ "libactivitymanager_aidl",
"libandroid_net",
"libaudioclient",
"libbinder",
diff --git a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
index 7bee002..af9cf45 100644
--- a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
+++ b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
@@ -62,7 +62,7 @@
binder::Status status = mPowerManager->acquireWakeLock(
binder, POWERMANAGER_PARTIAL_WAKE_LOCK,
String16("AWakeLock"), String16("media"),
- {} /* workSource */, {} /* historyTag */);
+ {} /* workSource */, {} /* historyTag */, -1 /* displayId */);
IPCThreadState::self()->restoreCallingIdentity(token);
if (status.isOk()) {
mWakeLockToken = binder;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 6a8c708..4a65f71 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1962,7 +1962,7 @@
ALOGV("Mime \"%s\" mapped to audio_format 0x%x",
mime.c_str(), audioFormat);
- int avgBitRate = -1;
+ int avgBitRate = 0;
format->findInt32("bitrate", &avgBitRate);
int32_t aacProfile = -1;
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index a063565..b19e711 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -366,6 +366,12 @@
return AMEDIA_ERROR_INVALID_OPERATION;
}
+ // Unwrap the callback and send heartbeats to the client after each operation during setup.
+ auto callback = mCallback.lock();
+ if (callback == nullptr) {
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
Status status;
::ndk::ScopedFileDescriptor srcFd, dstFd;
int srcFdInt = request.sourceFd.get();
@@ -379,6 +385,8 @@
srcFdInt = srcFd.get();
}
+ callback->onHeartBeat(clientId, sessionId);
+
int dstFdInt = request.destinationFd.get();
if (dstFdInt < 0) {
// Open dest file with "rw", as the transcoder could potentially reuse part of it
@@ -393,6 +401,8 @@
dstFdInt = dstFd.get();
}
+ callback->onHeartBeat(clientId, sessionId);
+
mCurrentClientId = clientId;
mCurrentSessionId = sessionId;
mCurrentCallingUid = callingUid;
@@ -405,6 +415,8 @@
return AMEDIA_ERROR_UNKNOWN;
}
+ callback->onHeartBeat(clientId, sessionId);
+
media_status_t err = mTranscoder->configureSource(srcFdInt);
if (err != AMEDIA_OK) {
ALOGE("failed to configure source: %d", err);
@@ -412,6 +424,8 @@
return err;
}
+ callback->onHeartBeat(clientId, sessionId);
+
std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
if (trackFormats.size() == 0) {
ALOGE("failed to get track formats!");
@@ -419,6 +433,8 @@
return AMEDIA_ERROR_MALFORMED;
}
+ callback->onHeartBeat(clientId, sessionId);
+
for (int i = 0; i < trackFormats.size(); ++i) {
std::shared_ptr<AMediaFormat> format;
const char* mime = nullptr;
@@ -437,6 +453,8 @@
*failureReason = TranscodingLogger::SessionEndedReason::CONFIG_TRACK_FAILED;
return err;
}
+
+ callback->onHeartBeat(clientId, sessionId);
}
err = mTranscoder->configureDestination(dstFdInt);
@@ -446,6 +464,8 @@
return err;
}
+ callback->onHeartBeat(clientId, sessionId);
+
return AMEDIA_OK;
}
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 06c5421..6dbcaf9 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -94,6 +94,12 @@
Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/,
bool* /*_aidl_return*/) override;
+ Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/,
+ bool* /*_aidl_return*/) override;
+
+ Status getClientUids(int32_t /*in_sessionId*/,
+ std::optional<std::vector<int32_t>>* /*_aidl_return*/) override;
+
Status unregister() override;
};
@@ -217,6 +223,63 @@
return Status::ok();
}
+Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId,
+ int32_t in_clientUid,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ int32_t callingPid = AIBinder_getCallingPid();
+ int32_t callingUid = AIBinder_getCallingUid();
+
+ // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions.
+ if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) {
+ in_clientUid = callingUid;
+ } else if (in_clientUid < 0) {
+ return Status::ok();
+ } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) {
+ ALOGE("addClientUid rejected (clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientUid, callingUid);
+ return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+ "addClientUid rejected (clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientUid, callingUid);
+ }
+
+ *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid);
+ return Status::ok();
+}
+
+Status TranscodingClientManager::ClientImpl::getClientUids(
+ int32_t in_sessionId, std::optional<std::vector<int32_t>>* _aidl_return) {
+ *_aidl_return = std::nullopt;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ std::vector<int32_t> result;
+
+ if (owner->mSessionController->getClientUids(mClientId, in_sessionId, &result)) {
+ *_aidl_return = result;
+ }
+ return Status::ok();
+}
+
Status TranscodingClientManager::ClientImpl::unregister() {
bool abandoned = mAbandoned.exchange(true);
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
index aeabe0f..68e2875 100644
--- a/media/libmediatranscoding/TranscodingSessionController.cpp
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -193,8 +193,9 @@
~Pacer() = default;
- void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
bool onSessionStarted(uid_t uid);
+ void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
+ void onSessionCancelled(uid_t uid);
private:
// Threshold of time between finish/start below which a back-to-back start is counted.
@@ -205,26 +206,20 @@
int32_t mBurstTimeQuotaSec;
struct UidHistoryEntry {
- std::chrono::steady_clock::time_point lastCompletedTime;
+ bool sessionActive = false;
int32_t burstCount = 0;
std::chrono::steady_clock::duration burstDuration{0};
+ std::chrono::steady_clock::time_point lastCompletedTime;
};
std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
};
-void TranscodingSessionController::Pacer::onSessionCompleted(
- uid_t uid, std::chrono::microseconds runningTime) {
+bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
+ // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
mUidHistoryMap.emplace(uid, UidHistoryEntry{});
- }
- mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
- mUidHistoryMap[uid].burstCount++;
- mUidHistoryMap[uid].burstDuration += runningTime;
-}
-
-bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
- // If uid doesn't exist, this uid has no completed sessions. Skip.
- if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+ mUidHistoryMap[uid].sessionActive = true;
+ ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
return true;
}
@@ -236,25 +231,55 @@
std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
- ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid,
- mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count());
+ ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
+ mUidHistoryMap[uid].burstCount,
+ (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
return false;
}
// If not over quota, allow the session, and reset as long as this is not too close
// to previous completion.
if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
- ALOGV("Pacer: uid %d: reset quota", uid);
+ ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
mUidHistoryMap[uid].burstCount = 0;
mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
} else {
- ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount,
- (long long)mUidHistoryMap[uid].burstDuration.count());
+ ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
+ mUidHistoryMap[uid].burstCount,
+ (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
}
+ mUidHistoryMap[uid].sessionActive = true;
return true;
}
+void TranscodingSessionController::Pacer::onSessionCompleted(
+ uid_t uid, std::chrono::microseconds runningTime) {
+ // Skip quota update if this uid missed the start. (Could happen if the uid is added via
+ // addClientUid() after the session start.)
+ if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
+ ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
+ return;
+ }
+ ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
+ mUidHistoryMap[uid].sessionActive = false;
+ mUidHistoryMap[uid].burstCount++;
+ mUidHistoryMap[uid].burstDuration += runningTime;
+ mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
+}
+
+void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
+ if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+ ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
+ return;
+ }
+ // This is only called if a uid is removed from a session (due to it being killed
+ // or the original submitting client was gone but session was kept for offline use).
+ // Since the uid is going to miss the onSessionCompleted(), we can't track this
+ // session, and have to check back at next onSessionStarted().
+ mUidHistoryMap[uid].sessionActive = false;
+}
+
///////////////////////////////////////////////////////////////////////////////
TranscodingSessionController::TranscodingSessionController(
@@ -372,6 +397,14 @@
}
uid_t topUid = *mUidSortedList.begin();
+ // If the current session is running, and it's in the topUid's queue, let it continue
+ // to run even if it's not the earliest in that uid's queue.
+ // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
+ // B is brought to front which caused the session to run, then user switches back to A.
+ if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
+ mCurrentSession->allClientUids.count(topUid) > 0) {
+ return mCurrentSession;
+ }
SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
return &mSessionMap[topSessionKey];
}
@@ -427,7 +460,7 @@
void TranscodingSessionController::updateCurrentSession_l() {
Session* curSession = mCurrentSession;
- Session* topSession = getTopSession_l();
+ Session* topSession = nullptr;
// Delayed init of transcoder and watchdog.
if (mTranscoder == nullptr) {
@@ -458,9 +491,18 @@
// Otherwise, ensure topSession is running.
if (topSession->getState() == Session::NOT_STARTED) {
- if (!mPacer->onSessionStarted(topSession->clientUid)) {
- // Unfortunately this uid is out of quota for new sessions.
- // Drop this sesion and try another one.
+ // Check if at least one client has quota to start the session.
+ bool keepForClient = false;
+ for (uid_t uid : topSession->allClientUids) {
+ if (mPacer->onSessionStarted(uid)) {
+ keepForClient = true;
+ // DO NOT break here, because book-keeping still needs to happen
+ // for the other uids.
+ }
+ }
+ if (!keepForClient) {
+ // Unfortunately all uids requesting this session are out of quota.
+ // Drop this session and try the next one.
{
auto clientCallback = mSessionMap[topSession->key].callback.lock();
if (clientCallback != nullptr) {
@@ -484,8 +526,35 @@
mCurrentSession = topSession;
}
-void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
- Session::State finalState) {
+void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
+ const SessionKeyType& sessionKey) {
+ // If it's an offline session, the queue was already added in constructor.
+ // If it's a real-time sessions, check if a queue is already present for the uid,
+ // and add a new queue if needed.
+ if (clientUid != OFFLINE_UID) {
+ if (mSessionQueues.count(clientUid) == 0) {
+ mUidPolicy->registerMonitorUid(clientUid);
+ if (mUidPolicy->isUidOnTop(clientUid)) {
+ mUidSortedList.push_front(clientUid);
+ } else {
+ // Shouldn't be submitting real-time requests from non-top app,
+ // put it in front of the offline queue.
+ mUidSortedList.insert(mOfflineUidIterator, clientUid);
+ }
+ } else if (clientUid != *mUidSortedList.begin()) {
+ if (mUidPolicy->isUidOnTop(clientUid)) {
+ mUidSortedList.remove(clientUid);
+ mUidSortedList.push_front(clientUid);
+ }
+ }
+ }
+ // Append this session to the uid's queue.
+ mSessionQueues[clientUid].push_back(sessionKey);
+}
+
+void TranscodingSessionController::removeSession_l(
+ const SessionKeyType& sessionKey, Session::State finalState,
+ const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
if (mSessionMap.count(sessionKey) == 0) {
@@ -494,26 +563,48 @@
}
// Remove session from uid's queue.
- const uid_t uid = mSessionMap[sessionKey].clientUid;
- SessionQueueType& sessionQueue = mSessionQueues[uid];
- auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
- if (it == sessionQueue.end()) {
- ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
- uid);
- return;
+ bool uidQueueRemoved = false;
+ std::unordered_set<uid_t> remainingUids;
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ if (keepUid != nullptr) {
+ if ((*keepUid)(uid)) {
+ remainingUids.insert(uid);
+ continue;
+ }
+ // If we have uids to keep, the session is not going to any final
+ // state we can't use onSessionCompleted as the running time will
+ // not be valid. Only notify pacer to stop tracking this session.
+ mPacer->onSessionCancelled(uid);
+ }
+ SessionQueueType& sessionQueue = mSessionQueues[uid];
+ auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
+ if (it == sessionQueue.end()) {
+ ALOGW("couldn't find session %s in queue for uid %d",
+ sessionToString(sessionKey).c_str(), uid);
+ continue;
+ }
+ sessionQueue.erase(it);
+
+ // If this is the last session in a real-time queue, remove this uid's queue.
+ if (uid != OFFLINE_UID && sessionQueue.empty()) {
+ mUidSortedList.remove(uid);
+ mSessionQueues.erase(uid);
+ mUidPolicy->unregisterMonitorUid(uid);
+
+ uidQueueRemoved = true;
+ }
}
- sessionQueue.erase(it);
- // If this is the last session in a real-time queue, remove this uid's queue.
- if (uid != OFFLINE_UID && sessionQueue.empty()) {
- mUidSortedList.remove(uid);
- mSessionQueues.erase(uid);
- mUidPolicy->unregisterMonitorUid(uid);
-
+ if (uidQueueRemoved) {
std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
moveUidsToTop_l(topUids, false /*preserveTopUid*/);
}
+ if (keepUid != nullptr) {
+ mSessionMap[sessionKey].allClientUids = remainingUids;
+ return;
+ }
+
// Clear current session.
if (mCurrentSession == &mSessionMap[sessionKey]) {
mCurrentSession = nullptr;
@@ -521,9 +612,10 @@
setSessionState_l(&mSessionMap[sessionKey], finalState);
- if (finalState == Session::FINISHED || finalState == Session::ERROR) {
- mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid,
- mSessionMap[sessionKey].runningTime);
+ // We can use onSessionCompleted() even for CANCELLED, because runningTime is
+ // now updated by setSessionState_l().
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
}
mSessionHistory.push_back(mSessionMap[sessionKey]);
@@ -617,34 +709,13 @@
// Add session to session map.
mSessionMap[sessionKey].key = sessionKey;
- mSessionMap[sessionKey].clientUid = clientUid;
mSessionMap[sessionKey].callingUid = callingUid;
+ mSessionMap[sessionKey].allClientUids.insert(clientUid);
mSessionMap[sessionKey].request = request;
mSessionMap[sessionKey].callback = callback;
setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
- // If it's an offline session, the queue was already added in constructor.
- // If it's a real-time sessions, check if a queue is already present for the uid,
- // and add a new queue if needed.
- if (clientUid != OFFLINE_UID) {
- if (mSessionQueues.count(clientUid) == 0) {
- mUidPolicy->registerMonitorUid(clientUid);
- if (mUidPolicy->isUidOnTop(clientUid)) {
- mUidSortedList.push_front(clientUid);
- } else {
- // Shouldn't be submitting real-time requests from non-top app,
- // put it in front of the offline queue.
- mUidSortedList.insert(mOfflineUidIterator, clientUid);
- }
- } else if (clientUid != *mUidSortedList.begin()) {
- if (mUidPolicy->isUidOnTop(clientUid)) {
- mUidSortedList.remove(clientUid);
- mUidSortedList.push_front(clientUid);
- }
- }
- }
- // Append this session to the uid's queue.
- mSessionQueues[clientUid].push_back(sessionKey);
+ addUidToSession_l(clientUid, sessionKey);
updateCurrentSession_l();
@@ -657,14 +728,20 @@
ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
- std::list<SessionKeyType> sessionsToRemove;
+ std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
std::scoped_lock lock{mLock};
if (sessionId < 0) {
for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
- if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) {
- sessionsToRemove.push_back(it->first);
+ if (it->first.first == clientId) {
+ // If there is offline request, only keep the offline client;
+ // otherwise remove the session.
+ if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
+ sessionsForOffline.push_back(it->first);
+ } else {
+ sessionsToRemove.push_back(it->first);
+ }
}
}
} else {
@@ -688,6 +765,12 @@
removeSession_l(*it, Session::CANCELED);
}
+ auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+ [](uid_t uid) { return uid == OFFLINE_UID; });
+ for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
+ removeSession_l(*it, Session::CANCELED, keepUid);
+ }
+
// Start next session.
updateCurrentSession_l();
@@ -695,6 +778,51 @@
return true;
}
+bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
+ uid_t clientUid) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
+ ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
+ return false;
+ }
+
+ mSessionMap[sessionKey].allClientUids.insert(clientUid);
+ addUidToSession_l(clientUid, sessionKey);
+
+ updateCurrentSession_l();
+
+ validateState_l();
+ return true;
+}
+
+bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ out_clientUids->clear();
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ if (uid != OFFLINE_UID) {
+ out_clientUids->push_back(uid);
+ }
+ }
+ return true;
+}
+
bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
TranscodingRequestParcel* request) {
SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
@@ -886,6 +1014,58 @@
validateState_l();
}
+void TranscodingSessionController::onUidGone(uid_t goneUid) {
+ ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
+
+ std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
+
+ std::scoped_lock lock{mLock};
+
+ for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
+ if (it->second.allClientUids.count(goneUid) > 0) {
+ // If goneUid is the only uid, remove the session; otherwise, only
+ // remove the uid from the session.
+ if (it->second.allClientUids.size() > 1) {
+ sessionsForOtherUids.push_back(it->first);
+ } else {
+ sessionsToRemove.push_back(it->first);
+ }
+ }
+ }
+
+ for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
+ // If the session has ever been started, stop it now.
+ // Note that stop() is needed even if the session is currently paused. This instructs
+ // the transcoder to discard any states for the session, otherwise the states may
+ // never be discarded.
+ if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
+ mTranscoder->stop(it->first, it->second);
+ }
+
+ {
+ auto clientCallback = mSessionMap[*it].callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFailed(it->second,
+ TranscodingErrorCode::kUidGoneCancelled);
+ }
+ }
+
+ // Remove the session.
+ removeSession_l(*it, Session::CANCELED);
+ }
+
+ auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+ [goneUid](uid_t uid) { return uid != goneUid; });
+ for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
+ removeSession_l(*it, Session::CANCELED, keepUid);
+ }
+
+ // Start next session.
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
void TranscodingSessionController::onResourceAvailable() {
std::scoped_lock lock{mLock};
@@ -938,7 +1118,8 @@
LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
"mOfflineUidIterator not pointing to offline uid");
LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
- "mUidList and mSessionQueues size mismatch");
+ "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
+ mUidSortedList.size(), mSessionQueues.size());
int32_t totalSessions = 0;
for (auto uid : mUidSortedList) {
@@ -952,8 +1133,14 @@
totalSessions += mSessionQueues[uid].size();
}
- LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
- "mSessions size doesn't match total sessions counted from uid queues");
+ int32_t totalSessionsAlternative = 0;
+ for (auto const& s : mSessionMap) {
+ totalSessionsAlternative += s.second.allClientUids.size();
+ }
+ LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
+ "session count (including dup) from mSessionQueues doesn't match that from "
+ "mSessionMap, %d vs %d",
+ totalSessions, totalSessionsAlternative);
#endif // VALIDATE_STATE
}
diff --git a/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
index b5eb028..0a1ffbc 100644
--- a/media/libmediatranscoding/TranscodingUidPolicy.cpp
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -141,38 +141,34 @@
}
void TranscodingUidPolicy::onUidStateChanged(uid_t uid, int32_t procState) {
- ALOGV("onUidStateChanged: %u, procState %d", uid, procState);
+ ALOGV("onUidStateChanged: uid %u, procState %d", uid, procState);
bool topUidSetChanged = false;
+ bool isUidGone = false;
std::unordered_set<uid_t> topUids;
{
Mutex::Autolock _l(mUidLock);
auto it = mUidStateMap.find(uid);
if (it != mUidStateMap.end() && it->second != procState) {
- // Top set changed if 1) the uid is in the current top uid set, or 2) the
- // new procState is at least the same priority as the current top uid state.
- bool isUidCurrentTop =
- mTopUidState != IMPORTANCE_UNKNOWN && mStateUidMap[mTopUidState].count(uid) > 0;
- bool isNewStateHigherThanTop =
- procState != IMPORTANCE_UNKNOWN &&
- (procState <= mTopUidState || mTopUidState == IMPORTANCE_UNKNOWN);
- topUidSetChanged = (isUidCurrentTop || isNewStateHigherThanTop);
+ isUidGone = (procState == AACTIVITYMANAGER_IMPORTANCE_GONE);
+
+ topUids = mStateUidMap[mTopUidState];
// Move uid to the new procState.
mStateUidMap[it->second].erase(uid);
mStateUidMap[procState].insert(uid);
it->second = procState;
- if (topUidSetChanged) {
- updateTopUid_l();
-
+ updateTopUid_l();
+ if (topUids != mStateUidMap[mTopUidState]) {
// Make a copy of the uid set for callback.
topUids = mStateUidMap[mTopUidState];
+ topUidSetChanged = true;
}
}
}
- ALOGV("topUidSetChanged: %d", topUidSetChanged);
+ ALOGV("topUidSetChanged: %d, isUidGone %d", topUidSetChanged, isUidGone);
if (topUidSetChanged) {
auto callback = mUidPolicyCallback.lock();
@@ -180,6 +176,12 @@
callback->onTopUidsChanged(topUids);
}
}
+ if (isUidGone) {
+ auto callback = mUidPolicyCallback.lock();
+ if (callback != nullptr) {
+ callback->onUidGone(uid);
+ }
+ }
}
void TranscodingUidPolicy::updateTopUid_l() {
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
index 151e3d0..9ef9052 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
@@ -55,6 +55,32 @@
boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session);
/**
+ * Add an additional client uid requesting a session.
+ *
+ * @sessionId the session id to which to add the additional client uid.
+ * @clientUid the additional client uid to be added.
+ * @return false if the session doesn't exist or the client is already requesting the
+ * session, true otherwise.
+ */
+ boolean addClientUid(in int sessionId, int clientUid);
+
+ /**
+ * Retrieves the (unsorted) list of all clients requesting a session.
+ *
+ * Note that if a session was submitted with offline priority (
+ * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
+ * by any particular client, because the client could go away any time after the submission.
+ * However, additional uids could be added via addClientUid() after the submission, which
+ * essentially make the request a real-time request instead of an offline request.
+ *
+ * @sessionId the session id for which to retrieve the client uid list.
+ * @clientUids array to hold the retrieved client uid list.
+ * @return false if the session doesn't exist, true otherwise.
+ */
+ @nullable
+ int[] getClientUids(in int sessionId);
+
+ /**
* Unregister the client with the MediaTranscodingService.
*
* Client will not be able to perform any more transcoding after unregister.
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
index 5349fe1..fdd86c7 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
@@ -38,4 +38,5 @@
kErrorIO = kPrivateErrorFirst + 5,
kInsufficientResources = kPrivateErrorFirst + 6,
kWatchdogTimeout = kPrivateErrorFirst + 7,
+ kUidGoneCancelled = kPrivateErrorFirst + 8,
}
\ No newline at end of file
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
index 0d13607..9311e2e 100644
--- a/media/libmediatranscoding/include/media/ControllerClientInterface.h
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -60,6 +60,29 @@
virtual bool getSession(ClientIdType clientId, SessionIdType sessionId,
TranscodingRequestParcel* request) = 0;
+ /**
+ * Add an additional client uid requesting the session identified by <clientId, sessionId>.
+ *
+ * Returns false if the session doesn't exist, or the client is already requesting the
+ * session. Returns true otherwise.
+ */
+ virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid);
+
+ /**
+ * Retrieves the (unsorted) list of all clients requesting the session identified by
+ * <clientId, sessionId>.
+ *
+ * Note that if a session was submitted with offline priority (
+ * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
+ * by any particular client, because the client could go away any time after the submission.
+ * However, additional uids could be added via addClientUid() after the submission, which
+ * essentially make the request a real-time request instead of an offline request.
+ *
+ * Returns false if the session doesn't exist. Returns true otherwise.
+ */
+ virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids);
+
protected:
virtual ~ControllerClientInterface() = default;
};
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
index b2d6f0a..2691201 100644
--- a/media/libmediatranscoding/include/media/TranscodingSessionController.h
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -54,6 +54,9 @@
bool cancel(ClientIdType clientId, SessionIdType sessionId) override;
bool getSession(ClientIdType clientId, SessionIdType sessionId,
TranscodingRequestParcel* request) override;
+ bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override;
+ bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) override;
// ~ControllerClientInterface
// TranscoderCallbackInterface
@@ -70,6 +73,7 @@
// UidPolicyCallbackInterface
void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
+ void onUidGone(uid_t goneUid) override;
// ~UidPolicyCallbackInterface
// ResourcePolicyCallbackInterface
@@ -120,8 +124,8 @@
DROPPED_BY_PACER,
};
SessionKeyType key;
- uid_t clientUid;
uid_t callingUid;
+ std::unordered_set<uid_t> allClientUids;
int32_t lastProgress = 0;
int32_t pauseCount = 0;
std::chrono::time_point<std::chrono::steady_clock> stateEnterTime;
@@ -184,7 +188,9 @@
void dumpSession_l(const Session& session, String8& result, bool closedSession = false);
Session* getTopSession_l();
void updateCurrentSession_l();
- void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState);
+ void addUidToSession_l(uid_t uid, const SessionKeyType& sessionKey);
+ void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState,
+ const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid = nullptr);
void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid);
void setSessionState_l(Session* session, Session::State state);
void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason,
diff --git a/media/libmediatranscoding/include/media/UidPolicyInterface.h b/media/libmediatranscoding/include/media/UidPolicyInterface.h
index 05d8db0..445a2ff 100644
--- a/media/libmediatranscoding/include/media/UidPolicyInterface.h
+++ b/media/libmediatranscoding/include/media/UidPolicyInterface.h
@@ -48,6 +48,9 @@
// has changed. The receiver of this callback should adjust accordingly.
virtual void onTopUidsChanged(const std::unordered_set<uid_t>& uids) = 0;
+ // Called when a uid is gone.
+ virtual void onUidGone(uid_t goneUid) = 0;
+
protected:
virtual ~UidPolicyCallbackInterface() = default;
};
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index 57a2e27..9233410 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -50,6 +50,7 @@
constexpr const char* kClientName = "TestClientName";
constexpr const char* kClientPackage = "TestClientPackage";
+constexpr uid_t OFFLINE_UID = -1;
#define SESSION(n) (n)
@@ -135,8 +136,8 @@
virtual ~TestController() { ALOGI("TestController Destroyed"); }
- bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/, uid_t /*uid*/,
- const TranscodingRequestParcel& request,
+ bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/,
+ uid_t clientUid, const TranscodingRequestParcel& request,
const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override {
SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
if (mSessions.count(sessionKey) > 0) {
@@ -149,13 +150,47 @@
return false;
}
+ if (request.priority == TranscodingSessionPriority::kUnspecified) {
+ clientUid = OFFLINE_UID;
+ }
+
mSessions[sessionKey].request = request;
mSessions[sessionKey].callback = clientCallback;
+ mSessions[sessionKey].allClientUids.insert(clientUid);
mLastSession = sessionKey;
return true;
}
+ bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ if (mSessions[sessionKey].allClientUids.count(clientUid) > 0) {
+ return false;
+ }
+ mSessions[sessionKey].allClientUids.insert(clientUid);
+ return true;
+ }
+
+ bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ out_clientUids->clear();
+ for (uid_t uid : mSessions[sessionKey].allClientUids) {
+ if (uid != OFFLINE_UID) {
+ out_clientUids->push_back(uid);
+ }
+ }
+ return true;
+ }
+
bool cancel(ClientIdType clientId, SessionIdType sessionId) override {
SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
@@ -211,6 +246,7 @@
struct Session {
TranscodingRequest request;
std::weak_ptr<ITranscodingClientCallback> callback;
+ std::unordered_set<uid_t> allClientUids;
};
typedef std::pair<ClientIdType, SessionIdType> SessionKeyType;
@@ -537,4 +573,93 @@
EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
}
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUidsInvalidArgs) {
+ addMultipleClients();
+
+ bool result;
+ std::optional<std::vector<int32_t>> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Add/Get clients with invalid session id fails.
+ EXPECT_TRUE(mClient1->addClientUid(-1, ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->getClientUids(-1, &clientUids).isOk());
+ EXPECT_EQ(clientUids, std::nullopt);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids).isOk());
+ EXPECT_EQ(clientUids, std::nullopt);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUids) {
+ addMultipleClients();
+
+ bool result;
+ std::optional<std::vector<int32_t>> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ request.sourceFilePath = "test_source_file_0";
+ request.destinationFilePath = "test_desintaion_file_0";
+ request.priority = TranscodingSessionPriority::kNormal;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 1);
+ EXPECT_EQ((*clientUids)[0], ownUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), kInvalidClientUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Adding own uid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Submit one offline session.
+ request.sourceFilePath = "test_source_file_1";
+ request.destinationFilePath = "test_desintaion_file_1";
+ request.priority = TranscodingSessionPriority::kUnspecified;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should not have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 0);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID) again, should succeed.
+ EXPECT_TRUE(
+ mClient1->addClientUid(SESSION(1), IMediaTranscodingService::USE_CALLING_UID, &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 1);
+ EXPECT_EQ((*clientUids)[0], ownUid);
+
+ // Add more uids, should succeed.
+ int32_t kFakeUid = ::getuid() ^ 0x1;
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(1), kFakeUid, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids->begin(), clientUids->end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+ EXPECT_EQ(uidSet.count(kFakeUid), 1);
+
+ unregisterMultipleClients();
+}
+
} // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
index 560d1fe..9e7fa95 100644
--- a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -234,11 +234,14 @@
}
struct TestClientCallback : public BnTranscodingClientCallback {
- TestClientCallback(TestTranscoder* owner, int64_t clientId)
- : mOwner(owner), mClientId(clientId) {
+ TestClientCallback(TestTranscoder* owner, ClientIdType clientId, uid_t clientUid)
+ : mOwner(owner), mClientId(clientId), mClientUid(clientUid) {
ALOGD("TestClient Created");
}
+ ClientIdType clientId() const { return mClientId; }
+ uid_t clientUid() const { return mClientUid; }
+
Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
return Status::ok();
@@ -277,7 +280,8 @@
private:
TestTranscoder* mOwner;
- int64_t mClientId;
+ ClientIdType mClientId;
+ uid_t mClientUid;
TestClientCallback(const TestClientCallback&) = delete;
TestClientCallback& operator=(const TestClientCallback&) = delete;
};
@@ -313,14 +317,14 @@
// Set priority only, ignore other fields for now.
mOfflineRequest.priority = TranscodingSessionPriority::kUnspecified;
mRealtimeRequest.priority = TranscodingSessionPriority::kHigh;
- mClientCallback0 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(0));
- mClientCallback1 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(1));
- mClientCallback2 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(2));
- mClientCallback3 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(3));
+ mClientCallback0 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(0), UID(0));
+ mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(1), UID(1));
+ mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(2), UID(2));
+ mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(3), UID(3));
}
void TearDown() override { ALOGI("TranscodingSessionControllerTest tear down"); }
@@ -337,34 +341,51 @@
void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
bool pauseLastSuccessSession = false) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {},
+ pauseLastSuccessSession);
+ }
+
+ void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
+ const std::shared_ptr<TestClientCallback>& client,
+ const std::vector<int>& additionalClientUids,
+ bool pauseLastSuccessSession) {
for (int i = 0; i < numSubmits; i++) {
- mController->submit(CLIENT(0), SESSION(i), UID(0), UID(0),
- mRealtimeRequest, mClientCallback0);
+ mController->submit(client->clientId(), SESSION(i), client->clientUid(),
+ client->clientUid(), mRealtimeRequest, client);
+ for (int additionalUid : additionalClientUids) {
+ mController->addClientUid(client->clientId(), SESSION(i), additionalUid);
+ }
}
for (int i = 0; i < expectedSuccess; i++) {
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Start(client->clientId(), SESSION(i)));
if ((i == expectedSuccess - 1) && pauseLastSuccessSession) {
// Insert a pause of 3 sec to the last success running session
mController->onThrottlingStarted();
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Pause(client->clientId(), SESSION(i)));
sleep(3);
mController->onThrottlingStopped();
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Resume(client->clientId(), SESSION(i)));
}
usleep(sessionDurationMs * 1000);
// Test half of Finish and half of Error, both should be counted as burst runs.
if (i & 1) {
- mController->onFinish(CLIENT(0), SESSION(i));
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(i)));
+ mController->onFinish(client->clientId(), SESSION(i));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Finished(client->clientId(), SESSION(i)));
} else {
- mController->onError(CLIENT(0), SESSION(i), TranscodingErrorCode::kUnknown);
+ mController->onError(client->clientId(), SESSION(i),
+ TranscodingErrorCode::kUnknown);
EXPECT_EQ(mTranscoder->popEvent(100000),
- TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
}
}
for (int i = expectedSuccess; i < numSubmits; i++) {
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService);
}
}
@@ -470,6 +491,83 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(3)));
}
+TEST_F(TranscodingSessionControllerTest, TestCancelSessionWithMultipleUids) {
+ ALOGD("TestCancelSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+
+ // Add UID(1) to the offline SESSION(2), SESSION(2) should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+ // Add UID(1) to SESSION(1) as well.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Cancel SESSION(2), should be cancelled and SESSION(1) should start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Cancel SESSION(1), should be cancelled and SESSION(0) should resume.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelAllSessionsForClient) {
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ std::vector<int32_t> clientUids;
+ // Make some more uids blocked on the sessions.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+
+ // Cancel all sessions for CLIENT(0) with -1.
+ // Expect SESSION(0) and SESSION(1) to be gone.
+ // Expect SESSION(2) still there with empty client uid list (only kept for offline) and start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), -1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+}
+
TEST_F(TranscodingSessionControllerTest, TestFinishSession) {
ALOGD("TestFinishSession");
@@ -527,6 +625,45 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
}
+TEST_F(TranscodingSessionControllerTest, TestFinishSessionWithMultipleUids) {
+ ALOGD("TestFinishSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Finish SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onFinish(CLIENT(0), SESSION(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Finish SESSION(2), SESSION(0) should resume.
+ mController->onFinish(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
TEST_F(TranscodingSessionControllerTest, TestFailSession) {
ALOGD("TestFailSession");
@@ -588,6 +725,49 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
}
+TEST_F(TranscodingSessionControllerTest, TestFailSessionWithMultipleUids) {
+ ALOGD("TestFailSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Add UID(1) and UID(2) to SESSION(2).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // Fail SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Fail SESSION(2), SESSION(0) should resume.
+ mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
TEST_F(TranscodingSessionControllerTest, TestTopUidChanged) {
ALOGD("TestTopUidChanged");
@@ -630,8 +810,59 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
}
+TEST_F(TranscodingSessionControllerTest, TestTopUidChangedMultipleUids) {
+ ALOGD("TestTopUidChangedMultipleUids");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Add UID(1) to SESSION(0), SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(0) back to top, SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(2) to top.
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Add UID(2) to the offline session, it should be started.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+ // ADD UID(3) to SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Bring UID(3) to top, SESSION(0) should resume.
+ mUidPolicy->setTop(UID(3));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(1), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Now make UID(2) also blocked on CLIENT(0), SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(2)));
+
+ // Bring UID(2) back to top, CLIENT(0), SESSION(0) should continue to run (even if it's
+ // added to UID(2)'s queue later than CLIENT(1)'s SESSION(0)).
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
TEST_F(TranscodingSessionControllerTest, TestTopUidSetChanged) {
- ALOGD("TestTopUidChanged_MultipleUids");
+ ALOGD("TestTopUidSetChanged");
// Start with unspecified top UID.
// Submit real-time session to CLIENT(0), session should start immediately.
@@ -684,6 +915,100 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
}
+TEST_F(TranscodingSessionControllerTest, TestUidGone) {
+ ALOGD("TestUidGone");
+
+ mUidPolicy->setTop(UID(0));
+ // Start with unspecified top UID.
+ // Submit real-time sessions to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session to CLIENT(1), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(1)));
+
+ // Tell the controller that UID(0) is gone.
+ mUidPolicy->setTop(UID(1));
+ // CLIENT(0)'s SESSION(1) should start, SESSION(0) should be cancelled.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+ mController->onUidGone(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ std::vector<int32_t> clientUids;
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], UID(1));
+
+ // Tell the controller that UID(1) is gone too.
+ mController->onUidGone(UID(1));
+ // CLIENT(1)'s SESSION(0) should start, CLIENT(0)'s SESSION(1) should be cancelled.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+ // CLIENT(1) SESSION(0) should not have any client uids as it's only kept for offline.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(1), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestAddGetClientUids) {
+ ALOGD("TestAddGetClientUids");
+
+ // Add/get client uids with non-existent session, should fail.
+ std::vector<int32_t> clientUids;
+ uid_t ownUid = ::getuid();
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+
+ // Submit a real-time request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Should have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], UID(0));
+
+ // Add UID(0) again should fail.
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), UID(0)));
+
+ // Add own uid should succeed.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(UID(0)), 1);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+
+ // Submit an offline request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mOfflineRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Should not have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ // Add UID(1) to offline session, offline session should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+}
+
/* Test resource lost without thermal throttling */
TEST_F(TranscodingSessionControllerTest, TestResourceLost) {
ALOGD("TestResourceLost");
@@ -973,4 +1298,24 @@
true /*pauseLastSuccessSession*/);
}
+/*
+ * Test the case where multiple client uids request the same session. Session should only
+ * be dropped when all clients are over quota.
+ */
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerMultipleUids) {
+ ALOGD("TestTranscoderPacerMultipleUids");
+ // First, run mClientCallback0 to the point of no quota.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+ mClientCallback0, {}, false /*pauseLastSuccessSession*/);
+ // Make UID(0) block on Client1's sessions too, Client1's quota should not be affected.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+ mClientCallback1, {UID(0)}, false /*pauseLastSuccessSession*/);
+ // Make UID(10) block on Client2's sessions. We expect to see 11 succeeds (instead of 10),
+ // because the addClientUid() is called after the submit, and first session is already
+ // started by the time UID(10) is added. UID(10) allowed us to run the 11th session,
+ // after that both UID(10) and UID(2) are out of quota.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 11 /*expectedSuccess*/,
+ mClientCallback2, {UID(10)}, false /*pauseLastSuccessSession*/);
+}
+
} // namespace android
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 88c1c42..10b2e80 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -328,8 +328,8 @@
}
lastProgressUpdate = progress;
}
- progressSinceLastReport = true;
}
+ progressSinceLastReport = true;
}
return AMEDIA_OK;
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index 413f049..879241e 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -158,6 +158,11 @@
return;
}
+ // The sample writer is not yet started so notify the caller that progress is still made.
+ if (mHeartBeatIntervalUs > 0) {
+ mCallbacks->onHeartBeat(this);
+ }
+
MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
mutableTranscoder->setSampleConsumer(consumer);
diff --git a/media/libnbaio/AudioStreamInSource.cpp b/media/libnbaio/AudioStreamInSource.cpp
index 1054b68..ca98b28 100644
--- a/media/libnbaio/AudioStreamInSource.cpp
+++ b/media/libnbaio/AudioStreamInSource.cpp
@@ -46,13 +46,11 @@
status_t result;
result = mStream->getBufferSize(&mStreamBufferSizeBytes);
if (result != OK) return result;
- audio_format_t streamFormat;
- uint32_t sampleRate;
- audio_channel_mask_t channelMask;
- result = mStream->getAudioProperties(&sampleRate, &channelMask, &streamFormat);
+ audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+ result = mStream->getAudioProperties(&config);
if (result != OK) return result;
- mFormat = Format_from_SR_C(sampleRate,
- audio_channel_count_from_in_mask(channelMask), streamFormat);
+ mFormat = Format_from_SR_C(config.sample_rate,
+ audio_channel_count_from_in_mask(config.channel_mask), config.format);
mFrameSize = Format_frameSize(mFormat);
}
return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp
index 8564899..581867f 100644
--- a/media/libnbaio/AudioStreamOutSink.cpp
+++ b/media/libnbaio/AudioStreamOutSink.cpp
@@ -44,13 +44,11 @@
status_t result;
result = mStream->getBufferSize(&mStreamBufferSizeBytes);
if (result != OK) return result;
- audio_format_t streamFormat;
- uint32_t sampleRate;
- audio_channel_mask_t channelMask;
- result = mStream->getAudioProperties(&sampleRate, &channelMask, &streamFormat);
+ audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+ result = mStream->getAudioProperties(&config);
if (result != OK) return result;
- mFormat = Format_from_SR_C(sampleRate,
- audio_channel_count_from_out_mask(channelMask), streamFormat);
+ mFormat = Format_from_SR_C(config.sample_rate,
+ audio_channel_count_from_out_mask(config.channel_mask), config.format);
mFrameSize = Format_frameSize(mFormat);
}
return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers);
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 52434b3..d6e36b9 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -274,6 +274,7 @@
"MPEG2TSWriter.cpp",
"MPEG4Writer.cpp",
"MediaAdapter.cpp",
+ "MediaAppender.cpp",
"MediaClock.cpp",
"MediaCodec.cpp",
"MediaCodecList.cpp",
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 76a5cab..5c39239 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -519,12 +519,12 @@
mSendNotify = false;
mWriteSeekErr = false;
mFallocateErr = false;
-
// Reset following variables for all the sessions and they will be
// initialized in start(MetaData *param).
mIsRealTimeRecording = true;
mUse4ByteNalLength = true;
mOffset = 0;
+ mMaxOffsetAppend = 0;
mPreAllocateFileEndOffset = 0;
mMdatOffset = 0;
mMdatEndOffset = 0;
@@ -992,6 +992,19 @@
seekOrPostError(mFd, mFreeBoxOffset, SEEK_SET);
writeInt32(mInMemoryCacheSize);
write("free", 4);
+ if (mInMemoryCacheSize >= 8) {
+ off64_t bufSize = mInMemoryCacheSize - 8;
+ char* zeroBuffer = new (std::nothrow) char[bufSize];
+ if (zeroBuffer) {
+ std::fill_n(zeroBuffer, bufSize, '0');
+ writeOrPostError(mFd, zeroBuffer, bufSize);
+ delete [] zeroBuffer;
+ } else {
+ ALOGW("freebox in file isn't initialized to 0");
+ }
+ } else {
+ ALOGW("freebox size is less than 8:%" PRId64, mInMemoryCacheSize);
+ }
mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
} else {
mMdatOffset = mOffset;
@@ -1541,6 +1554,26 @@
MediaBuffer *buffer, bool usePrefix,
uint32_t tiffHdrOffset, size_t *bytesWritten) {
off64_t old_offset = mOffset;
+ int64_t offset;
+ ALOGV("buffer->range_length:%lld", (long long)buffer->range_length());
+ if (buffer->meta_data().findInt64(kKeySampleFileOffset, &offset)) {
+ ALOGV("offset:%lld, old_offset:%lld", (long long)offset, (long long)old_offset);
+ if (old_offset == offset) {
+ mOffset += buffer->range_length();
+ } else {
+ ALOGV("offset and old_offset are not equal! diff:%lld", (long long)offset - old_offset);
+ mOffset = offset + buffer->range_length();
+ // mOffset += buffer->range_length() + offset - old_offset;
+ }
+ *bytesWritten = buffer->range_length();
+ ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld, bytesWritten:%lld", (long long)mOffset,
+ (long long)mMaxOffsetAppend, (long long)*bytesWritten);
+ mMaxOffsetAppend = std::max(mOffset, mMaxOffsetAppend);
+ seekOrPostError(mFd, mMaxOffsetAppend, SEEK_SET);
+ return offset;
+ }
+
+ ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset, (long long)mMaxOffsetAppend);
if (usePrefix) {
addMultipleLengthPrefixedSamples_l(buffer);
@@ -1557,6 +1590,10 @@
mOffset += buffer->range_length();
}
*bytesWritten = mOffset - old_offset;
+
+ ALOGV("mOffset:%lld, old_offset:%lld, bytesWritten:%lld", (long long)mOffset,
+ (long long)old_offset, (long long)*bytesWritten);
+
return old_offset;
}
@@ -1569,6 +1606,7 @@
(const uint8_t *)buffer->data() + buffer->range_offset();
if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+ ALOGV("stripping start code");
buffer->set_range(
buffer->range_offset() + 4, buffer->range_length() - 4);
}
@@ -1599,8 +1637,10 @@
}
void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
+ ALOGV("alp:buffer->range_length:%lld", (long long)buffer->range_length());
size_t length = buffer->range_length();
if (mUse4ByteNalLength) {
+ ALOGV("mUse4ByteNalLength");
uint8_t x[4];
x[0] = length >> 24;
x[1] = (length >> 16) & 0xff;
@@ -1610,6 +1650,7 @@
writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
mOffset += length + 4;
} else {
+ ALOGV("mUse2ByteNalLength");
CHECK_LT(length, 65536u);
uint8_t x[2];
@@ -2762,6 +2803,9 @@
}
writeAllChunks();
+ ALOGV("threadFunc mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset,
+ (long long)mMaxOffsetAppend);
+ mOffset = std::max(mOffset, mMaxOffsetAppend);
}
status_t MPEG4Writer::startWriterThread() {
@@ -3323,6 +3367,7 @@
uint32_t lastSamplesPerChunk = 0;
int64_t lastSampleDurationUs = -1; // Duration calculated from EOS buffer and its timestamp
int64_t lastSampleDurationTicks = -1; // Timescale based ticks
+ int64_t sampleFileOffset = -1;
if (mIsAudio) {
prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0);
@@ -3342,6 +3387,7 @@
MediaBufferBase *buffer;
const char *trackName = getTrackType();
while (!mDone && (err = mSource->read(&buffer)) == OK) {
+ ALOGV("read:buffer->range_length:%lld", (long long)buffer->range_length());
int32_t isEOS = false;
if (buffer->range_length() == 0) {
if (buffer->meta_data().findInt32(kKeyIsEndOfStream, &isEOS) && isEOS) {
@@ -3448,6 +3494,14 @@
continue;
}
}
+ if (!buffer->meta_data().findInt64(kKeySampleFileOffset, &sampleFileOffset)) {
+ sampleFileOffset = -1;
+ }
+ int64_t lastSample = -1;
+ if (!buffer->meta_data().findInt64(kKeyLastSampleIndexInChunk, &lastSample)) {
+ lastSample = -1;
+ }
+ ALOGV("sampleFileOffset:%lld", (long long)sampleFileOffset);
/*
* Reserve space in the file for the current sample + to be written MOOV box. If reservation
@@ -3455,7 +3509,7 @@
* write MOOV box successfully as space for the same was reserved in the prior call.
* Release the current buffer/sample here.
*/
- if (!mOwner->preAllocate(buffer->range_length())) {
+ if (sampleFileOffset == -1 && !mOwner->preAllocate(buffer->range_length())) {
buffer->release();
buffer = nullptr;
break;
@@ -3466,9 +3520,14 @@
// Make a deep copy of the MediaBuffer and Metadata and release
// the original as soon as we can
MediaBuffer *copy = new MediaBuffer(buffer->range_length());
- memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ if (sampleFileOffset != -1) {
+ copy->meta_data().setInt64(kKeySampleFileOffset, sampleFileOffset);
+ } else {
+ memcpy(copy->data(), (uint8_t*)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+ }
copy->set_range(0, buffer->range_length());
+
meta_data = new MetaData(buffer->meta_data());
buffer->release();
buffer = NULL;
@@ -3476,14 +3535,16 @@
copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
}
bool usePrefix = this->usePrefix() && !isExif;
-
- if (usePrefix) StripStartcode(copy);
-
+ if (sampleFileOffset == -1 && usePrefix) {
+ StripStartcode(copy);
+ }
size_t sampleSize = copy->range_length();
- if (usePrefix) {
+ if (sampleFileOffset == -1 && usePrefix) {
if (mOwner->useNalLengthFour()) {
+ ALOGV("nallength4");
sampleSize += 4;
} else {
+ ALOGV("nallength2");
sampleSize += 2;
}
}
@@ -3778,7 +3839,8 @@
chunkTimestampUs = timestampUs;
} else {
int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
- if (chunkDurationUs > interleaveDurationUs) {
+ if (chunkDurationUs > interleaveDurationUs || lastSample > 1) {
+ ALOGV("lastSample:%lld", (long long)lastSample);
if (chunkDurationUs > mMaxChunkDurationUs) {
mMaxChunkDurationUs = chunkDurationUs;
}
@@ -5331,4 +5393,4 @@
endBox();
}
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/media/libstagefright/MediaAppender.cpp b/media/libstagefright/MediaAppender.cpp
new file mode 100644
index 0000000..5d80b30
--- /dev/null
+++ b/media/libstagefright/MediaAppender.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2020 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 "MediaAppender"
+
+#include <media/stagefright/MediaAppender.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/Log.h>
+// TODO : check if this works for NDK apps without JVM
+// #include <media/ndk/NdkJavaVMHelperPriv.h>
+
+namespace android {
+
+struct MediaAppender::sampleDataInfo {
+ size_t size;
+ int64_t time;
+ size_t exTrackIndex;
+ sp<MetaData> meta;
+};
+
+sp<MediaAppender> MediaAppender::create(int fd, AppendMode mode) {
+ if (fd < 0) {
+ ALOGE("invalid file descriptor");
+ return nullptr;
+ }
+ if (!(mode >= APPEND_MODE_FIRST && mode <= APPEND_MODE_LAST)) {
+ ALOGE("invalid mode %d", mode);
+ return nullptr;
+ }
+ sp<MediaAppender> ma = new (std::nothrow) MediaAppender(fd, mode);
+ if (ma->init() != OK) {
+ return nullptr;
+ }
+ return ma;
+}
+
+// TODO: inject mediamuxer and mediaextractor objects.
+// TODO: @format is not required as an input if we can sniff the file and find the format of
+// the existing content.
+// TODO: Code it to the interface(MediaAppender), and have a separate MediaAppender NDK
+MediaAppender::MediaAppender(int fd, AppendMode mode)
+ : mFd(fd),
+ mMode(mode),
+ // TODO : check if this works for NDK apps without JVM
+ // mExtractor(new NuMediaExtractor(NdkJavaVMHelper::getJNIEnv() != nullptr
+ // ? NuMediaExtractor::EntryPoint::NDK_WITH_JVM
+ // : NuMediaExtractor::EntryPoint::NDK_NO_JVM)),
+ mExtractor(new (std::nothrow) NuMediaExtractor(NuMediaExtractor::EntryPoint::NDK_WITH_JVM)),
+ mTrackCount(0),
+ mState(UNINITIALIZED) {
+ ALOGV("MediaAppender::MediaAppender mode:%d", mode);
+ }
+
+status_t MediaAppender::init() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::init");
+ status_t status = mExtractor->setDataSource(mFd, 0, lseek(mFd, 0, SEEK_END));
+ if (status != OK) {
+ ALOGE("extractor_setDataSource failed, status :%d", status);
+ return status;
+ }
+
+ if (strcmp("MPEG4Extractor", mExtractor->getName()) == 0) {
+ mFormat = MediaMuxer::OUTPUT_FORMAT_MPEG_4;
+ } else {
+ ALOGE("Unsupported format, extractor name:%s", mExtractor->getName());
+ return ERROR_UNSUPPORTED;
+ }
+
+ mTrackCount = mExtractor->countTracks();
+ ALOGV("mTrackCount:%zu", mTrackCount);
+ if (mTrackCount == 0) {
+ ALOGE("no tracks are present");
+ return ERROR_MALFORMED;
+ }
+ size_t exTrackIndex = 0;
+ ssize_t audioTrackIndex = -1, videoTrackIndex = -1;
+ bool audioSyncSampleTimeSet = false;
+
+ while (exTrackIndex < mTrackCount) {
+ sp<AMessage> fmt;
+ status = mExtractor->getTrackFormat(exTrackIndex, &fmt, 0);
+ if (status != OK) {
+ ALOGE("getTrackFormat failed for trackIndex:%zu, status:%d", exTrackIndex, status);
+ return status;
+ }
+ AString mime;
+ if (fmt->findString("mime", &mime)) {
+ if (!strncasecmp(mime.c_str(), "video/", 6)) {
+ ALOGV("VideoTrack");
+ if (videoTrackIndex != -1) {
+ ALOGE("Not more than one video track is supported");
+ return ERROR_UNSUPPORTED;
+ }
+ videoTrackIndex = exTrackIndex;
+ } else if (!strncasecmp(mime.c_str(), "audio/", 6)) {
+ ALOGV("AudioTrack");
+ if (audioTrackIndex != -1) {
+ ALOGE("Not more than one audio track is supported");
+ }
+ audioTrackIndex = exTrackIndex;
+ } else {
+ ALOGV("Neither Video nor Audio track");
+ }
+ }
+ mFmtIndexMap.emplace(exTrackIndex, fmt);
+ mSampleCountVect.emplace_back(0);
+ mMaxTimestampVect.emplace_back(0);
+ mLastSyncSampleTimeVect.emplace_back(0);
+ status = mExtractor->selectTrack(exTrackIndex);
+ if (status != OK) {
+ ALOGE("selectTrack failed for trackIndex:%zu, status:%d", exTrackIndex, status);
+ return status;
+ }
+ ++exTrackIndex;
+ }
+
+ ALOGV("AudioTrackIndex:%zu, VideoTrackIndex:%zu", audioTrackIndex, videoTrackIndex);
+
+ do {
+ sampleDataInfo tmpSDI;
+ // TODO: read info into members of the struct sampleDataInfo directly
+ size_t sampleSize;
+ status = mExtractor->getSampleSize(&sampleSize);
+ if (status != OK) {
+ ALOGE("getSampleSize failed, status:%d", status);
+ return status;
+ }
+ mSampleSizeVect.emplace_back(sampleSize);
+ tmpSDI.size = sampleSize;
+ int64_t sampleTime = 0;
+ status = mExtractor->getSampleTime(&sampleTime);
+ if (status != OK) {
+ ALOGE("getSampleTime failed, status:%d", status);
+ return status;
+ }
+ mSampleTimeVect.emplace_back(sampleTime);
+ tmpSDI.time = sampleTime;
+ status = mExtractor->getSampleTrackIndex(&exTrackIndex);
+ if (status != OK) {
+ ALOGE("getSampleTrackIndex failed, status:%d", status);
+ return status;
+ }
+ mSampleIndexVect.emplace_back(exTrackIndex);
+ tmpSDI.exTrackIndex = exTrackIndex;
+ ++mSampleCountVect[exTrackIndex];
+ mMaxTimestampVect[exTrackIndex] = std::max(mMaxTimestampVect[exTrackIndex], sampleTime);
+ sp<MetaData> sampleMeta;
+ status = mExtractor->getSampleMeta(&sampleMeta);
+ if (status != OK) {
+ ALOGE("getSampleMeta failed, status:%d", status);
+ return status;
+ }
+ mSampleMetaVect.emplace_back(sampleMeta);
+ int32_t val = 0;
+ if (sampleMeta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ mLastSyncSampleTimeVect[exTrackIndex] = sampleTime;
+ }
+ tmpSDI.meta = sampleMeta;
+ mSDI.emplace_back(tmpSDI);
+ } while (mExtractor->advance() == OK);
+
+ mExtractor.clear();
+
+ std::sort(mSDI.begin(), mSDI.end(), [](sampleDataInfo& a, sampleDataInfo& b) {
+ int64_t aOffset, bOffset;
+ a.meta->findInt64(kKeySampleFileOffset, &aOffset);
+ b.meta->findInt64(kKeySampleFileOffset, &bOffset);
+ return aOffset < bOffset;
+ });
+ for (int64_t syncSampleTime : mLastSyncSampleTimeVect) {
+ ALOGV("before ignoring frames, mLastSyncSampleTimeVect:%lld", (long long)syncSampleTime);
+ }
+ ALOGV("mMode:%u", mMode);
+ if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex != -1 ) {
+ ALOGV("Video track is present");
+ bool lastVideoIframe = false;
+ size_t lastVideoIframeOffset = 0;
+ int64_t lastVideoSampleTime = -1;
+ for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend(); ++rItr) {
+ if (rItr->exTrackIndex != videoTrackIndex) {
+ continue;
+ }
+ if (lastVideoSampleTime == -1) {
+ lastVideoSampleTime = rItr->time;
+ }
+ int64_t offset = 0;
+ if (!rItr->meta->findInt64(kKeySampleFileOffset, &offset) || offset == 0) {
+ ALOGE("Missing offset");
+ return ERROR_MALFORMED;
+ }
+ ALOGV("offset:%lld", (long long)offset);
+ int32_t val = 0;
+ if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ ALOGV("sampleTime:%lld", (long long)rItr->time);
+ ALOGV("lastVideoSampleTime:%lld", (long long)lastVideoSampleTime);
+ if (lastVideoIframe == false && (lastVideoSampleTime - rItr->time) >
+ 1000000/* Track interleaving duration in MPEG4Writer*/) {
+ ALOGV("lastVideoIframe got chosen");
+ lastVideoIframe = true;
+ mLastSyncSampleTimeVect[videoTrackIndex] = rItr->time;
+ lastVideoIframeOffset = offset;
+ ALOGV("lastVideoIframeOffset:%lld", (long long)offset);
+ break;
+ }
+ }
+ }
+ if (lastVideoIframe == false) {
+ ALOGV("Need to rewrite all samples");
+ mLastSyncSampleTimeVect[videoTrackIndex] = 0;
+ lastVideoIframeOffset = 0;
+ }
+ unsigned int framesIgnoredCount = 0;
+ for (auto itr = mSDI.begin(); itr != mSDI.end();) {
+ int64_t offset = 0;
+ ALOGV("trackIndex:%zu, %" PRId64 "", itr->exTrackIndex, itr->time);
+ if (itr->meta->findInt64(kKeySampleFileOffset, &offset) &&
+ offset >= lastVideoIframeOffset) {
+ ALOGV("offset:%lld", (long long)offset);
+ if (!audioSyncSampleTimeSet && audioTrackIndex != -1 &&
+ audioTrackIndex == itr->exTrackIndex) {
+ mLastSyncSampleTimeVect[audioTrackIndex] = itr->time;
+ audioSyncSampleTimeSet = true;
+ }
+ itr = mSDI.erase(itr);
+ ++framesIgnoredCount;
+ } else {
+ ++itr;
+ }
+ }
+ ALOGV("framesIgnoredCount:%u", framesIgnoredCount);
+ }
+
+ if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex == -1 &&
+ audioTrackIndex != -1) {
+ ALOGV("Only AudioTrack is present");
+ for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend(); ++rItr) {
+ int32_t val = 0;
+ if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ mLastSyncSampleTimeVect[audioTrackIndex] = rItr->time;
+ break;
+ }
+ }
+ unsigned int framesIgnoredCount = 0;
+ for (auto itr = mSDI.begin(); itr != mSDI.end();) {
+ if (itr->time >= mLastSyncSampleTimeVect[audioTrackIndex]) {
+ itr = mSDI.erase(itr);
+ ++framesIgnoredCount;
+ } else {
+ ++itr;
+ }
+ }
+ ALOGV("framesIgnoredCount :%u", framesIgnoredCount);
+ }
+
+ for (size_t i = 0; i < mLastSyncSampleTimeVect.size(); ++i) {
+ ALOGV("mLastSyncSampleTimeVect[%zu]:%lld", i, (long long)mLastSyncSampleTimeVect[i]);
+ mFmtIndexMap[i]->setInt64(
+ "sample-time-before-append" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
+ mLastSyncSampleTimeVect[i]);
+ }
+ for (size_t i = 0; i < mMaxTimestampVect.size(); ++i) {
+ ALOGV("mMaxTimestamp[%zu]:%lld", i, (long long)mMaxTimestampVect[i]);
+ }
+ for (size_t i = 0; i < mSampleCountVect.size(); ++i) {
+ ALOGV("SampleCountVect[%zu]:%zu", i, mSampleCountVect[i]);
+ }
+ mState = INITIALIZED;
+ return OK;
+}
+
+MediaAppender::~MediaAppender() {
+ ALOGV("MediaAppender::~MediaAppender");
+ mMuxer.clear();
+ mExtractor.clear();
+}
+
+status_t MediaAppender::start() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::start");
+ if (mState != INITIALIZED) {
+ ALOGE("MediaAppender::start() is called in invalid state %d", mState);
+ return INVALID_OPERATION;
+ }
+ mMuxer = new (std::nothrow) MediaMuxer(mFd, mFormat);
+ for (const auto& n : mFmtIndexMap) {
+ ssize_t muxIndex = mMuxer->addTrack(n.second);
+ if (muxIndex < 0) {
+ ALOGE("addTrack failed");
+ return UNKNOWN_ERROR;
+ }
+ mTrackIndexMap.emplace(n.first, muxIndex);
+ }
+ ALOGV("trackIndexmap size:%zu", mTrackIndexMap.size());
+
+ status_t status = mMuxer->start();
+ if (status != OK) {
+ ALOGE("muxer start failed:%d", status);
+ return status;
+ }
+
+ ALOGV("Sorting samples based on their offsets");
+ for (int i = 0; i < mSDI.size(); ++i) {
+ ALOGV("i:%d", i + 1);
+ /* TODO : Allocate a single allocation of the max size, and reuse it across ABuffers if
+ * using new ABuffer(void *, size_t).
+ */
+ sp<ABuffer> data = new (std::nothrow) ABuffer(mSDI[i].size);
+ if (data == nullptr) {
+ ALOGE("memory allocation failed");
+ return NO_MEMORY;
+ }
+ data->setRange(0, mSDI[i].size);
+ int32_t val = 0;
+ int sampleFlags = 0;
+ if (mSDI[i].meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ sampleFlags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
+ }
+
+ int64_t val64;
+ if (mSDI[i].meta->findInt64(kKeySampleFileOffset, &val64)) {
+ ALOGV("SampleFileOffset Found :%zu:%lld:%lld", mSDI[i].exTrackIndex,
+ (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
+ sp<AMessage> bufMeta = data->meta();
+ bufMeta->setInt64("sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
+ val64);
+ }
+ if (mSDI[i].meta->findInt64(kKeyLastSampleIndexInChunk, &val64)) {
+ ALOGV("kKeyLastSampleIndexInChunk Found %lld:%lld",
+ (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
+ sp<AMessage> bufMeta = data->meta();
+ bufMeta->setInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ val64);
+ }
+ status = mMuxer->writeSampleData(data, mTrackIndexMap[mSDI[i].exTrackIndex], mSDI[i].time,
+ sampleFlags);
+ if (status != OK) {
+ ALOGE("muxer writeSampleData failed:%d", status);
+ return status;
+ }
+ }
+ mState = STARTED;
+ return OK;
+}
+
+status_t MediaAppender::stop() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::stop");
+ if (mState == STARTED) {
+ status_t status = mMuxer->stop();
+ if (status != OK) {
+ mState = ERROR;
+ } else {
+ mState = STOPPED;
+ }
+ return status;
+ } else {
+ ALOGE("stop() is called in invalid state %d", mState);
+ return INVALID_OPERATION;
+ }
+}
+
+ssize_t MediaAppender::getTrackCount() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::getTrackCount");
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackCount() is called in invalid state %d", mState);
+ return -1;
+ }
+ return mTrackCount;
+}
+
+sp<AMessage> MediaAppender::getTrackFormat(size_t idx) {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::getTrackFormat");
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackFormat() is called in invalid state %d", mState);
+ return nullptr;
+ }
+ if (idx < 0 || idx >= mTrackCount) {
+ ALOGE("getTrackFormat() idx is out of range");
+ return nullptr;
+ }
+ return mFmtIndexMap[idx];
+}
+
+status_t MediaAppender::writeSampleData(const sp<ABuffer>& buffer, size_t trackIndex,
+ int64_t timeUs, uint32_t flags) {
+ std::scoped_lock lock(mMutex);
+ ALOGV("writeSampleData:trackIndex:%zu, time:%" PRId64 "", trackIndex, timeUs);
+ return mMuxer->writeSampleData(buffer, trackIndex, timeUs, flags);
+}
+
+status_t MediaAppender::setOrientationHint([[maybe_unused]] int degrees) {
+ ALOGE("setOrientationHint not supported. Has to be called prior to start on initial muxer");
+ return ERROR_UNSUPPORTED;
+};
+
+status_t MediaAppender::setLocation([[maybe_unused]] int latit, [[maybe_unused]] int longit) {
+ ALOGE("setLocation not supported. Has to be called prior to start on initial muxer");
+ return ERROR_UNSUPPORTED;
+}
+
+ssize_t MediaAppender::addTrack([[maybe_unused]] const sp<AMessage> &format) {
+ ALOGE("addTrack not supported");
+ return ERROR_UNSUPPORTED;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index be21a5d..c21ea8f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -29,6 +29,7 @@
#include <C2Buffer.h>
#include "include/SoftwareRenderer.h"
+#include "PlaybackDurationAccumulator.h"
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
@@ -109,6 +110,7 @@
static const char *kCodecLevel = "android.media.mediacodec.level"; /* 0..n */
static const char *kCodecBitrateMode = "android.media.mediacodec.bitrate_mode"; /* CQ/VBR/CBR */
static const char *kCodecBitrate = "android.media.mediacodec.bitrate"; /* 0..n */
+static const char *kCodecOriginalBitrate = "android.media.mediacodec.original.bitrate"; /* 0..n */
static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth"; /* 0..n */
static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight"; /* 0..n */
static const char *kCodecError = "android.media.mediacodec.errcode";
@@ -138,6 +140,10 @@
static const char *kCodecRecentLatencyAvg = "android.media.mediacodec.recent.avg"; /* in us */
static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n";
static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist"; /* in us */
+static const char *kCodecPlaybackDuration =
+ "android.media.mediacodec.playback-duration"; /* in sec */
+
+static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped"; /* 0/1 */
// XXX suppress until we get our representation right
static bool kEmitHistogram = false;
@@ -716,6 +722,8 @@
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
+ mPlaybackDurationAccumulator(new PlaybackDurationAccumulator()),
+ mIsSurfaceToScreen(false),
mLatencyUnknown(0),
mBytesEncoded(0),
mEarliestEncodedPtsUs(INT64_MAX),
@@ -822,6 +830,10 @@
if (mLatencyUnknown > 0) {
mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
}
+ int64_t playbackDuration = mPlaybackDurationAccumulator->getDurationInSeconds();
+ if (playbackDuration > 0) {
+ mediametrics_setInt64(mMetricsHandle, kCodecPlaybackDuration, playbackDuration);
+ }
if (mLifetimeStartNs > 0) {
nsecs_t lifetime = systemTime(SYSTEM_TIME_MONOTONIC) - mLifetimeStartNs;
lifetime = lifetime / (1000 * 1000); // emitted in ms, truncated not rounded
@@ -959,6 +971,22 @@
ALOGV("Ignoring tunnel-peek=%d for %s", tunnelPeek, asString(mTunnelPeekState));
}
+void MediaCodec::updatePlaybackDuration(const sp<AMessage> &msg) {
+ if (msg->what() != kWhatOutputFramesRendered) {
+ ALOGE("updatePlaybackDuration: expected kWhatOuputFramesRendered (%d)", msg->what());
+ return;
+ }
+ // Playback duration only counts if the buffers are going to the screen.
+ if (!mIsSurfaceToScreen) {
+ return;
+ }
+ int64_t renderTimeNs;
+ size_t index = 0;
+ while (msg->findInt64(AStringPrintf("%zu-system-nano", index++).c_str(), &renderTimeNs)) {
+ mPlaybackDurationAccumulator->processRenderTime(renderTimeNs);
+ }
+}
+
bool MediaCodec::Histogram::setup(int nbuckets, int64_t width, int64_t floor)
{
if (nbuckets <= 0 || width <= 0) {
@@ -1390,6 +1418,12 @@
return msg->post();
}
+/*
+ * MediaFormat Shaping forward declarations
+ * including the property name we use for control.
+ */
+static int enableMediaFormatShapingDefault = 1;
+static const char enableMediaFormatShapingProperty[] = "debug.stagefright.enableshaping";
static void mapFormat(AString componentName, const sp<AMessage> &format, const char *kind,
bool reverse);
@@ -1463,15 +1497,14 @@
}
}
- // apply framework level modifications to the mediaformat for encoding
- // XXX: default off for a while during dogfooding
- static const char *enable_property = "debug.stagefright.enableshaping";
- int8_t enableShaping = property_get_bool(enable_property, 0);
- if (!enableShaping) {
- ALOGD("format shaping disabled via property '%s'", enable_property);
- } else {
- if (flags & CONFIGURE_FLAG_ENCODE) {
+ if (flags & CONFIGURE_FLAG_ENCODE) {
+ int8_t enableShaping = property_get_bool(enableMediaFormatShapingProperty,
+ enableMediaFormatShapingDefault);
+ if (!enableShaping) {
+ ALOGI("format shaping disabled, property '%s'", enableMediaFormatShapingProperty);
+ } else {
(void) shapeMediaFormat(format, flags);
+ // XXX: do we want to do this regardless of shaping enablement?
mapFormat(mComponentName, format, nullptr, false);
}
}
@@ -1552,10 +1585,14 @@
static bool connectFormatShaper() {
static std::once_flag sCheckOnce;
- static void *libHandle = NULL;
+
+ ALOGV("connectFormatShaper...");
std::call_once(sCheckOnce, [&](){
+ void *libHandle = NULL;
+ nsecs_t loading_started = systemTime(SYSTEM_TIME_MONOTONIC);
+
// prefer any copy in the mainline module
//
android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
@@ -1614,44 +1651,33 @@
ALOGV("connectFormatShaper: connected to library %s", libraryName.c_str());
}
+ nsecs_t loading_finished = systemTime(SYSTEM_TIME_MONOTONIC);
+ ALOGV("connectFormatShaper: loaded libraries: %" PRId64 " us",
+ (loading_finished - loading_started)/1000);
+
});
return true;
}
+
+#if 0
// a construct to force the above dlopen() to run very early.
// goal: so the dlopen() doesn't happen on critical path of latency sensitive apps
// failure of this means that cold start of those apps is slower by the time to dlopen()
+// TODO(b/183454066): tradeoffs between memory of early loading vs latency of late loading
//
static bool forceEarlyLoadingShaper = connectFormatShaper();
+#endif
// parse the codec's properties: mapping, whether it meets min quality, etc
// and pass them into the video quality code
//
-status_t MediaCodec::setupFormatShaper(AString mediaType) {
- ALOGV("setupFormatShaper: initializing shaper data for codec %s mediaType %s",
- mComponentName.c_str(), mediaType.c_str());
-
- nsecs_t mapping_started = systemTime(SYSTEM_TIME_MONOTONIC);
-
- // see if the shaper is already present, if so return
- mediaformatshaper::shaperHandle_t shaperHandle;
- shaperHandle = sShaperOps->findShaper(mComponentName.c_str(), mediaType.c_str());
- if (shaperHandle != nullptr) {
- ALOGV("shaperhandle %p -- no initialization needed", shaperHandle);
- return OK;
- }
-
- // not there, so we get to build & register one
- shaperHandle = sShaperOps->createShaper(mComponentName.c_str(), mediaType.c_str());
- if (shaperHandle == nullptr) {
- ALOGW("unable to create a shaper for cocodec %s mediaType %s",
- mComponentName.c_str(), mediaType.c_str());
- return OK;
- }
+static void loadCodecProperties(mediaformatshaper::shaperHandle_t shaperHandle,
+ sp<MediaCodecInfo> codecInfo, AString mediaType) {
sp<MediaCodecInfo::Capabilities> capabilities =
- mCodecInfo->getCapabilitiesFor(mediaType.c_str());
+ codecInfo->getCapabilitiesFor(mediaType.c_str());
if (capabilities == nullptr) {
ALOGI("no capabilities as part of the codec?");
} else {
@@ -1665,6 +1691,8 @@
//
static const char *featurePrefix = "feature-";
static const int featurePrefixLen = strlen(featurePrefix);
+ static const char *tuningPrefix = "tuning-";
+ static const int tuningPrefixLen = strlen(tuningPrefix);
static const char *mappingPrefix = "mapping-";
static const int mappingPrefixLen = strlen(mappingPrefix);
@@ -1678,6 +1706,14 @@
intValue);
}
continue;
+ } else if (!strncmp(mapSrc, tuningPrefix, tuningPrefixLen)) {
+ AString value;
+ if (details->findString(mapSrc, &value)) {
+ ALOGV("-- tuning '%s' -> '%s'", mapSrc, value.c_str());
+ (void)(sShaperOps->setTuning)(shaperHandle, &mapSrc[tuningPrefixLen],
+ value.c_str());
+ }
+ continue;
} else if (!strncmp(mapSrc, mappingPrefix, mappingPrefixLen)) {
AString target;
if (details->findString(mapSrc, &target)) {
@@ -1697,11 +1733,37 @@
}
}
}
+}
+
+status_t MediaCodec::setupFormatShaper(AString mediaType) {
+ ALOGV("setupFormatShaper: initializing shaper data for codec %s mediaType %s",
+ mComponentName.c_str(), mediaType.c_str());
+
+ nsecs_t mapping_started = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // someone might have beaten us to it.
+ mediaformatshaper::shaperHandle_t shaperHandle;
+ shaperHandle = sShaperOps->findShaper(mComponentName.c_str(), mediaType.c_str());
+ if (shaperHandle != nullptr) {
+ ALOGV("shaperhandle %p -- no initialization needed", shaperHandle);
+ return OK;
+ }
+
+ // we get to build & register one
+ shaperHandle = sShaperOps->createShaper(mComponentName.c_str(), mediaType.c_str());
+ if (shaperHandle == nullptr) {
+ ALOGW("unable to create a shaper for cocodec %s mediaType %s",
+ mComponentName.c_str(), mediaType.c_str());
+ return OK;
+ }
+
+ (void) loadCodecProperties(shaperHandle, mCodecInfo, mediaType);
+
shaperHandle = sShaperOps->registerShaper(shaperHandle,
mComponentName.c_str(), mediaType.c_str());
nsecs_t mapping_finished = systemTime(SYSTEM_TIME_MONOTONIC);
- ALOGD("setupFormatShaper: populated shaper node for codec %s: %" PRId64 " us",
+ ALOGV("setupFormatShaper: populated shaper node for codec %s: %" PRId64 " us",
mComponentName.c_str(), (mapping_finished - mapping_started)/1000);
return OK;
@@ -1768,10 +1830,20 @@
AMediaFormat_getFormat(updatedNdkFormat, &updatedFormat);
sp<AMessage> deltas = updatedFormat->changesFrom(format, false /* deep */);
- ALOGD("shapeMediaFormat: deltas: %s", deltas->debugString(2).c_str());
-
- // note that this means that for anything in both, the copy in deltas wins
- format->extend(deltas);
+ size_t changeCount = deltas->countEntries();
+ ALOGD("shapeMediaFormat: deltas(%zu): %s", changeCount, deltas->debugString(2).c_str());
+ if (changeCount > 0) {
+ if (mMetricsHandle != 0) {
+ mediametrics_setInt32(mMetricsHandle, kCodecShapingEnhanced, changeCount);
+ // save some old properties before we fold in the new ones
+ int32_t bitrate;
+ if (format->findInt32(KEY_BIT_RATE, &bitrate)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecOriginalBitrate, bitrate);
+ }
+ }
+ // NB: for any field in both format and deltas, the deltas copy wins
+ format->extend(deltas);
+ }
}
AMediaFormat_delete(updatedNdkFormat);
@@ -1791,9 +1863,12 @@
// make sure we have the function entry points for the shaper library
//
+#if 0
+ // let's play the faster "only do mapping if we've already loaded the library
connectFormatShaper();
+#endif
if (sShaperOps == nullptr) {
- ALOGW("mapFormat: no MediaFormatShaper hooks available");
+ ALOGV("mapFormat: no MediaFormatShaper hooks available");
return;
}
@@ -3074,6 +3149,7 @@
ALOGV("TunnelPeekState: %s -> %s",
asString(previousState),
asString(TunnelPeekState::kBufferRendered));
+ updatePlaybackDuration(msg);
// check that we have a notification set
if (mOnFrameRenderedNotification != NULL) {
sp<AMessage> notify = mOnFrameRenderedNotification->dup();
@@ -4774,6 +4850,10 @@
return ALREADY_EXISTS;
}
+ // in case we don't connect, ensure that we don't signal the surface is
+ // connected to the screen
+ mIsSurfaceToScreen = false;
+
err = nativeWindowConnect(surface.get(), "connectToSurface");
if (err == OK) {
// Require a fresh set of buffers after each connect by using a unique generation
@@ -4799,6 +4879,10 @@
if (!mAllowFrameDroppingBySurface) {
disableLegacyBufferDropPostQ(surface);
}
+ // keep track whether or not the buffers of the connected surface go to the screen
+ int result = 0;
+ surface->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result);
+ mIsSurfaceToScreen = result != 0;
}
}
// do not return ALREADY_EXISTS unless surfaces are the same
@@ -4816,6 +4900,7 @@
}
// assume disconnected even on error
mSurface.clear();
+ mIsSurfaceToScreen = false;
}
return err;
}
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 876d06c..0107c32 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -169,9 +169,7 @@
}
status_t MediaCodecSource::Puller::setStopTimeUs(int64_t stopTimeUs) {
- sp<AMessage> msg = new AMessage(kWhatSetStopTimeUs, this);
- msg->setInt64("stop-time-us", stopTimeUs);
- return postSynchronouslyAndReturnError(msg);
+ return mSource->setStopTimeUs(stopTimeUs);
}
status_t MediaCodecSource::Puller::start(const sp<MetaData> &meta, const sp<AMessage> ¬ify) {
@@ -189,19 +187,11 @@
}
void MediaCodecSource::Puller::stop() {
- bool interrupt = false;
- {
- // mark stopping before actually reaching kWhatStop on the looper, so the pulling will
- // stop.
- Mutexed<Queue>::Locked queue(mQueue);
- queue->mPulling = false;
- interrupt = queue->mReadPendingSince && (queue->mReadPendingSince < ALooper::GetNowUs() - 1000000);
- queue->flush(); // flush any unprocessed pulled buffers
- }
-
- if (interrupt) {
- interruptSource();
- }
+ // mark stopping before actually reaching kWhatStop on the looper, so the pulling will
+ // stop.
+ Mutexed<Queue>::Locked queue(mQueue);
+ queue->mPulling = false;
+ queue->flush(); // flush any unprocessed pulled buffers
}
void MediaCodecSource::Puller::interruptSource() {
@@ -685,9 +675,9 @@
if (mStopping && reachedEOS) {
ALOGI("encoder (%s) stopped", mIsVideo ? "video" : "audio");
if (mPuller != NULL) {
- mPuller->stopSource();
+ mPuller->interruptSource();
}
- ALOGV("source (%s) stopped", mIsVideo ? "video" : "audio");
+ ALOGI("source (%s) stopped", mIsVideo ? "video" : "audio");
// posting reply to everyone that's waiting
List<sp<AReplyToken>>::iterator it;
for (it = mStopReplyIDQueue.begin();
@@ -715,6 +705,9 @@
status_t MediaCodecSource::feedEncoderInputBuffers() {
MediaBufferBase* mbuf = NULL;
while (!mAvailEncoderInputIndices.empty() && mPuller->readBuffer(&mbuf)) {
+ if (!mEncoder) {
+ return BAD_VALUE;
+ }
size_t bufferIndex = *mAvailEncoderInputIndices.begin();
mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
@@ -893,7 +886,7 @@
{
int32_t eos = 0;
if (msg->findInt32("eos", &eos) && eos) {
- ALOGV("puller (%s) reached EOS", mIsVideo ? "video" : "audio");
+ ALOGI("puller (%s) reached EOS", mIsVideo ? "video" : "audio");
signalEOS();
break;
}
@@ -1111,12 +1104,7 @@
if (generation != mGeneration) {
break;
}
-
- if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
- ALOGV("source (%s) stopping", mIsVideo ? "video" : "audio");
- mPuller->interruptSource();
- ALOGV("source (%s) stopped", mIsVideo ? "video" : "audio");
- }
+ ALOGD("source (%s) stopping stalled", mIsVideo ? "video" : "audio");
signalEOS();
break;
}
@@ -1148,7 +1136,7 @@
if (mFlags & FLAG_USE_SURFACE_INPUT) {
sp<AMessage> params = new AMessage;
params->setInt64(PARAMETER_KEY_OFFSET_TIME, mInputBufferTimeOffsetUs);
- err = mEncoder->setParameters(params);
+ err = mEncoder ? mEncoder->setParameters(params) : BAD_VALUE;
}
sp<AMessage> response = new AMessage;
@@ -1168,7 +1156,7 @@
if (mFlags & FLAG_USE_SURFACE_INPUT) {
sp<AMessage> params = new AMessage;
params->setInt64("stop-time-us", stopTimeUs);
- err = mEncoder->setParameters(params);
+ err = mEncoder ? mEncoder->setParameters(params) : BAD_VALUE;
} else {
err = mPuller->setStopTimeUs(stopTimeUs);
}
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index d77845f..2520e2a 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -253,6 +253,7 @@
(GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
if (getDef == nullptr) {
ALOGI("no sniffer found in %s", libPath.string());
+ dlclose(libHandle);
continue;
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index c91386d..a946f71 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -76,6 +76,7 @@
mFileMeta.clear();
mWriter.clear();
mTrackList.clear();
+ mFormatList.clear();
}
ssize_t MediaMuxer::addTrack(const sp<AMessage> &format) {
@@ -109,6 +110,8 @@
ALOGW("addTrack() setCaptureRate failed :%d", result);
}
}
+
+ mFormatList.add(format);
return mTrackList.add(newTrack);
}
@@ -224,9 +227,42 @@
ALOGV("BUFFER_FLAG_EOS");
}
+ sp<AMessage> bufMeta = buffer->meta();
+ int64_t val64;
+ if (bufMeta->findInt64("sample-file-offset", &val64)) {
+ sampleMetaData.setInt64(kKeySampleFileOffset, val64);
+ }
+ if (bufMeta->findInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ &val64)) {
+ sampleMetaData.setInt64(kKeyLastSampleIndexInChunk, val64);
+ }
+
sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
// This pushBuffer will wait until the mediaBuffer is consumed.
return currentTrack->pushBuffer(mediaBuffer);
}
+ssize_t MediaMuxer::getTrackCount() {
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackCount() must be called either in INITIALIZED or STARTED state");
+ return -1;
+ }
+ return mTrackList.size();
+}
+
+sp<AMessage> MediaMuxer::getTrackFormat([[maybe_unused]] size_t idx) {
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackFormat() must be called either in INITIALIZED or STARTED state");
+ return nullptr;
+ }
+ if (idx < 0 || idx >= mFormatList.size()) {
+ ALOGE("getTrackFormat() idx is out of range");
+ return nullptr;
+ }
+ return mFormatList[idx];
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp
index 24ba38a..2447f5e 100644
--- a/media/libstagefright/MediaTrack.cpp
+++ b/media/libstagefright/MediaTrack.cpp
@@ -133,6 +133,14 @@
if (format->mFormat->findInt64("target-time", &val64)) {
meta.setInt64(kKeyTargetTime, val64);
}
+ if (format->mFormat->findInt64("sample-file-offset", &val64)) {
+ meta.setInt64(kKeySampleFileOffset, val64);
+ }
+ if (format->mFormat->findInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ &val64)) {
+ meta.setInt64(kKeyLastSampleIndexInChunk, val64);
+ }
int32_t val32;
if (format->mFormat->findInt32("is-sync-frame", &val32)) {
meta.setInt32(kKeyIsSyncFrame, val32);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f2c7dd6..f0383b5 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -189,6 +189,11 @@
return err;
}
+const char* NuMediaExtractor::getName() const {
+ Mutex::Autolock autoLock(mLock);
+ return mImpl == nullptr ? nullptr : mImpl->name().string();
+}
+
static String8 arrayToString(const std::vector<uint8_t> &array) {
String8 result;
for (size_t i = 0; i < array.size(); i++) {
diff --git a/media/libstagefright/OWNERS b/media/libstagefright/OWNERS
index 819389d..0cc2294 100644
--- a/media/libstagefright/OWNERS
+++ b/media/libstagefright/OWNERS
@@ -4,4 +4,8 @@
lajos@google.com
marcone@google.com
taklee@google.com
-wonsik@google.com
\ No newline at end of file
+wonsik@google.com
+
+# LON
+olly@google.com
+andrewlewis@google.com
diff --git a/media/libstagefright/PlaybackDurationAccumulator.h b/media/libstagefright/PlaybackDurationAccumulator.h
new file mode 100644
index 0000000..cb5f0c4
--- /dev/null
+++ b/media/libstagefright/PlaybackDurationAccumulator.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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 PLAYBACK_DURATION_ACCUMULATOR_H_
+
+namespace android {
+
+// Accumulates playback duration by processing render times of individual frames and by ignoring
+// frames rendered during inactive playbacks such as seeking, pausing, or re-buffering.
+class PlaybackDurationAccumulator {
+private:
+ // Controls the maximum delta between render times before considering the playback is not
+ // active and has stalled.
+ static const int64_t MAX_PRESENTATION_DURATION_NS = 500 * 1000 * 1000;
+
+public:
+ PlaybackDurationAccumulator() {
+ mPlaybackDurationNs = 0;
+ mPreviousRenderTimeNs = 0;
+ }
+
+ // Process a render time expressed in nanoseconds.
+ void processRenderTime(int64_t newRenderTimeNs) {
+ // If we detect wrap-around or out of order frames, just ignore the duration for this
+ // and the next frame.
+ if (newRenderTimeNs < mPreviousRenderTimeNs) {
+ mPreviousRenderTimeNs = 0;
+ }
+ if (mPreviousRenderTimeNs > 0) {
+ int64_t presentationDurationNs = newRenderTimeNs - mPreviousRenderTimeNs;
+ if (presentationDurationNs < MAX_PRESENTATION_DURATION_NS) {
+ mPlaybackDurationNs += presentationDurationNs;
+ }
+ }
+ mPreviousRenderTimeNs = newRenderTimeNs;
+ }
+
+ int64_t getDurationInSeconds() {
+ return mPlaybackDurationNs / 1000 / 1000 / 1000; // Nanoseconds to seconds.
+ }
+
+private:
+ // The playback duration accumulated so far.
+ int64_t mPlaybackDurationNs;
+ // The previous render time used to compute the next presentation duration.
+ int64_t mPreviousRenderTimeNs;
+};
+
+}
+
+#endif
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 51d9730..04a9b17 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -725,16 +725,19 @@
}
};
-static std::vector<std::pair<const char *, uint32_t>> int64Mappings {
+static std::vector<std::pair<const char*, uint32_t>> int64Mappings {
{
- { "exif-offset", kKeyExifOffset },
- { "exif-size", kKeyExifSize },
- { "xmp-offset", kKeyXmpOffset },
- { "xmp-size", kKeyXmpSize },
- { "target-time", kKeyTargetTime },
- { "thumbnail-time", kKeyThumbnailTime },
- { "timeUs", kKeyTime },
- { "durationUs", kKeyDuration },
+ { "exif-offset", kKeyExifOffset},
+ { "exif-size", kKeyExifSize},
+ { "xmp-offset", kKeyXmpOffset},
+ { "xmp-size", kKeyXmpSize},
+ { "target-time", kKeyTargetTime},
+ { "thumbnail-time", kKeyThumbnailTime},
+ { "timeUs", kKeyTime},
+ { "durationUs", kKeyDuration},
+ { "sample-file-offset", kKeySampleFileOffset},
+ { "last-sample-index-in-chunk", kKeyLastSampleIndexInChunk},
+ { "sample-time-before-append", kKeySampleTimeBeforeAppend},
}
};
@@ -2169,7 +2172,7 @@
}
info->duration_us = duration;
- int32_t brate = -1;
+ int32_t brate = 0;
if (!meta->findInt32(kKeyBitRate, &brate)) {
ALOGV("track of type '%s' does not publish bitrate", mime);
}
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index dd2eed3..a15a988 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -274,6 +274,9 @@
<Limit name="bitrate" range="1-2000000" />
</Variant>
<Feature name="intra-refresh" />
+ <!-- Video Quality control -->
+ <!-- supports QP bounding with standard keys -->
+ <Feature name="qp-bounds" />
</MediaCodec>
<MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8" variant="slow-cpu,!slow-cpu">
<Alias name="OMX.google.vp8.encoder" />
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 2582ed0..7f2728e 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -106,6 +106,7 @@
off64_t mOffset;
off64_t mPreAllocateFileEndOffset; //End of file offset during preallocation.
off64_t mMdatOffset;
+ off64_t mMaxOffsetAppend; // File offset written upto while appending.
off64_t mMdatEndOffset; // End offset of mdat atom.
uint8_t *mInMemoryCache;
off64_t mInMemoryCacheOffset;
diff --git a/media/libstagefright/include/media/stagefright/MediaAppender.h b/media/libstagefright/include/media/stagefright/MediaAppender.h
new file mode 100644
index 0000000..c2f6f10
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaAppender.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_APPENDER_H
+#define ANDROID_MEDIA_APPENDER_H
+
+#include <media/stagefright/MediaMuxer.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <stack>
+
+namespace android {
+
+struct MediaAppender : public MediaMuxerBase {
+public:
+ enum AppendMode {
+ APPEND_MODE_FIRST = 0,
+ APPEND_MODE_IGNORE_LAST_VIDEO_GOP = APPEND_MODE_FIRST,
+ APPEND_MODE_ADD_TO_EXISTING_DATA = 1,
+ APPEND_MODE_LAST = APPEND_MODE_ADD_TO_EXISTING_DATA,
+ };
+
+ static sp<MediaAppender> create(int fd, AppendMode mode);
+
+ virtual ~MediaAppender();
+
+ status_t init();
+
+ status_t start();
+
+ status_t stop();
+
+ status_t writeSampleData(const sp<ABuffer>& buffer, size_t trackIndex, int64_t timeUs,
+ uint32_t flags);
+
+ status_t setOrientationHint(int degrees);
+
+ status_t setLocation(int latitude, int longitude);
+
+ ssize_t addTrack(const sp<AMessage> &format);
+
+ ssize_t getTrackCount();
+
+ sp<AMessage> getTrackFormat(size_t idx);
+
+private:
+ MediaAppender(int fd, AppendMode mode);
+
+ int mFd;
+ MediaMuxer::OutputFormat mFormat;
+ AppendMode mMode;
+ sp<NuMediaExtractor> mExtractor;
+ sp<MediaMuxer> mMuxer;
+ size_t mTrackCount;
+ // Map track index given by extractor to the ones received from muxer.
+ std::map<size_t, ssize_t> mTrackIndexMap;
+ // Count of the samples in each track, indexed by extractor track ids.
+ std::vector<size_t> mSampleCountVect;
+ // Extractor track index of samples.
+ std::vector<size_t> mSampleIndexVect;
+ // Track format indexed by extractor track ids.
+ std::map<size_t, sp<AMessage>> mFmtIndexMap;
+ // Size of samples.
+ std::vector<size_t> mSampleSizeVect;
+ // Presentation time stamp of samples.
+ std::vector<int64_t> mSampleTimeVect;
+ // Timestamp of last sample of tracks.
+ std::vector<int64_t> mMaxTimestampVect;
+ // Metadata of samples.
+ std::vector<sp<MetaData>> mSampleMetaVect;
+ std::mutex mMutex;
+ // Timestamp of the last sync sample of tracks.
+ std::vector<int64_t> mLastSyncSampleTimeVect;
+
+ struct sampleDataInfo;
+ std::vector<sampleDataInfo> mSDI;
+
+ enum : int {
+ UNINITIALIZED,
+ INITIALIZED,
+ STARTED,
+ STOPPED,
+ ERROR,
+ } mState GUARDED_BY(mMutex);
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_APPENDER_H
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 3f93e6d..d7b1794 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -58,6 +58,7 @@
struct PersistentSurface;
class SoftwareRenderer;
class Surface;
+class PlaybackDurationAccumulator;
namespace hardware {
namespace cas {
namespace native {
@@ -413,6 +414,7 @@
void updateLowLatency(const sp<AMessage> &msg);
constexpr const char *asString(TunnelPeekState state, const char *default_string="?");
void updateTunnelPeek(const sp<AMessage> &msg);
+ void updatePlaybackDuration(const sp<AMessage> &msg);
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
@@ -480,6 +482,9 @@
std::shared_ptr<BufferChannelBase> mBufferChannel;
+ PlaybackDurationAccumulator * mPlaybackDurationAccumulator;
+ bool mIsSurfaceToScreen;
+
MediaCodec(
const sp<ALooper> &looper, pid_t pid, uid_t uid,
std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase = nullptr,
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index a1b9465..e97a65e 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -22,7 +22,12 @@
#include <utils/Vector.h>
#include <utils/threads.h>
+#include <map>
+#include <mutex>
+#include <vector>
+
#include "media/stagefright/foundation/ABase.h"
+#include "MediaMuxerBase.h"
namespace android {
@@ -33,6 +38,7 @@
struct MediaSource;
class MetaData;
struct MediaWriter;
+struct NuMediaExtractor;
// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
// support a mp4 file as the output.
@@ -40,19 +46,8 @@
// Constructor -> addTrack+ -> start -> writeSampleData+ -> stop
// If muxing operation need to be cancelled, the app is responsible for
// deleting the output file after stop.
-struct MediaMuxer : public RefBase {
+struct MediaMuxer : public MediaMuxerBase {
public:
- // Please update media/java/android/media/MediaMuxer.java if the
- // OutputFormat is updated.
- enum OutputFormat {
- OUTPUT_FORMAT_MPEG_4 = 0,
- OUTPUT_FORMAT_WEBM = 1,
- OUTPUT_FORMAT_THREE_GPP = 2,
- OUTPUT_FORMAT_HEIF = 3,
- OUTPUT_FORMAT_OGG = 4,
- OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
- };
-
// Construct the muxer with the file descriptor. Note that the MediaMuxer
// will close this file at stop().
MediaMuxer(int fd, OutputFormat format);
@@ -117,10 +112,25 @@
status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
int64_t timeUs, uint32_t flags) ;
+ /**
+ * Gets the number of tracks added successfully. Should be called in
+ * INITIALIZED(after constructor) or STARTED(after start()) state.
+ * @return the number of tracks or -1 in wrong state.
+ */
+ ssize_t getTrackCount();
+
+ /**
+ * Gets the format of the track by their index.
+ * @param idx : index of the track whose format is wanted.
+ * @return smart pointer to AMessage containing the format details.
+ */
+ sp<AMessage> getTrackFormat(size_t idx);
+
private:
const OutputFormat mFormat;
sp<MediaWriter> mWriter;
Vector< sp<MediaAdapter> > mTrackList; // Each track has its MediaAdapter.
+ Vector< sp<AMessage> > mFormatList; // Format of each track.
sp<MetaData> mFileMeta; // Metadata for the whole file.
Mutex mMuxerLock;
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxerBase.h b/media/libstagefright/include/media/stagefright/MediaMuxerBase.h
new file mode 100644
index 0000000..f02d510
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaMuxerBase.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 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 MEDIA_MUXER_BASE_H_
+#define MEDIA_MUXER_BASE_H_
+
+#include <utils/RefBase.h>
+#include "media/stagefright/foundation/ABase.h"
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
+// support a mp4 file as the output.
+// The expected calling order of the functions is:
+// Constructor -> addTrack+ -> start -> writeSampleData+ -> stop
+// If muxing operation need to be cancelled, the app is responsible for
+// deleting the output file after stop.
+struct MediaMuxerBase : public RefBase {
+public:
+ // Please update media/java/android/media/MediaMuxer.java if the
+ // OutputFormat is updated.
+ enum OutputFormat {
+ OUTPUT_FORMAT_MPEG_4 = 0,
+ OUTPUT_FORMAT_WEBM = 1,
+ OUTPUT_FORMAT_THREE_GPP = 2,
+ OUTPUT_FORMAT_HEIF = 3,
+ OUTPUT_FORMAT_OGG = 4,
+ OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
+ };
+
+ // Construct the muxer with the file descriptor. Note that the MediaMuxer
+ // will close this file at stop().
+ MediaMuxerBase() {};
+
+ virtual ~MediaMuxerBase() {};
+
+ /**
+ * Add a track with its format information. This should be
+ * called before start().
+ * @param format the track's format.
+ * @return the track's index or negative number if error.
+ */
+ virtual ssize_t addTrack(const sp<AMessage> &format) = 0;
+
+ /**
+ * Start muxing. Make sure all the tracks have been added before
+ * calling this.
+ */
+ virtual status_t start() = 0;
+
+ /**
+ * Set the orientation hint.
+ * @param degrees The rotation degrees. It has to be either 0,
+ * 90, 180 or 270.
+ * @return OK if no error.
+ */
+ virtual status_t setOrientationHint(int degrees) = 0;
+
+ /**
+ * Set the location.
+ * @param latitude The latitude in degree x 1000. Its value must be in the range
+ * [-900000, 900000].
+ * @param longitude The longitude in degree x 1000. Its value must be in the range
+ * [-1800000, 1800000].
+ * @return OK if no error.
+ */
+ virtual status_t setLocation(int latitude, int longitude) = 0;
+
+ /**
+ * Stop muxing.
+ * This method is a blocking call. Depending on how
+ * much data is bufferred internally, the time needed for stopping
+ * the muxer may be time consuming. UI thread is
+ * not recommended for launching this call.
+ * @return OK if no error.
+ */
+ virtual status_t stop() = 0;
+
+ /**
+ * Send a sample buffer for muxing.
+ * The buffer can be reused once this method returns. Typically,
+ * this function won't be blocked for very long, and thus there
+ * is no need to use a separate thread calling this method to
+ * push a buffer.
+ * @param buffer the incoming sample buffer.
+ * @param trackIndex the buffer's track index number.
+ * @param timeUs the buffer's time stamp.
+ * @param flags the only supported flag for now is
+ * MediaCodec::BUFFER_FLAG_SYNCFRAME.
+ * @return OK if no error.
+ */
+ virtual status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
+ int64_t timeUs, uint32_t flags) = 0 ;
+
+ /**
+ * Gets the number of tracks added successfully. Should be called in
+ * INITIALIZED(after constructor) or STARTED(after start()) state.
+ * @return the number of tracks or -1 in wrong state.
+ */
+ virtual ssize_t getTrackCount() = 0;
+
+ /**
+ * Gets the format of the track by their index.
+ * @param idx : index of the track whose format is wanted.
+ * @return smart pointer to AMessage containing the format details.
+ */
+ virtual sp<AMessage> getTrackFormat(size_t idx) = 0;
+
+private:
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaMuxerBase);
+};
+
+} // namespace android
+
+#endif // MEDIA_MUXER_BASE_H_
+
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 940bd86..408872f 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -264,6 +264,11 @@
// Slow-motion markers
kKeySlowMotionMarkers = 'slmo', // raw data, byte array following spec for
// MediaFormat#KEY_SLOW_MOTION_MARKERS
+
+ kKeySampleFileOffset = 'sfof', // int64_t, sample's offset in a media file.
+ kKeyLastSampleIndexInChunk = 'lsic', //int64_t, index of last sample in a chunk.
+ kKeySampleTimeBeforeAppend = 'lsba', // int64_t, timestamp of last sample of a track.
+
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index d8f2b00..6aa7c0f 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -100,6 +100,10 @@
status_t getAudioPresentations(size_t trackIdx, AudioPresentationCollection *presentations);
+ status_t setPlaybackId(const String8& playbackId);
+
+ const char* getName() const;
+
protected:
virtual ~NuMediaExtractor();
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 5a9760d..67c6102 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -146,7 +146,10 @@
};
static std::vector<std::string> names = {
prefixes[0] + variants[0] + ".xml",
- prefixes[1] + variants[1] + ".xml"
+ prefixes[1] + variants[1] + ".xml",
+
+ // shaping information is not currently variant specific.
+ "media_codecs_shaping.xml"
};
return names;
}
@@ -347,6 +350,7 @@
status_t addFeature(const char **attrs);
status_t addLimit(const char **attrs);
status_t addMapping(const char **attrs);
+ status_t addTuning(const char **attrs);
status_t addQuirk(const char **attrs, const char *prefix = nullptr);
status_t addSetting(const char **attrs, const char *prefix = nullptr);
status_t enterMediaCodec(const char **attrs, bool encoder);
@@ -429,7 +433,7 @@
if (findFileInDirs(searchDirs, fileName, &path)) {
err = parseXmlPath(path);
} else {
- ALOGD("Cannot find %s in search path", fileName.c_str());
+ ALOGI("Did not find %s in search path", fileName.c_str());
}
res = combineStatus(res, err);
}
@@ -439,7 +443,7 @@
status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) {
std::lock_guard<std::mutex> guard(mLock);
if (!fileExists(path)) {
- ALOGD("Cannot find %s", path.c_str());
+ ALOGV("Cannot find %s", path.c_str());
mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND);
return NAME_NOT_FOUND;
}
@@ -743,7 +747,8 @@
// ignore limits and features specified outside of type
if (!mState->inType()
&& (strEq(name, "Limit") || strEq(name, "Feature")
- || strEq(name, "Variant") || strEq(name, "Mapping"))) {
+ || strEq(name, "Variant") || strEq(name, "Mapping")
+ || strEq(name, "Tuning"))) {
PLOGD("ignoring %s specified outside of a Type", name);
return;
} else if (strEq(name, "Limit")) {
@@ -752,6 +757,8 @@
err = addFeature(attrs);
} else if (strEq(name, "Mapping")) {
err = addMapping(attrs);
+ } else if (strEq(name, "Tuning")) {
+ err = addTuning(attrs);
} else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
err = limitVariants(attrs);
mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
@@ -1445,6 +1452,45 @@
return OK;
}
+status_t MediaCodecsXmlParser::Impl::Parser::addTuning(const char **attrs) {
+ CHECK(mState->inType());
+ size_t i = 0;
+ const char *a_name = nullptr;
+ const char *a_value = nullptr;
+
+ while (attrs[i] != nullptr) {
+ CHECK((i & 1) == 0);
+ if (attrs[i + 1] == nullptr) {
+ PLOGD("Mapping: attribute '%s' is null", attrs[i]);
+ return BAD_VALUE;
+ }
+
+ if (strEq(attrs[i], "name")) {
+ a_name = attrs[++i];
+ } else if (strEq(attrs[i], "value")) {
+ a_value = attrs[++i];
+ } else {
+ PLOGD("Tuning: ignoring unrecognized attribute '%s'", attrs[i]);
+ ++i;
+ }
+ ++i;
+ }
+
+ // Every tuning must have both fields
+ if (a_name == nullptr) {
+ PLOGD("Tuning with no 'name' attribute");
+ return BAD_VALUE;
+ }
+
+ if (a_value == nullptr) {
+ PLOGD("Tuning with no 'value' attribute");
+ return BAD_VALUE;
+ }
+
+ mState->addDetail(std::string("tuning-") + a_name, a_value);
+ return OK;
+}
+
status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
CHECK(mState->inCodec());
size_t i = 0;
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index 6f55dc0..ecfd85e 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -87,6 +87,7 @@
method public String getName();
method public java.util.List<media.codecs.Quirk> getQuirk_optional();
method public String getRank();
+ method public java.util.List<media.codecs.Tuning> getTuning_optional();
method public String getType();
method public java.util.List<media.codecs.Type> getType_optional();
method public String getUpdate();
@@ -136,6 +137,14 @@
method public java.util.List<media.codecs.Setting> getVariant_optional();
}
+ public class Tuning {
+ ctor public Tuning();
+ method public String getName();
+ method public String getValue();
+ method public void setName(String);
+ method public void setValue(String);
+ }
+
public class Type {
ctor public Type();
method public java.util.List<media.codecs.Alias> getAlias();
diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd
index 30974f6..c9a7efc 100644
--- a/media/libstagefright/xmlparser/media_codecs.xsd
+++ b/media/libstagefright/xmlparser/media_codecs.xsd
@@ -64,6 +64,7 @@
<xs:element name="Limit" type="Limit" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Feature" type="Feature" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Mapping" type="Mapping" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="Tuning" type="Tuning" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Variant" type="Variant" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
<xs:attribute name="name" type="xs:string"/>
@@ -128,6 +129,10 @@
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="value" type="xs:string"/>
</xs:complexType>
+ <xs:complexType name="Tuning">
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="value" type="xs:string"/>
+ </xs:complexType>
<xs:complexType name="Include">
<xs:attribute name="href" type="xs:string"/>
</xs:complexType>
diff --git a/media/libstagefright/xmlparser/test/XMLParserTest.cpp b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
index c411c8d..7629d97 100644
--- a/media/libstagefright/xmlparser/test/XMLParserTest.cpp
+++ b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
@@ -138,6 +138,12 @@
pair<string, string>("mapping-fire-from", "to"),
},
{}, "");
+ setCodecProperties("test11.encoder", true, 11, {}, {}, {}, "video/av01",
+ {
+ pair<string, string>("tuning-hungry", "yes"),
+ pair<string, string>("tuning-pi", "3.1415"),
+ },
+ {}, "");
setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
{pair<string, string>("attribute::disabled", "present"),
@@ -180,6 +186,11 @@
setRoleProperties("video_encoder.hevc", true, 10, "video/hevc", "test10.encoder",
{ pair<string, string>("mapping-fire-from", "to")});
+ setRoleProperties("video_encoder.av01", true, 11, "video/av01", "test11.encoder",
+ {pair<string, string>("tuning-hungry", "yes"),
+ pair<string, string>("tuning-pi", "3.1415")
+ });
+
setServiceAttribute(
{pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
index c8913e5..8cae423 100644
--- a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
@@ -83,5 +83,10 @@
<MediaCodec name="test10.encoder" type="video/hevc" >
<Mapping kind="fire" name="from" value="to"/>
</MediaCodec>
+ <!-- entry for testing Tuning -->
+ <MediaCodec name="test11.encoder" type="video/av01" >
+ <Tuning name="hungry" value="yes"/>
+ <Tuning name="pi" value="3.1415"/>
+ </MediaCodec>
</Encoders>
</Included>
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 0c65e9e..07fc5de 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -419,6 +419,7 @@
EXPORT
media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) {
+ ALOGV("AMediaExtractor_getSampleFormat");
if (fmt == NULL) {
return AMEDIA_ERROR_INVALID_PARAMETER;
}
@@ -428,6 +429,9 @@
if (err != OK) {
return translate_error(err);
}
+#ifdef LOG_NDEBUG
+ sampleMeta->dumpToLog();
+#endif
sp<AMessage> meta;
AMediaFormat_getFormat(fmt, &meta);
@@ -483,6 +487,19 @@
meta->setBuffer(AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, audioPresentationsData);
}
+ int64_t val64;
+ if (sampleMeta->findInt64(kKeySampleFileOffset, &val64)) {
+ meta->setInt64("sample-file-offset", val64);
+ ALOGV("SampleFileOffset Found");
+ }
+ if (sampleMeta->findInt64(kKeyLastSampleIndexInChunk, &val64)) {
+ meta->setInt64("last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ val64);
+ ALOGV("kKeyLastSampleIndexInChunk Found");
+ }
+
+ ALOGV("AMediaFormat_toString:%s", AMediaFormat_toString(fmt));
+
return AMEDIA_OK;
}
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 1773023..c1793ce 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -334,6 +334,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME = "is-sync-frame";
EXPORT const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval";
EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
+EXPORT const char* AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK = "last-sample-index-in-chunk";
EXPORT const char* AMEDIAFORMAT_KEY_LATENCY = "latency";
EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level";
EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location";
@@ -359,7 +360,9 @@
EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET = "sample-file-offset";
EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND = "sample-time-before-append";
EXPORT const char* AMEDIAFORMAT_KEY_SAR_HEIGHT = "sar-height";
EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width";
EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index d1992bf..1965e62 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -17,28 +17,24 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaMuxer"
-
-#include <media/NdkMediaMuxer.h>
+#include <android_util_Binder.h>
+#include <jni.h>
+#include <media/IMediaHTTPService.h>
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaErrorPriv.h>
#include <media/NdkMediaFormatPriv.h>
-
-
-#include <utils/Log.h>
-#include <utils/StrongPointer.h>
+#include <media/NdkMediaMuxer.h>
+#include <media/stagefright/MediaAppender.h>
+#include <media/stagefright/MediaMuxer.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaMuxer.h>
-#include <media/IMediaHTTPService.h>
-#include <android_util_Binder.h>
-
-#include <jni.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
using namespace android;
struct AMediaMuxer {
- sp<MediaMuxer> mImpl;
-
+ sp<MediaMuxerBase> mImpl;
};
extern "C" {
@@ -46,8 +42,15 @@
EXPORT
AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) {
ALOGV("ctor");
- AMediaMuxer *mData = new AMediaMuxer();
- mData->mImpl = new MediaMuxer(fd, (android::MediaMuxer::OutputFormat)format);
+ AMediaMuxer *mData = new (std::nothrow) AMediaMuxer();
+ if (mData == nullptr) {
+ return nullptr;
+ }
+ mData->mImpl = new (std::nothrow) MediaMuxer(fd, (android::MediaMuxer::OutputFormat)format);
+ if (mData->mImpl == nullptr) {
+ delete mData;
+ return nullptr;
+ }
return mData;
}
@@ -94,6 +97,34 @@
muxer->mImpl->writeSampleData(buf, trackIdx, info->presentationTimeUs, info->flags));
}
+EXPORT
+AMediaMuxer* AMediaMuxer_append(int fd, AppendMode mode) {
+ ALOGV("append");
+ AMediaMuxer* mData = new (std::nothrow) AMediaMuxer();
+ if (mData == nullptr) {
+ return nullptr;
+ }
+ mData->mImpl = MediaAppender::create(fd, (android::MediaAppender::AppendMode)mode);
+ if (mData->mImpl == nullptr) {
+ delete mData;
+ return nullptr;
+ }
+ return mData;
+}
+
+EXPORT
+ssize_t AMediaMuxer_getTrackCount(AMediaMuxer* muxer) {
+ return muxer->mImpl->getTrackCount();
+}
+
+EXPORT
+AMediaFormat* AMediaMuxer_getTrackFormat(AMediaMuxer* muxer, size_t idx) {
+ sp<AMessage> format = muxer->mImpl->getTrackFormat(idx);
+ if (format != nullptr) {
+ return AMediaFormat_fromMsg(&format);
+ }
+ return nullptr;
+}
} // extern "C"
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 476bbd9..fbd855d 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -307,6 +307,9 @@
extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C __INTRODUCED_IN(31);
extern const char* AMEDIAFORMAT_KEY_XMP_OFFSET __INTRODUCED_IN(31);
extern const char* AMEDIAFORMAT_KEY_XMP_SIZE __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND __INTRODUCED_IN(31);
extern const char* AMEDIAFORMAT_VIDEO_QP_B_MAX __INTRODUCED_IN(31);
extern const char* AMEDIAFORMAT_VIDEO_QP_B_MIN __INTRODUCED_IN(31);
diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h
index 519e249..866ebfd 100644
--- a/media/ndk/include/media/NdkMediaMuxer.h
+++ b/media/ndk/include/media/NdkMediaMuxer.h
@@ -54,6 +54,17 @@
AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP = 2,
} OutputFormat;
+typedef enum {
+ /* Last group of pictures(GOP) of video track can be incomplete, so it would be safe to
+ * scrap that and rewrite. If both audio and video tracks are present in a file, then
+ * samples of audio track after last GOP of video would be scrapped too.
+ * If only audio track is present, then no sample would be discarded.
+ */
+ AMEDIAMUXER_APPEND_IGNORE_LAST_VIDEO_GOP = 0,
+ // Keep all existing samples as it is and append new samples after that only.
+ AMEDIAMUXER_APPEND_TO_EXISTING_DATA = 1,
+} AppendMode;
+
/**
* Create new media muxer.
*
@@ -138,6 +149,41 @@
size_t trackIdx, const uint8_t *data,
const AMediaCodecBufferInfo *info) __INTRODUCED_IN(21);
+/**
+ * Creates a new media muxer for appending data to an existing MPEG4 file.
+ * This is a synchronous API call and could take a while to return if the existing file is large.
+ * Works for only MPEG4 files that contain a) a single audio track, b) a single video track,
+ * c) a single audio and a single video track.
+ * @param(fd): needs to be opened with read and write permission. Does not take ownership of
+ * this fd i.e., caller is responsible for closing fd.
+ * @param(mode): AppendMode is an enum that specifies one of the modes of appending data.
+ * @return : Pointer to AMediaMuxer if the file(fd) has tracks already, otherwise, nullptr.
+ * {@link AMediaMuxer_delete} should be used to free the returned pointer.
+ *
+ * Available since API level 31.
+ */
+AMediaMuxer* AMediaMuxer_append(int fd, AppendMode mode) __INTRODUCED_IN(31);
+
+/**
+ * Returns the number of tracks added in the file passed to {@link AMediaMuxer_new} or
+ * the number of existing tracks in the file passed to {@link AMediaMuxer_append}.
+ * Should be called in INITIALIZED or STARTED state, otherwise returns -1.
+ *
+ * Available since API level 31.
+ */
+ssize_t AMediaMuxer_getTrackCount(AMediaMuxer*) __INTRODUCED_IN(31);
+
+/**
+ * Returns AMediaFormat of the added track with index idx in the file passed to
+ * {@link AMediaMuxer_new} or the AMediaFormat of the existing track with index idx
+ * in the file passed to {@link AMediaMuxer_append}.
+ * Should be called in INITIALIZED or STARTED state, otherwise returns nullptr.
+ * {@link AMediaFormat_delete} should be used to free the returned pointer.
+ *
+ * Available since API level 31.
+ */
+AMediaFormat* AMediaMuxer_getTrackFormat(AMediaMuxer* muxer, size_t idx) __INTRODUCED_IN(31);
+
__END_DECLS
#endif // _NDK_MEDIA_MUXER_H
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index eead681..7e9e57e 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -109,6 +109,7 @@
AMEDIAFORMAT_KEY_IS_SYNC_FRAME; # var introduced=29
AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; # var introduced=21
AMEDIAFORMAT_KEY_LANGUAGE; # var introduced=21
+ AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK; # var introduced=31
AMEDIAFORMAT_KEY_LATENCY; # var introduced=28
AMEDIAFORMAT_KEY_LEVEL; # var introduced=28
AMEDIAFORMAT_KEY_LOCATION; # var introduced=29
@@ -134,6 +135,8 @@
AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; # var introduced=21
AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var introduced=21
AMEDIAFORMAT_KEY_ROTATION; # var introduced=28
+ AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET; # var introduced=31
+ AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND; # var introduced=31
AMEDIAFORMAT_KEY_SAMPLE_RATE; # var introduced=21
AMEDIAFORMAT_KEY_SAR_HEIGHT; # var introduced=29
AMEDIAFORMAT_KEY_SAR_WIDTH; # var introduced=29
@@ -286,7 +289,10 @@
AMediaFormat_setString;
AMediaFormat_toString;
AMediaMuxer_addTrack;
+ AMediaMuxer_append; # introduced=31
AMediaMuxer_delete;
+ AMediaMuxer_getTrackCount; # introduced=31
+ AMediaMuxer_getTrackFormat; # introduced=31
AMediaMuxer_new;
AMediaMuxer_setLocation;
AMediaMuxer_setOrientationHint;
diff --git a/media/tests/SampleVideoEncoder/README.md b/media/tests/SampleVideoEncoder/README.md
index 074c939..2e275c5 100644
--- a/media/tests/SampleVideoEncoder/README.md
+++ b/media/tests/SampleVideoEncoder/README.md
@@ -2,7 +2,7 @@
This is a sample android application for encoding AVC/HEVC streams with B-Frames enabled. It uses MediaRecorder APIs to record B-frames enabled video from camera2 input and MediaCodec APIs to encode reference test vector using input surface.
-This page describes how to get started with the Encoder App.
+This page describes how to get started with the Encoder App and how to run the tests for it.
# Getting Started
@@ -33,6 +33,17 @@
After installing the app, a TextureView showing camera preview is dispalyed on one third of the screen. It also features checkboxes to select either avc/hevc and hw/sw codecs. It also has an option to select either MediaRecorder APIs or MediaCodec, along with the 'Start' button to start/stop recording.
+# Running Tests
+
+The app also contains a test, which will test the MediaCodec APIs for encoding avc/hevc streams with B-frames enabled. This does not require us to use application UI.
+
+## Running the tests using atest
+Note that atest command will install the SampleVideoEncoder app on the device.
+
+Command to run the tests:
+```
+atest SampleVideoEncoder
+```
# Ouput
@@ -40,3 +51,6 @@
```
/storage/emulated/0/Android/data/com.android.media.samplevideoencoder/files/
```
+
+The total number of I-frames, P-frames and B-frames after encoding has been done using MediaCodec APIs are displayed on the screen.
+The results of the tests can be obtained from the logcats of the test.
diff --git a/media/tests/SampleVideoEncoder/app/Android.bp b/media/tests/SampleVideoEncoder/app/Android.bp
index 3a66955..58b219b 100644
--- a/media/tests/SampleVideoEncoder/app/Android.bp
+++ b/media/tests/SampleVideoEncoder/app/Android.bp
@@ -23,7 +23,7 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-android_app {
+android_test {
name: "SampleVideoEncoder",
manifest: "src/main/AndroidManifest.xml",
@@ -41,6 +41,10 @@
"androidx.annotation_annotation",
"androidx.appcompat_appcompat",
"androidx-constraintlayout_constraintlayout",
+ "junit",
+ "androidx.test.core",
+ "androidx.test.runner",
+ "hamcrest-library",
],
javacflags: [
diff --git a/media/tests/SampleVideoEncoder/app/AndroidTest.xml b/media/tests/SampleVideoEncoder/app/AndroidTest.xml
new file mode 100644
index 0000000..91f4304
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs SampleVideoEncoder Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="false" />
+ <option name="test-file-name" value="SampleVideoEncoder.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="SampleVideoEncoder" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.media.samplevideoencoder" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/media/tests/SampleVideoEncoder/app/src/androidTest/java/com/android/media/samplevideoencoder/tests/SampleVideoEncoderTest.java b/media/tests/SampleVideoEncoder/app/src/androidTest/java/com/android/media/samplevideoencoder/tests/SampleVideoEncoderTest.java
new file mode 100644
index 0000000..1ef332e
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/androidTest/java/com/android/media/samplevideoencoder/tests/SampleVideoEncoderTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+package com.android.media.samplevideoencoder.tests;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import android.content.Context;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import com.android.media.samplevideoencoder.MediaCodecSurfaceEncoder;
+import com.android.media.samplevideoencoder.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+@RunWith(Parameterized.class)
+public class SampleVideoEncoderTest {
+ private static final String TAG = SampleVideoEncoderTest.class.getSimpleName();
+ private final Context mContext;
+ private int mMaxBFrames;
+ private int mInputResId;
+ private String mMime;
+ private boolean mIsSoftwareEncoder;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> inputFiles() {
+ return Arrays.asList(new Object[][]{
+ // Parameters: MimeType, isSoftwareEncoder, maxBFrames
+ {MediaFormat.MIMETYPE_VIDEO_AVC, false, 1},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, true, 1},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, false, 1},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, true, 1}});
+ }
+
+ public SampleVideoEncoderTest(String mimeType, boolean isSoftwareEncoder, int maxBFrames) {
+ this.mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ this.mInputResId = R.raw.crowd_1920x1080_25fps_4000kbps_h265;
+ this.mMime = mimeType;
+ this.mIsSoftwareEncoder = isSoftwareEncoder;
+ this.mMaxBFrames = maxBFrames;
+ }
+
+ private String getOutputPath() {
+ File dir = mContext.getExternalFilesDir(null);
+ if (dir == null) {
+ Log.e(TAG, "Cannot get external directory path to save output video");
+ return null;
+ }
+ String videoPath = dir.getAbsolutePath() + "/Video-" + System.currentTimeMillis() + ".mp4";
+ Log.i(TAG, "Output video is saved at: " + videoPath);
+ return videoPath;
+ }
+
+ @Test
+ public void testMediaSurfaceEncoder() throws IOException, InterruptedException {
+ String outputFilePath = getOutputPath();
+ MediaCodecSurfaceEncoder surfaceEncoder =
+ new MediaCodecSurfaceEncoder(mContext, mInputResId, mMime, mIsSoftwareEncoder,
+ outputFilePath, mMaxBFrames);
+ int encodingStatus = surfaceEncoder.startEncodingSurface();
+ assertThat(encodingStatus, is(equalTo(0)));
+ int[] frameNumArray = surfaceEncoder.getFrameTypes();
+ Log.i(TAG, "Results: I-Frames: " + frameNumArray[0] + "; P-Frames: " + frameNumArray[1] +
+ "\n " + "; B-Frames:" + frameNumArray[2]);
+ assertNotEquals("Encoder mime: " + mMime + " isSoftware: " + mIsSoftwareEncoder +
+ " failed to generate B Frames", frameNumArray[2], 0);
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
index ed668bb..b17541d 100644
--- a/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
+++ b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
@@ -38,4 +38,8 @@
</activity>
</application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.media.samplevideoencoder"
+ android:label="SampleVideoEncoder Test"/>
+
</manifest>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
index 33e81bb..a7a353c 100644
--- a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
@@ -56,6 +56,7 @@
import android.util.Log;
import android.util.Size;
import android.widget.RadioGroup;
+import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
@@ -80,6 +81,14 @@
private static final int VIDEO_BITRATE = 8000000 /* 8 Mbps */;
private static final int VIDEO_FRAMERATE = 30;
+ /**
+ * Constant values to frame types assigned here are internal to this app.
+ * These values does not correspond to the actual values defined in avc/hevc specifications.
+ */
+ public static final int FRAME_TYPE_I = 0;
+ public static final int FRAME_TYPE_P = 1;
+ public static final int FRAME_TYPE_B = 2;
+
private String mMime = MediaFormat.MIMETYPE_VIDEO_AVC;
private String mOutputVideoPath = null;
@@ -89,6 +98,7 @@
private boolean mIsRecording;
private AutoFitTextureView mTextureView;
+ private TextView mTextView;
private CameraDevice mCameraDevice;
private CameraCaptureSession mPreviewSession;
private CaptureRequest.Builder mPreviewBuilder;
@@ -101,6 +111,8 @@
private Button mStartButton;
+ private int[] mFrameTypeOccurrences;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -129,6 +141,8 @@
final CheckBox checkBox_mr = findViewById(R.id.checkBox_media_recorder);
final CheckBox checkBox_mc = findViewById(R.id.checkBox_media_codec);
mTextureView = findViewById(R.id.texture);
+ mTextView = findViewById(R.id.textViewResults);
+
checkBox_mr.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -162,6 +176,7 @@
@Override
public void onClick(View v) {
if (v.getId() == R.id.start_button) {
+ mTextView.setText(null);
if (mIsMediaRecorder) {
if (mIsRecording) {
stopRecordingVideo();
@@ -198,6 +213,7 @@
mainActivity.mOutputVideoPath);
try {
encodingStatus = codecSurfaceEncoder.startEncodingSurface();
+ mainActivity.mFrameTypeOccurrences = codecSurfaceEncoder.getFrameTypes();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
@@ -211,6 +227,13 @@
if (encodingStatus == 0) {
Toast.makeText(mainActivity.getApplicationContext(), "Encoding Completed",
Toast.LENGTH_SHORT).show();
+ mainActivity.mTextView.append("\n Encoded stream contains: ");
+ mainActivity.mTextView.append("\n Number of I-Frames: " +
+ mainActivity.mFrameTypeOccurrences[FRAME_TYPE_I]);
+ mainActivity.mTextView.append("\n Number of P-Frames: " +
+ mainActivity.mFrameTypeOccurrences[FRAME_TYPE_P]);
+ mainActivity.mTextView.append("\n Number of B-Frames: " +
+ mainActivity.mFrameTypeOccurrences[FRAME_TYPE_B]);
} else {
Toast.makeText(mainActivity.getApplicationContext(),
"Error occurred while " + "encoding", Toast.LENGTH_SHORT).show();
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
index 146a475..011c38c 100644
--- a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
@@ -31,10 +31,14 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
+
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_B;
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_I;
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_P;
public class MediaCodecSurfaceEncoder {
private static final String TAG = MediaCodecSurfaceEncoder.class.getSimpleName();
-
private static final boolean DEBUG = false;
private static final int VIDEO_BITRATE = 8000000 /*8 Mbps*/;
private static final int VIDEO_FRAMERATE = 30;
@@ -44,6 +48,8 @@
private final String mMime;
private final String mOutputPath;
private int mTrackID = -1;
+ private int mFrameNum = 0;
+ private int[] mFrameTypeOccurrences = {0, 0, 0};
private Surface mSurface;
private MediaExtractor mExtractor;
@@ -128,8 +134,10 @@
mEncoder.reset();
mSurface.release();
mSurface = null;
+ Log.i(TAG, "Number of I-frames = " + mFrameTypeOccurrences[FRAME_TYPE_I]);
+ Log.i(TAG, "Number of P-frames = " + mFrameTypeOccurrences[FRAME_TYPE_P]);
+ Log.i(TAG, "Number of B-frames = " + mFrameTypeOccurrences[FRAME_TYPE_B]);
}
-
mEncoder.release();
mDecoder.release();
mExtractor.release();
@@ -193,6 +201,8 @@
mSawEncOutputEOS = false;
mDecOutputCount = 0;
mEncOutputCount = 0;
+ mFrameNum = 0;
+ Arrays.fill(mFrameTypeOccurrences, 0);
}
private void configureCodec(MediaFormat decFormat, MediaFormat encFormat) {
@@ -336,6 +346,21 @@
}
if (info.size > 0) {
ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex);
+ // Parse the buffer to get the frame type
+ if (DEBUG) Log.d(TAG, "[ Frame : " + (mFrameNum++) + " ]");
+ int frameTypeResult = -1;
+ if (mMime == MediaFormat.MIMETYPE_VIDEO_AVC) {
+ frameTypeResult = NalUnitUtil.getStandardizedFrameTypesFromAVC(buf);
+ } else if (mMime == MediaFormat.MIMETYPE_VIDEO_HEVC){
+ frameTypeResult = NalUnitUtil.getStandardizedFrameTypesFromHEVC(buf);
+ } else {
+ Log.e(TAG, "Mime type " + mMime + " is not supported.");
+ return;
+ }
+ if (frameTypeResult != -1) {
+ mFrameTypeOccurrences[frameTypeResult]++;
+ }
+
if (mMuxer != null) {
if (mTrackID == -1) {
mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat());
@@ -353,4 +378,8 @@
private boolean hasSeenError() {
return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
}
+
+ public int[] getFrameTypes() {
+ return mFrameTypeOccurrences;
+ }
}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/NalUnitUtil.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/NalUnitUtil.java
new file mode 100644
index 0000000..efff4fd
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/NalUnitUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.media.samplevideoencoder;
+
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_B;
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_I;
+import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_P;
+
+public class NalUnitUtil {
+ private static final String TAG = MediaCodecSurfaceEncoder.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ public static int findNalUnit(byte[] dataArray, int pos, int limit) {
+ int startOffset = 0;
+ if (limit - pos < 4) {
+ return startOffset;
+ }
+ if (dataArray[pos] == 0 && dataArray[pos + 1] == 0 && dataArray[pos + 2] == 1) {
+ startOffset = 3;
+ } else {
+ if (dataArray[pos] == 0 && dataArray[pos + 1] == 0 && dataArray[pos + 2] == 0 &&
+ dataArray[pos + 3] == 1) {
+ startOffset = 4;
+ }
+ }
+ return startOffset;
+ }
+
+ private static int getAVCNalUnitType(byte[] dataArray, int nalUnitOffset) {
+ return dataArray[nalUnitOffset] & 0x1F;
+ }
+
+ private static int parseAVCNALUnitData(byte[] dataArray, int offset, int limit) {
+ ParsableBitArray bitArray = new ParsableBitArray(dataArray);
+ bitArray.reset(dataArray, offset, limit);
+
+ bitArray.skipBit(); // forbidden_zero_bit
+ bitArray.readBits(2); // nal_ref_idc
+ bitArray.skipBits(5); // nal_unit_type
+
+ bitArray.readUEV(); // first_mb_in_slice
+ if (!bitArray.canReadUEV()) {
+ return -1;
+ }
+ int sliceType = bitArray.readUEV();
+ if (DEBUG) Log.d(TAG, "slice_type = " + sliceType);
+ if (sliceType == 0) {
+ return FRAME_TYPE_P;
+ } else if (sliceType == 1) {
+ return FRAME_TYPE_B;
+ } else if (sliceType == 2) {
+ return FRAME_TYPE_I;
+ } else {
+ return -1;
+ }
+ }
+
+ private static int getHEVCNalUnitType(byte[] dataArray, int nalUnitOffset) {
+ return (dataArray[nalUnitOffset] & 0x7E) >> 1;
+ }
+
+ private static int parseHEVCNALUnitData(byte[] dataArray, int offset, int limit,
+ int nalUnitType) {
+ // nal_unit_type values from H.265/HEVC Table 7-1.
+ final int BLA_W_LP = 16;
+ final int RSV_IRAP_VCL23 = 23;
+
+ ParsableBitArray bitArray = new ParsableBitArray(dataArray);
+ bitArray.reset(dataArray, offset, limit);
+
+ bitArray.skipBit(); // forbidden zero bit
+ bitArray.readBits(6); // nal_unit_header
+ bitArray.readBits(6); // nuh_layer_id
+ bitArray.readBits(3); // nuh_temporal_id_plus1
+
+ // Parsing slice_segment_header values from H.265/HEVC Table 7.3.6.1
+ boolean first_slice_segment = bitArray.readBit(); // first_slice_segment_in_pic_flag
+ if (!first_slice_segment) return -1;
+ if (nalUnitType >= BLA_W_LP && nalUnitType <= RSV_IRAP_VCL23) {
+ bitArray.readBit(); // no_output_of_prior_pics_flag
+ }
+ bitArray.readUEV(); // slice_pic_parameter_set_id
+ // Assume num_extra_slice_header_bits element of PPS data to be 0
+ int sliceType = bitArray.readUEV();
+ if (DEBUG) Log.d(TAG, "slice_type = " + sliceType);
+ if (sliceType == 0) {
+ return FRAME_TYPE_B;
+ } else if (sliceType == 1) {
+ return FRAME_TYPE_P;
+ } else if (sliceType == 2) {
+ return FRAME_TYPE_I;
+ } else {
+ return -1;
+ }
+ }
+
+ public static int getStandardizedFrameTypesFromAVC(ByteBuffer buf) {
+ int limit = buf.limit();
+ byte[] dataArray = new byte[buf.remaining()];
+ buf.get(dataArray);
+ int frameType = -1;
+ for (int pos = 0; pos + 3 < limit; ) {
+ int startOffset = NalUnitUtil.findNalUnit(dataArray, pos, limit);
+ if (startOffset != 0) {
+ int nalUnitType = getAVCNalUnitType(dataArray, (pos + startOffset));
+ if (DEBUG) {
+ Log.d(TAG, "NalUnitOffset = " + (pos + startOffset));
+ Log.d(TAG, "NalUnitType = " + nalUnitType);
+ }
+ // SLICE_NAL = 1; IDR_SLICE_NAL = 5
+ if (nalUnitType == 1 || nalUnitType == 5) {
+ frameType = parseAVCNALUnitData(dataArray, (pos + startOffset),
+ (limit - pos - startOffset));
+ break;
+ }
+ pos += 3;
+ } else {
+ pos++;
+ }
+ }
+ return frameType;
+ }
+
+ public static int getStandardizedFrameTypesFromHEVC(ByteBuffer buf) {
+ int limit = buf.limit();
+ byte[] dataArray = new byte[buf.remaining()];
+ buf.get(dataArray);
+ int frameType = -1;
+ for (int pos = 0; pos + 3 < limit; ) {
+ int startOffset = NalUnitUtil.findNalUnit(dataArray, pos, limit);
+ if (startOffset != 0) {
+ int nalUnitType = NalUnitUtil.getHEVCNalUnitType(dataArray, (pos + startOffset));
+ if (DEBUG) {
+ Log.d(TAG, "NalUnitOffset = " + (pos + startOffset));
+ Log.d(TAG, "NalUnitType = " + nalUnitType);
+ }
+ // Parse NALUnits containing slice_headers which lies in the range of 0 to 21
+ if (nalUnitType >= 0 && nalUnitType <= 21) {
+ frameType = parseHEVCNALUnitData(dataArray, (pos + startOffset),
+ (limit - pos - startOffset), nalUnitType);
+ break;
+ }
+ pos += 3;
+ } else {
+ pos++;
+ }
+ }
+ return frameType;
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/ParsableBitArray.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/ParsableBitArray.java
new file mode 100644
index 0000000..e4bfaa3
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/ParsableBitArray.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package com.android.media.samplevideoencoder;
+
+public class ParsableBitArray {
+ public byte[] data;
+ private int byteOffset;
+ private int bitOffset;
+ private int byteLimit;
+
+ public ParsableBitArray(byte[] dataArray) {
+ this(dataArray, dataArray.length);
+ }
+
+ public ParsableBitArray(byte[] dataArray, int limit) {
+ this.data = dataArray;
+ byteLimit = limit;
+ }
+
+ public void reset(byte[] data, int offset, int limit) {
+ this.data = data;
+ byteOffset = offset;
+ bitOffset = 0;
+ byteLimit = limit;
+ }
+
+ public void skipBit() {
+ if (++bitOffset == 8) {
+ bitOffset = 0;
+ byteOffset++;
+ }
+ }
+
+ public void skipBits(int numBits) {
+ int numBytes = numBits / 8;
+ byteOffset += numBytes;
+ bitOffset += numBits - (numBytes * 8);
+ if (bitOffset > 7) {
+ byteOffset++;
+ bitOffset -= 8;
+ }
+ }
+
+ public boolean readBit() {
+ boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0;
+ skipBit();
+ return returnValue;
+ }
+
+ public int readBits(int numBits) {
+ if (numBits == 0) {
+ return 0;
+ }
+ int returnValue = 0;
+ bitOffset += numBits;
+ while (bitOffset > 8) {
+ bitOffset -= 8;
+ returnValue |= (data[byteOffset++] & 0xFF) << bitOffset;
+ }
+ returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset);
+ returnValue &= 0xFFFFFFFF >>> (32 - numBits);
+ if (bitOffset == 8) {
+ bitOffset = 0;
+ byteOffset++;
+ }
+ return returnValue;
+ }
+
+ public boolean canReadUEV() {
+ int initialByteOffset = byteOffset;
+ int initialBitOffset = bitOffset;
+ int leadingZeros = 0;
+ while (byteOffset < byteLimit && !readBit()) {
+ leadingZeros++;
+ }
+ boolean hitLimit = byteOffset == byteLimit;
+ byteOffset = initialByteOffset;
+ bitOffset = initialBitOffset;
+ return !hitLimit && canReadBits(leadingZeros * 2 + 1);
+ }
+
+ public int readUEV() {
+ int leadingZeros = 0;
+ while (!readBit()) {
+ leadingZeros++;
+ }
+ return (1 << leadingZeros) - 1 + (leadingZeros > 0 ? readBits(leadingZeros) : 0);
+ }
+
+ public boolean canReadBits(int numBits) {
+ int oldByteOffset = byteOffset;
+ int numBytes = numBits / 8;
+ int newByteOffset = byteOffset + numBytes;
+ int newBitOffset = bitOffset + numBits - (numBytes * 8);
+ if (newBitOffset > 7) {
+ newByteOffset++;
+ newBitOffset -= 8;
+ }
+ for (int i = oldByteOffset + 1; i <= newByteOffset && newByteOffset < byteLimit; i++) {
+ if (shouldSkipByte(i)) {
+ // Skip the byte and check three bytes ahead.
+ newByteOffset++;
+ i += 2;
+ }
+ }
+ return newByteOffset < byteLimit || (newByteOffset == byteLimit && newBitOffset == 0);
+ }
+
+ private boolean shouldSkipByte(int offset) {
+ return (2 <= offset && offset < byteLimit && data[offset] == (byte) 0x03 &&
+ data[offset - 2] == (byte) 0x00 && data[offset - 1] == (byte) 0x00);
+ }
+
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
index 164e02a..017012d 100644
--- a/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
@@ -124,4 +124,15 @@
</FrameLayout>
+ <TextView
+ android:id="@+id/textViewResults"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:fontFamily="sans-serif-medium"
+ android:textSize="18sp"
+ android:textStyle="normal"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf = "@+id/frameLayout2" />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 26cdc3a..52dc0cf 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -37,6 +37,8 @@
],
static_libs: [
"libc_malloc_debug_backtrace",
+ "libbatterystats_aidl",
+ "libprocessinfoservice_aidl",
],
shared_libs: [
"libaudioclient_aidl_conversion",
@@ -50,6 +52,9 @@
"android.hidl.token@1.0-utils",
"media_permission-aidl-cpp",
],
+ export_static_lib_headers: [
+ "libbatterystats_aidl",
+ ],
logtags: ["EventLogTags.logtags"],
diff --git a/media/utils/ProcessInfo.cpp b/media/utils/ProcessInfo.cpp
index 19225d3..e212794 100644
--- a/media/utils/ProcessInfo.cpp
+++ b/media/utils/ProcessInfo.cpp
@@ -21,9 +21,9 @@
#include <media/stagefright/ProcessInfo.h>
#include <binder/IPCThreadState.h>
-#include <binder/IProcessInfoService.h>
#include <binder/IServiceManager.h>
#include <private/android_filesystem_config.h>
+#include <processinfo/IProcessInfoService.h>
namespace android {
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index 187ef7c..b245834 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -10,6 +10,7 @@
cc_defaults {
name: "libmediautils_fuzzer_defaults",
shared_libs: [
+ "libbatterystats_aidl",
"libbinder",
"libcutils",
"liblog",
diff --git a/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
index 4521853..130feee 100644
--- a/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
+++ b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#define LOG_TAG "BatteryNotifierFuzzer"
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <android/log.h>
diff --git a/media/utils/include/mediautils/BatteryNotifier.h b/media/utils/include/mediautils/BatteryNotifier.h
index a4e42ad..3812d7a 100644
--- a/media/utils/include/mediautils/BatteryNotifier.h
+++ b/media/utils/include/mediautils/BatteryNotifier.h
@@ -17,7 +17,7 @@
#ifndef MEDIA_BATTERY_NOTIFIER_H
#define MEDIA_BATTERY_NOTIFIER_H
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
#include <utils/Singleton.h>
#include <utils/String8.h>
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6678287..7cdac30 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -199,10 +199,21 @@
mDeviceEffectManager(this),
mSystemReady(false)
{
+ // Move the audio session unique ID generator start base as time passes to limit risk of
+ // generating the same ID again after an audioserver restart.
+ // This is important because clients will reuse previously allocated audio session IDs
+ // when reconnecting after an audioserver restart and newly allocated IDs may conflict with
+ // active clients.
+ // Moving the base by 1 for each elapsed second is a good compromise between avoiding overlap
+ // between allocation ranges and not reaching wrap around too soon.
+ timespec ts{};
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ // zero ID has a special meaning, so start allocation at least at AUDIO_UNIQUE_ID_USE_MAX
+ uint32_t sessionBase = (uint32_t)std::max((long)1, ts.tv_sec);
// unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum
for (unsigned use = AUDIO_UNIQUE_ID_USE_UNSPECIFIED; use < AUDIO_UNIQUE_ID_USE_MAX; use++) {
- // zero ID has a special meaning, so unavailable
- mNextUniqueIds[use] = AUDIO_UNIQUE_ID_USE_MAX;
+ mNextUniqueIds[use] =
+ ((use == AUDIO_UNIQUE_ID_USE_SESSION) ? sessionBase : 1) * AUDIO_UNIQUE_ID_USE_MAX;
}
#if 1
@@ -267,6 +278,21 @@
return NO_ERROR;
}
+status_t AudioFlinger::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ Mutex::Autolock _l(mLock);
+ mAudioVibratorInfos = vibratorInfos;
+ return NO_ERROR;
+}
+
+// getDefaultVibratorInfo_l must be called with AudioFlinger lock held.
+const media::AudioVibratorInfo* AudioFlinger::getDefaultVibratorInfo_l() {
+ if (mAudioVibratorInfos.empty()) {
+ return nullptr;
+ }
+ return &mAudioVibratorInfos.front();
+}
+
AudioFlinger::~AudioFlinger()
{
while (!mRecordThreads.isEmpty()) {
@@ -4111,7 +4137,8 @@
case TransactionCode::SET_MIC_MUTE:
case TransactionCode::SET_LOW_RAM_DEVICE:
case TransactionCode::SYSTEM_READY:
- case TransactionCode::SET_AUDIO_HAL_PIDS: {
+ case TransactionCode::SET_AUDIO_HAL_PIDS:
+ case TransactionCode::SET_VIBRATOR_INFOS: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 1cfdffc..a980752 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -267,6 +267,8 @@
virtual status_t setAudioHalPids(const std::vector<pid_t>& pids);
+ virtual status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
+
status_t onPreTransact(TransactionCode code, const Parcel& data, uint32_t flags) override;
// end of IAudioFlinger interface
@@ -296,6 +298,8 @@
void updateDownStreamPatches_l(const struct audio_patch *patch,
const std::set<audio_io_handle_t> streams);
+ const media::AudioVibratorInfo* getDefaultVibratorInfo_l();
+
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;
@@ -971,6 +975,8 @@
SimpleLog mAppSetParameterLog;
SimpleLog mSystemSetParameterLog;
+ std::vector<media::AudioVibratorInfo> mAudioVibratorInfos;
+
static inline constexpr const char *mMetricsId = AMEDIAMETRICS_KEY_AUDIO_FLINGER;
};
diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp
index 7e06096..d8565bd 100644
--- a/services/audioflinger/AudioStreamOut.cpp
+++ b/services/audioflinger/AudioStreamOut.cpp
@@ -173,22 +173,15 @@
return status;
}
-audio_format_t AudioStreamOut::getFormat() const
+audio_config_base_t AudioStreamOut::getAudioProperties() const
{
- audio_format_t result;
- return stream->getFormat(&result) == OK ? result : AUDIO_FORMAT_INVALID;
-}
-
-uint32_t AudioStreamOut::getSampleRate() const
-{
- uint32_t result;
- return stream->getSampleRate(&result) == OK ? result : 0;
-}
-
-audio_channel_mask_t AudioStreamOut::getChannelMask() const
-{
- audio_channel_mask_t result;
- return stream->getChannelMask(&result) == OK ? result : AUDIO_CHANNEL_INVALID;
+ audio_config_base_t result = AUDIO_CONFIG_BASE_INITIALIZER;
+ if (stream->getAudioProperties(&result) != OK) {
+ result.sample_rate = 0;
+ result.channel_mask = AUDIO_CHANNEL_INVALID;
+ result.format = AUDIO_FORMAT_INVALID;
+ }
+ return result;
}
int AudioStreamOut::flush()
diff --git a/services/audioflinger/AudioStreamOut.h b/services/audioflinger/AudioStreamOut.h
index 16fbcf2..565f43a 100644
--- a/services/audioflinger/AudioStreamOut.h
+++ b/services/audioflinger/AudioStreamOut.h
@@ -81,22 +81,14 @@
virtual size_t getFrameSize() const { return mHalFrameSize; }
/**
- * @return format from the perspective of the application and the AudioFlinger.
+ * @return audio stream configuration: channel mask, format, sample rate:
+ * - channel mask from the perspective of the application and the AudioFlinger,
+ * The HAL is in stereo mode when playing multi-channel compressed audio over HDMI;
+ * - format from the perspective of the application and the AudioFlinger;
+ * - sample rate from the perspective of the application and the AudioFlinger,
+ * The HAL may be running at a higher sample rate if, for example, playing wrapped EAC3.
*/
- virtual audio_format_t getFormat() const;
-
- /**
- * The HAL may be running at a higher sample rate if, for example, playing wrapped EAC3.
- * @return sample rate from the perspective of the application and the AudioFlinger.
- */
- virtual uint32_t getSampleRate() const;
-
- /**
- * The HAL is in stereo mode when playing multi-channel compressed audio over HDMI.
- * @return channel mask from the perspective of the application and the AudioFlinger.
- */
- virtual audio_channel_mask_t getChannelMask() const;
-
+ virtual audio_config_base_t getAudioProperties() const;
virtual status_t flush();
virtual status_t standby();
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 031e0cf..d75b13b 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1585,6 +1585,34 @@
return status;
}
+status_t AudioFlinger::EffectModule::setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ if (!isHapticGenerator()) {
+ ALOGW("Should not set vibrator info for effects that are not HapticGenerator");
+ return INVALID_OPERATION;
+ }
+
+ std::vector<uint8_t> request(
+ sizeof(effect_param_t) + sizeof(int32_t) + 2 * sizeof(float));
+ effect_param_t *param = (effect_param_t*) request.data();
+ param->psize = sizeof(int32_t);
+ param->vsize = 2 * sizeof(float);
+ *(int32_t*)param->data = HG_PARAM_VIBRATOR_INFO;
+ float* vibratorInfoPtr = reinterpret_cast<float*>(param->data + sizeof(int32_t));
+ vibratorInfoPtr[0] = vibratorInfo->resonantFrequency;
+ vibratorInfoPtr[1] = vibratorInfo->qFactor;
+ std::vector<uint8_t> response;
+ status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
+ if (status == NO_ERROR) {
+ LOG_ALWAYS_FATAL_IF(response.size() != sizeof(status_t));
+ status = *reinterpret_cast<const status_t*>(response.data());
+ }
+ return status;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 8e82d53..9da95bc 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -258,6 +258,7 @@
bool isHapticGenerator() const;
status_t setHapticIntensity(int id, int intensity);
+ status_t setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo);
void dump(int fd, const Vector<String16>& args);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index ca9b747..2e59baa 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -336,7 +336,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- android::media::permission::Identity& identity);
+ const android::media::permission::Identity& identity);
virtual ~OutputTrack();
virtual status_t start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 4d03441..5f248e1 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -147,7 +147,6 @@
// used to enforce OP_RECORD_AUDIO
uid_t mUid;
- media::permission::Identity mIdentity;
sp<OpRecordAudioMonitor> mOpRecordAudioMonitor;
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7f91a54..c83fc80 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1439,6 +1439,16 @@
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
}
+ if (effect->isHapticGenerator()) {
+ // TODO(b/184194057): Use the vibrator information from the vibrator that will be used
+ // for the HapticGenerator.
+ const media::AudioVibratorInfo* defaultVibratorInfo =
+ mAudioFlinger->getDefaultVibratorInfo_l();
+ if (defaultVibratorInfo != nullptr) {
+ // Only set the vibrator info when it is a valid one.
+ effect->setVibratorInfo(defaultVibratorInfo);
+ }
+ }
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
lStatus = handle->initCheck();
@@ -2757,8 +2767,9 @@
void AudioFlinger::PlaybackThread::readOutputParameters_l()
{
// unfortunately we have no way of recovering from errors here, hence the LOG_ALWAYS_FATAL
- mSampleRate = mOutput->getSampleRate();
- mChannelMask = mOutput->getChannelMask();
+ const audio_config_base_t audioConfig = mOutput->getAudioProperties();
+ mSampleRate = audioConfig.sample_rate;
+ mChannelMask = audioConfig.channel_mask;
if (!audio_is_output_channel(mChannelMask)) {
LOG_ALWAYS_FATAL("HAL channel mask %#x not valid for output", mChannelMask);
}
@@ -2771,11 +2782,11 @@
mBalance.setChannelMask(mChannelMask);
// Get actual HAL format.
- status_t result = mOutput->stream->getFormat(&mHALFormat);
+ status_t result = mOutput->stream->getAudioProperties(nullptr, nullptr, &mHALFormat);
LOG_ALWAYS_FATAL_IF(result != OK, "Error when retrieving output stream format: %d", result);
// Get format from the shim, which will be different than the HAL format
// if playing compressed audio over HDMI passthrough.
- mFormat = mOutput->getFormat();
+ mFormat = audioConfig.format;
if (!audio_is_valid_format(mFormat)) {
LOG_ALWAYS_FATAL("HAL format %#x not valid for output", mFormat);
}
@@ -8424,13 +8435,11 @@
}
if (reconfig) {
if (status == BAD_VALUE) {
- uint32_t sRate;
- audio_channel_mask_t channelMask;
- audio_format_t format;
- if (mInput->stream->getAudioProperties(&sRate, &channelMask, &format) == OK &&
- audio_is_linear_pcm(format) && audio_is_linear_pcm(reqFormat) &&
- sRate <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate) &&
- audio_channel_count_from_in_mask(channelMask) <= FCC_8) {
+ audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+ if (mInput->stream->getAudioProperties(&config) == OK &&
+ audio_is_linear_pcm(config.format) && audio_is_linear_pcm(reqFormat) &&
+ config.sample_rate <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate) &&
+ audio_channel_count_from_in_mask(config.channel_mask) <= FCC_8) {
status = NO_ERROR;
}
}
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index efcdb51..db7528d 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -239,9 +239,10 @@
}
// TODO b/182392769: use identity util
-Identity audioServerIdentity() {
- Identity i = Identity();
+static Identity audioServerIdentity(pid_t pid) {
+ Identity i{};
i.uid = AID_AUDIOSERVER;
+ i.pid = pid;
return i;
}
@@ -1846,7 +1847,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- Identity& identity)
+ const Identity& identity)
: Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
audio_attributes_t{} /* currently unused for output track */,
sampleRate, format, channelMask, frameCount,
@@ -2081,7 +2082,7 @@
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
- AUDIO_SESSION_NONE, getpid(), audioServerIdentity(), flags,
+ AUDIO_SESSION_NONE, getpid(), audioServerIdentity(getpid()), flags,
TYPE_PATCH, AUDIO_PORT_HANDLE_NONE, frameCountToBeReady),
PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
*playbackThread, timeout)
@@ -2414,7 +2415,7 @@
mRecordBufferConverter(NULL),
mFlags(flags),
mSilenced(false),
- mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(mIdentity, attr))
+ mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(identity, attr))
{
if (mCblk == NULL) {
return;
@@ -2455,9 +2456,7 @@
#endif
// Once this item is logged by the server, the client can add properties.
- pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid));
- uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid));
- mTrackMetrics.logConstructor(pid, uid, id());
+ mTrackMetrics.logConstructor(creatorPid, uid(), id());
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -2729,11 +2728,10 @@
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, AUDIO_SESSION_NONE, getpid(),
- audioServerIdentity(), flags, TYPE_PATCH),
+ audioServerIdentity(getpid()), flags, TYPE_PATCH),
PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
*recordThread, timeout)
{
- mIdentity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
__func__, mId, sampleRate,
(int)mPeerTimeout.tv_sec,
@@ -3023,8 +3021,7 @@
mSilenced(false), mSilencedNotified(false)
{
// Once this item is logged by the server, the client can add properties.
- mTrackMetrics.logConstructor(creatorPid,
- VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)), id());
+ mTrackMetrics.logConstructor(creatorPid, uid(), id());
}
AudioFlinger::MmapThread::MmapTrack::~MmapTrack()
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index c6bdb04..c2a20c6 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -77,6 +77,7 @@
sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
const DeviceVector &availableDeviceTypes,
+ uid_t uid,
sp<AudioPolicyMix> *policyMix) const;
/**
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 05ec69e..20b4044 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -226,7 +226,9 @@
add(devices);
return size();
}
- return SortedVector::merge(devices);
+ ssize_t ret = SortedVector::merge(devices);
+ refreshTypes();
+ return ret;
}
/**
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index c024a85..b209a88 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -391,6 +391,7 @@
sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
audio_source_t inputSource,
const DeviceVector &availDevices,
+ uid_t uid,
sp<AudioPolicyMix> *policyMix) const
{
for (size_t i = 0; i < size(); i++) {
@@ -402,7 +403,11 @@
if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
mix->mCriteria[j].mValue.mSource == inputSource) ||
(RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mValue.mSource != inputSource)) {
+ mix->mCriteria[j].mValue.mSource != inputSource) ||
+ (RULE_MATCH_UID == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mValue.mUid == uid) ||
+ (RULE_EXCLUDE_UID == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mValue.mUid != uid)) {
// assuming PolicyMix only for remote submix for input
// so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX
audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 9bef97c..0f8b0a5 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -170,11 +170,13 @@
status_t getMediaDevicesForRole(device_role_t role, const DeviceVector& availableDevices,
DeviceVector& devices) const;
+ void dumpCapturePresetDevicesRoleMap(String8 *dst, int spaces) const;
+
AudioPolicyManagerObserver *mApmObserver = nullptr;
ProductStrategyMap mProductStrategies;
- ProductStrategyPreferredRoutingMap mProductStrategyPreferredDevices;
- CapturePresetDevicesRoleMap mCapturePresetDevicesRole;
+ ProductStrategyDevicesRoleMap mProductStrategyDeviceRoleMap;
+ CapturePresetDevicesRoleMap mCapturePresetDevicesRoleMap;
VolumeGroupMap mVolumeGroups;
LastRemovableMediaDevices mLastRemovableMediaDevices;
audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */
diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h
index 54625ea..2aa2f9a 100644
--- a/services/audiopolicy/engine/common/include/ProductStrategy.h
+++ b/services/audiopolicy/engine/common/include/ProductStrategy.h
@@ -18,20 +18,20 @@
#include "VolumeGroup.h"
-#include <system/audio.h>
-#include <utils/RefBase.h>
-#include <HandleGenerator.h>
-#include <string>
-#include <vector>
#include <map>
-#include <utils/Errors.h>
-#include <utils/String8.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <HandleGenerator.h>
#include <media/AudioAttributes.h>
#include <media/AudioContainers.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioPolicy.h>
-
-#include <vector>
+#include <system/audio.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
namespace android {
@@ -170,11 +170,12 @@
product_strategy_t mDefaultStrategy = PRODUCT_STRATEGY_NONE;
};
-class ProductStrategyPreferredRoutingMap : public std::map<product_strategy_t,
- AudioDeviceTypeAddrVector>
-{
-public:
- void dump(String8 *dst, int spaces = 0) const;
-};
+using ProductStrategyDevicesRoleMap =
+ std::map<std::pair<product_strategy_t, device_role_t>, AudioDeviceTypeAddrVector>;
+
+void dumpProductStrategyDevicesRoleMap(
+ const ProductStrategyDevicesRoleMap& productStrategyDeviceRoleMap,
+ String8 *dst,
+ int spaces);
} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 37e4caa..150a9a8 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -17,6 +17,10 @@
#define LOG_TAG "APM::AudioPolicyEngine/Base"
//#define LOG_NDEBUG 0
+#include <functional>
+#include <string>
+#include <sys/stat.h>
+
#include "EngineBase.h"
#include "EngineDefaultConfig.h"
#include <TypeConverter.h>
@@ -148,10 +152,15 @@
});
return iter != end(volumeGroups);
};
+ auto fileExists = [](const char* path) {
+ struct stat fileStat;
+ return stat(path, &fileStat) == 0 && S_ISREG(fileStat.st_mode);
+ };
- auto result = engineConfig::parse();
+ auto result = fileExists(engineConfig::DEFAULT_PATH) ?
+ engineConfig::parse(engineConfig::DEFAULT_PATH) : engineConfig::ParsingResult{};
if (result.parsedConfig == nullptr) {
- ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
+ ALOGD("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
engineConfig::Config config = gDefaultEngineConfig;
android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
result = {std::make_unique<engineConfig::Config>(config),
@@ -342,23 +351,33 @@
return NO_ERROR;
}
-status_t EngineBase::setDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s invalid strategy %u", __func__, strategy);
+namespace {
+template <typename T>
+status_t setDevicesRoleForT(
+ std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, const AudioDeviceTypeAddrVector &devices,
+ const std::string& logStr, std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
case DEVICE_ROLE_PREFERRED:
- mProductStrategyPreferredDevices[strategy] = devices;
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support set devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
+ case DEVICE_ROLE_DISABLED: {
+ tDevicesRoleMap[std::make_pair(t, role)] = devices;
+ // The preferred devices and disabled devices are mutually exclusive. Once a device is added
+ // the a list, it must be removed from the other one.
+ const device_role_t roleToRemove = role == DEVICE_ROLE_PREFERRED ? DEVICE_ROLE_DISABLED
+ : DEVICE_ROLE_PREFERRED;
+ auto it = tDevicesRoleMap.find(std::make_pair(t, roleToRemove));
+ if (it != tDevicesRoleMap.end()) {
+ it->second = excludeDeviceTypeAddrsFrom(it->second, devices);
+ if (it->second.empty()) {
+ tDevicesRoleMap.erase(it);
+ }
+ }
+ } break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it is no need to set device role as none for a strategy.
default:
@@ -368,28 +387,26 @@
return NO_ERROR;
}
-status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s invalid strategy %u", __func__, strategy);
+template <typename T>
+status_t removeAllDevicesRoleForT(
+ std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, const std::string& logStr, std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
case DEVICE_ROLE_PREFERRED:
- if (mProductStrategyPreferredDevices.erase(strategy) == 0) {
- // no preferred device was set
+ case DEVICE_ROLE_DISABLED:
+ if (tDevicesRoleMap.erase(std::make_pair(t, role)) == 0) {
+ // no preferred/disabled device was set
return NAME_NOT_FOUND;
}
break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support remove devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it makes no sense to remove devices with
- // role as DEVICE_ROLE_NONE for a strategy
+ // role as DEVICE_ROLE_NONE
default:
ALOGE("%s invalid role %d", __func__, role);
return BAD_VALUE;
@@ -397,25 +414,26 @@
return NO_ERROR;
}
-status_t EngineBase::getDevicesForRoleAndStrategy(product_strategy_t strategy, device_role_t role,
- AudioDeviceTypeAddrVector &devices) const
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s unknown strategy %u", __func__, strategy);
+template <typename T>
+status_t getDevicesRoleForT(
+ const std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, AudioDeviceTypeAddrVector &devices, const std::string& logStr,
+ std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
- case DEVICE_ROLE_PREFERRED: {
- // preferred device for this strategy?
- auto devIt = mProductStrategyPreferredDevices.find(strategy);
- if (devIt == mProductStrategyPreferredDevices.end()) {
- ALOGV("%s no preferred device for strategy %u", __func__, strategy);
+ case DEVICE_ROLE_PREFERRED:
+ case DEVICE_ROLE_DISABLED: {
+ auto it = tDevicesRoleMap.find(std::make_pair(t, role));
+ if (it == tDevicesRoleMap.end()) {
+ ALOGV("%s no device as role %u for %s %u", __func__, role, logStr.c_str(), t);
return NAME_NOT_FOUND;
}
- devices = devIt->second;
+ devices = it->second;
} break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as the DEVICE_ROLE_NONE is never set
@@ -426,32 +444,45 @@
return NO_ERROR;
}
+} // namespace
+
+status_t EngineBase::setDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
+ const AudioDeviceTypeAddrVector &devices)
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return setDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
+}
+
+status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return removeAllDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, "strategy" /*logStr*/, p);
+}
+
+status_t EngineBase::getDevicesForRoleAndStrategy(product_strategy_t strategy, device_role_t role,
+ AudioDeviceTypeAddrVector &devices) const
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return getDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
+}
+
status_t EngineBase::setDevicesRoleForCapturePreset(audio_source_t audioSource, device_role_t role,
const AudioDeviceTypeAddrVector &devices)
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- }
-
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- mCapturePresetDevicesRole[audioSource][role] = devices;
- // When the devices are set as preferred devices, remove them from the disabled devices.
- doRemoveDevicesRoleForCapturePreset(
- audioSource, DEVICE_ROLE_DISABLED, devices, false /*forceMatched*/);
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support setting devices role as disabled for capture preset.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as it is no need to set device role as none
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
- }
- return NO_ERROR;
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return setDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, devices, "audio source" /*logStr*/, p);
}
status_t EngineBase::addDevicesRoleForCapturePreset(audio_source_t audioSource, device_role_t role,
@@ -464,19 +495,20 @@
switch (role) {
case DEVICE_ROLE_PREFERRED:
- mCapturePresetDevicesRole[audioSource][role] = excludeDeviceTypeAddrsFrom(
- mCapturePresetDevicesRole[audioSource][role], devices);
- for (const auto& device : devices) {
- mCapturePresetDevicesRole[audioSource][role].push_back(device);
+ case DEVICE_ROLE_DISABLED: {
+ const auto audioSourceRole = std::make_pair(audioSource, role);
+ mCapturePresetDevicesRoleMap[audioSourceRole] = excludeDeviceTypeAddrsFrom(
+ mCapturePresetDevicesRoleMap[audioSourceRole], devices);
+ for (const auto &device : devices) {
+ mCapturePresetDevicesRoleMap[audioSourceRole].push_back(device);
}
// When the devices are set as preferred devices, remove them from the disabled devices.
doRemoveDevicesRoleForCapturePreset(
- audioSource, DEVICE_ROLE_DISABLED, devices, false /*forceMatched*/);
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support setting devices role as disabled for capture preset.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
+ audioSource,
+ role == DEVICE_ROLE_PREFERRED ? DEVICE_ROLE_DISABLED : DEVICE_ROLE_PREFERRED,
+ devices,
+ false /*forceMatched*/);
+ } break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it is no need to set device role as none
default:
@@ -502,21 +534,22 @@
switch (role) {
case DEVICE_ROLE_PREFERRED:
case DEVICE_ROLE_DISABLED: {
- if (mCapturePresetDevicesRole.count(audioSource) == 0 ||
- mCapturePresetDevicesRole[audioSource].count(role) == 0) {
+ const auto audioSourceRole = std::make_pair(audioSource, role);
+ if (mCapturePresetDevicesRoleMap.find(audioSourceRole) ==
+ mCapturePresetDevicesRoleMap.end()) {
return NAME_NOT_FOUND;
}
AudioDeviceTypeAddrVector remainingDevices = excludeDeviceTypeAddrsFrom(
- mCapturePresetDevicesRole[audioSource][role], devices);
+ mCapturePresetDevicesRoleMap[audioSourceRole], devices);
if (forceMatched && remainingDevices.size() !=
- mCapturePresetDevicesRole[audioSource][role].size() - devices.size()) {
+ mCapturePresetDevicesRoleMap[audioSourceRole].size() - devices.size()) {
// There are some devices from `devicesToRemove` that are not shown in the cached record
return BAD_VALUE;
}
- mCapturePresetDevicesRole[audioSource][role] = remainingDevices;
- if (mCapturePresetDevicesRole[audioSource][role].empty()) {
+ mCapturePresetDevicesRoleMap[audioSourceRole] = remainingDevices;
+ if (mCapturePresetDevicesRoleMap[audioSourceRole].empty()) {
// Remove the role when device list is empty
- mCapturePresetDevicesRole[audioSource].erase(role);
+ mCapturePresetDevicesRoleMap.erase(audioSourceRole);
}
} break;
case DEVICE_ROLE_NONE:
@@ -532,63 +565,21 @@
status_t EngineBase::clearDevicesRoleForCapturePreset(audio_source_t audioSource,
device_role_t role)
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- }
-
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- if (mCapturePresetDevicesRole.count(audioSource) == 0 ||
- mCapturePresetDevicesRole[audioSource].erase(role) == 0) {
- // no preferred device for the given audio source
- return NAME_NOT_FOUND;
- }
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support remove devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as it makes no sense to remove devices with
- // role as DEVICE_ROLE_NONE for a strategy
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
- }
- return NO_ERROR;
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return removeAllDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, "audio source" /*logStr*/, p);
}
status_t EngineBase::getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
device_role_t role, AudioDeviceTypeAddrVector &devices) const
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- return BAD_VALUE;
- }
-
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- case DEVICE_ROLE_DISABLED: {
- if (mCapturePresetDevicesRole.count(audioSource) == 0) {
- return NAME_NOT_FOUND;
- }
- auto devIt = mCapturePresetDevicesRole.at(audioSource).find(role);
- if (devIt == mCapturePresetDevicesRole.at(audioSource).end()) {
- ALOGV("%s no devices role(%d) for capture preset %u", __func__, role, audioSource);
- return NAME_NOT_FOUND;
- }
-
- devices = devIt->second;
- } break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as the DEVICE_ROLE_NONE is never set
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
- }
- return NO_ERROR;
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return getDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, devices, "audio source" /*logStr*/, p);
}
status_t EngineBase::getMediaDevicesForRole(device_role_t role,
@@ -630,10 +621,22 @@
return activeDevices;
}
+void EngineBase::dumpCapturePresetDevicesRoleMap(String8 *dst, int spaces) const
+{
+ dst->appendFormat("\n%*sDevice role per capture preset dump:", spaces, "");
+ for (const auto& [capturePresetRolePair, devices] : mCapturePresetDevicesRoleMap) {
+ dst->appendFormat("\n%*sCapture preset(%u) Device Role(%u) Devices(%s)", spaces + 2, "",
+ capturePresetRolePair.first, capturePresetRolePair.second,
+ dumpAudioDeviceTypeAddrVector(devices, true /*includeSensitiveInfo*/).c_str());
+ }
+ dst->appendFormat("\n");
+}
+
void EngineBase::dump(String8 *dst) const
{
mProductStrategies.dump(dst, 2);
- mProductStrategyPreferredDevices.dump(dst, 2);
+ dumpProductStrategyDevicesRoleMap(mProductStrategyDeviceRoleMap, dst, 2);
+ dumpCapturePresetDevicesRoleMap(dst, 2);
mVolumeGroups.dump(dst, 2);
}
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
index d4cea5a..b3d144f 100644
--- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -320,14 +320,15 @@
}
}
-void ProductStrategyPreferredRoutingMap::dump(android::String8* dst, int spaces) const {
- dst->appendFormat("\n%*sPreferred devices per product strategy dump:", spaces, "");
- for (const auto& iter : *this) {
- dst->appendFormat("\n%*sStrategy %u %s",
- spaces + 2, "",
- (uint32_t) iter.first,
- dumpAudioDeviceTypeAddrVector(iter.second, true /*includeSensitiveInfo*/)
- .c_str());
+void dumpProductStrategyDevicesRoleMap(
+ const ProductStrategyDevicesRoleMap& productStrategyDeviceRoleMap,
+ String8 *dst,
+ int spaces) {
+ dst->appendFormat("\n%*sDevice role per product strategy dump:", spaces, "");
+ for (const auto& [strategyRolePair, devices] : productStrategyDeviceRoleMap) {
+ dst->appendFormat("\n%*sStrategy(%u) Device Role(%u) Devices(%s)", spaces + 2, "",
+ strategyRolePair.first, strategyRolePair.second,
+ dumpAudioDeviceTypeAddrVector(devices, true /*includeSensitiveInfo*/).c_str());
}
dst->appendFormat("\n");
}
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 7cfef5b..1c86051 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -643,7 +643,11 @@
xmlDocPtr doc;
doc = xmlParseFile(path);
if (doc == NULL) {
- ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
+ // It is OK not to find an engine config file at the default location
+ // as the caller will default to hardcoded default config
+ if (strncmp(path, DEFAULT_PATH, strlen(DEFAULT_PATH))) {
+ ALOGW("%s: Could not parse document %s", __FUNCTION__, path);
+ }
return {nullptr, 0};
}
xmlNodePtr cur = xmlDocGetRootElement(doc);
diff --git a/services/audiopolicy/engine/interface/EngineInterface.h b/services/audiopolicy/engine/interface/EngineInterface.h
index f0a01d3..518f86e 100644
--- a/services/audiopolicy/engine/interface/EngineInterface.h
+++ b/services/audiopolicy/engine/interface/EngineInterface.h
@@ -16,6 +16,8 @@
#pragma once
+#include <utility>
+
#include <AudioPolicyManagerObserver.h>
#include <media/AudioProductStrategy.h>
#include <media/AudioVolumeGroup.h>
@@ -35,7 +37,7 @@
using StrategyVector = std::vector<product_strategy_t>;
using VolumeGroupVector = std::vector<volume_group_t>;
using CapturePresetDevicesRoleMap =
- std::map<audio_source_t, std::map<device_role_t, AudioDeviceTypeAddrVector>>;
+ std::map<std::pair<audio_source_t, device_role_t>, AudioDeviceTypeAddrVector>;
/**
* This interface is dedicated to the policy manager that a Policy Engine shall implement.
@@ -171,8 +173,10 @@
* @param[out] mix to be used if a mix has been installed for the given audio attributes.
* @return selected input device for the audio attributes, may be null if error.
*/
- virtual sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const = 0;
+ virtual sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const = 0;
/**
* Get the legacy stream type for a given audio attributes.
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 6d42fcf..b0c376a 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -310,6 +310,7 @@
}
sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid,
sp<AudioPolicyMix> *mix) const
{
const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
@@ -328,7 +329,10 @@
return device;
}
- device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix);
+ device = policyMixes.getDeviceAndMixForInputSource(attr.source,
+ availableInputDevices,
+ uid,
+ mix);
if (device != nullptr) {
return device;
}
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index 3b371d8..d8e2742 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -61,8 +61,10 @@
DeviceVector getOutputDevicesForStream(audio_stream_type_t stream,
bool fromCache = false) const override;
- sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const override;
+ sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const override;
void updateDeviceSelectionCache() override;
diff --git a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
index 5083b14..43b3dd2 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
@@ -172,12 +172,6 @@
logging.info("added stub input device mask")
# Transform input source in inclusive criterion
- shift = len(all_component_types['OutputDevicesMask'])
- if shift > 32:
- logging.critical("OutputDevicesMask incompatible with criterion representation on 32 bits")
- logging.info("EXIT ON FAILURE")
- exit(1)
-
for component_types in all_component_types:
values = ','.join('{}:{}'.format(value, key) for key, value in all_component_types[component_types].items())
logging.info("{}: <{}>".format(component_types, values))
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 380bf6b..27f89e3 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -144,9 +144,10 @@
void Engine::filterOutputDevicesForStrategy(legacy_strategy strategy,
DeviceVector& availableOutputDevices,
- const DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs) const
{
+ DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
+
switch (strategy) {
case STRATEGY_SONIFICATION_RESPECTFUL: {
if (!(isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
@@ -204,9 +205,49 @@
}
}
+product_strategy_t Engine::remapStrategyFromContext(product_strategy_t strategy,
+ const SwAudioOutputCollection &outputs) const {
+ auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
+ mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
+
+ if (isInCall()) {
+ switch (legacyStrategy) {
+ case STRATEGY_ACCESSIBILITY:
+ case STRATEGY_DTMF:
+ case STRATEGY_MEDIA:
+ case STRATEGY_SONIFICATION:
+ case STRATEGY_SONIFICATION_RESPECTFUL:
+ legacyStrategy = STRATEGY_PHONE;
+ break;
+
+ default:
+ return strategy;
+ }
+ } else {
+ switch (legacyStrategy) {
+ case STRATEGY_SONIFICATION_RESPECTFUL:
+ case STRATEGY_SONIFICATION:
+ if (outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
+ legacyStrategy = STRATEGY_PHONE;
+ }
+ break;
+
+ case STRATEGY_ACCESSIBILITY:
+ if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
+ outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
+ legacyStrategy = STRATEGY_SONIFICATION;
+ }
+ break;
+
+ default:
+ return strategy;
+ }
+ }
+ return getProductStrategyFromLegacy(legacyStrategy);
+}
+
DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
DeviceVector availableOutputDevices,
- DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs) const
{
DeviceVector devices;
@@ -217,32 +258,6 @@
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
break;
- case STRATEGY_SONIFICATION_RESPECTFUL:
- if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
- devices = getDevicesForStrategyInt(
- STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
- } else {
- bool media_active_locally =
- outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
- SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
- || outputs.isActiveLocally(
- toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
- SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
- devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
- availableOutputDevices,
- availableInputDevices, outputs);
- // if no media is playing on the device, check for mandatory use of "safe" speaker
- // when media would have played on speaker, and the safe speaker path is available
- if (!media_active_locally) {
- devices.replaceDevicesByType(
- AUDIO_DEVICE_OUT_SPEAKER,
- availableOutputDevices.getDevicesFromType(
- AUDIO_DEVICE_OUT_SPEAKER_SAFE));
- }
- }
- break;
-
- case STRATEGY_DTMF:
case STRATEGY_PHONE: {
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
if (!devices.isEmpty()) break;
@@ -257,16 +272,6 @@
} break;
case STRATEGY_SONIFICATION:
-
- // If incall, just select the STRATEGY_PHONE device
- if (isInCall() ||
- outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
- devices = getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- break;
- }
- FALLTHROUGH_INTENDED;
-
case STRATEGY_ENFORCED_AUDIBLE:
// strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION
// except:
@@ -314,22 +319,9 @@
// The second device used for sonification is the same as the device used by media strategy
FALLTHROUGH_INTENDED;
+ case STRATEGY_DTMF:
case STRATEGY_ACCESSIBILITY:
- if (strategy == STRATEGY_ACCESSIBILITY) {
- if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
- outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
- return getDevicesForStrategyInt(
- STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
- }
- if (isInCall()) {
- return getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- }
- }
- // For other cases, STRATEGY_ACCESSIBILITY behaves like STRATEGY_MEDIA
- FALLTHROUGH_INTENDED;
-
- // FIXME: STRATEGY_REROUTING follow STRATEGY_MEDIA for now
+ case STRATEGY_SONIFICATION_RESPECTFUL:
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {
DeviceVector devices2;
@@ -342,11 +334,6 @@
devices2.add(remoteSubmix);
}
}
- if (isInCall() && (strategy == STRATEGY_MEDIA)) {
- devices = getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- break;
- }
if ((devices2.isEmpty()) &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
@@ -394,9 +381,19 @@
devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
}
- // for STRATEGY_SONIFICATION:
+ bool mediaActiveLocally =
+ outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
+ SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+ || outputs.isActiveLocally(
+ toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
+ SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+ // - for STRATEGY_SONIFICATION:
// if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead
- if (strategy == STRATEGY_SONIFICATION) {
+ // - for STRATEGY_SONIFICATION_RESPECTFUL:
+ // if no media is playing on the device, check for mandatory use of "safe" speaker
+ // when media would have played on speaker, and the safe speaker path is available
+ if (strategy == STRATEGY_SONIFICATION
+ || (strategy == STRATEGY_SONIFICATION_RESPECTFUL && !mediaActiveLocally)) {
devices.replaceDevicesByType(
AUDIO_DEVICE_OUT_SPEAKER,
availableOutputDevices.getDevicesFromType(
@@ -649,22 +646,20 @@
return preferredAvailableDevVec;
}
+
DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t strategy) const {
- DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
+ const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
+
+ // Take context into account to remap product strategy before
+ // checking preferred device for strategy and applying default routing rules
+ strategy = remapStrategyFromContext(strategy, outputs);
+
auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
- // When not in call, STRATEGY_DTMF follows STRATEGY_MEDIA
- if (!isInCall() && legacyStrategy == STRATEGY_DTMF) {
- legacyStrategy = STRATEGY_MEDIA;
- strategy = getProductStrategyFromLegacy(STRATEGY_MEDIA);
- }
+ DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
- DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
- const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
-
- filterOutputDevicesForStrategy(legacyStrategy, availableOutputDevices,
- availableInputDevices, outputs);
+ filterOutputDevicesForStrategy(legacyStrategy, availableOutputDevices, outputs);
// check if this strategy has a preferred device that is available,
// if yes, give priority to it.
@@ -676,7 +671,7 @@
return getDevicesForStrategyInt(legacyStrategy,
availableOutputDevices,
- availableInputDevices, outputs);
+ outputs);
}
DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
@@ -712,6 +707,7 @@
}
sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid,
sp<AudioPolicyMix> *mix) const
{
const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
@@ -731,7 +727,10 @@
return device;
}
- device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix);
+ device = policyMixes.getDeviceAndMixForInputSource(attr.source,
+ availableInputDevices,
+ uid,
+ mix);
if (device != nullptr) {
return device;
}
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 6dc6cd0..595e289 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -62,8 +62,10 @@
DeviceVector getOutputDevicesForStream(audio_stream_type_t stream,
bool fromCache = false) const override;
- sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const override;
+ sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const override;
void updateDeviceSelectionCache() override;
@@ -76,12 +78,13 @@
void filterOutputDevicesForStrategy(legacy_strategy strategy,
DeviceVector& availableOutputDevices,
- const DeviceVector availableInputDevices,
+ const SwAudioOutputCollection &outputs) const;
+
+ product_strategy_t remapStrategyFromContext(product_strategy_t strategy,
const SwAudioOutputCollection &outputs) const;
DeviceVector getDevicesForStrategyInt(legacy_strategy strategy,
DeviceVector availableOutputDevices,
- DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs) const;
DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2b9f8d7..dd44c54 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -284,7 +284,7 @@
setOutputDevices(desc, newDevices, force, 0);
}
if (!desc->isDuplicated() && desc->mProfile->hasDynamicAudioProfile() &&
- desc->devices() != activeMediaDevices &&
+ !activeMediaDevices.empty() && desc->devices() != activeMediaDevices &&
desc->supportsDevicesForPlayback(activeMediaDevices)) {
// Reopen the output to query the dynamic profiles when there is not active
// clients or all active clients will be rerouted. Otherwise, set the flag
@@ -2228,7 +2228,8 @@
} else {
// Prevent from storing invalid requested device id in clients
requestedDeviceId = AUDIO_PORT_HANDLE_NONE;
- device = mEngine->getInputDeviceForAttributes(attributes, &policyMix);
+ device = mEngine->getInputDeviceForAttributes(attributes, uid, &policyMix);
+ ALOGV("%s found device type is 0x%X", __FUNCTION__, device->type());
}
if (device == nullptr) {
ALOGW("getInputForAttr() could not find device for source %d", attributes.source);
@@ -2614,7 +2615,7 @@
bool close = false;
for (const auto& client : input->clientsList()) {
sp<DeviceDescriptor> device =
- mEngine->getInputDeviceForAttributes(client->attributes());
+ mEngine->getInputDeviceForAttributes(client->attributes(), client->uid());
if (!input->supportedDevices().contains(device)) {
close = true;
break;
@@ -5858,12 +5859,22 @@
// If we are not in call and no client is active on this input, this methods returns
// a null sp<>, causing the patch on the input stream to be released.
- audio_attributes_t attributes = inputDesc->getHighestPriorityAttributes();
+ audio_attributes_t attributes;
+ uid_t uid;
+ sp<RecordClientDescriptor> topClient = inputDesc->getHighestPriorityClient();
+ if (topClient != nullptr) {
+ attributes = topClient->attributes();
+ uid = topClient->uid();
+ } else {
+ attributes = { .source = AUDIO_SOURCE_DEFAULT };
+ uid = 0;
+ }
+
if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) {
attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
}
if (attributes.source != AUDIO_SOURCE_DEFAULT) {
- device = mEngine->getInputDeviceForAttributes(attributes);
+ device = mEngine->getInputDeviceForAttributes(attributes, uid);
}
return device;
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index d5ba756..14be671 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -23,6 +23,7 @@
],
shared_libs: [
+ "libactivitymanager_aidl",
"libaudioclient",
"libaudioclient_aidl_conversion",
"libaudiofoundation",
@@ -67,6 +68,7 @@
],
export_shared_lib_headers: [
+ "libactivitymanager_aidl",
"libsensorprivacy",
"media_permission-aidl-cpp",
],
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index a0b35a8..32c0267 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -113,6 +113,7 @@
"libutilscallstack",
"libutils",
"libbinder",
+ "libactivitymanager_aidl",
"libcutils",
"libmedia",
"libmediautils",
@@ -154,12 +155,14 @@
],
static_libs: [
+ "libprocessinfoservice_aidl",
"libbinderthreadstateutils",
"media_permission-aidl-cpp",
],
export_shared_lib_headers: [
"libbinder",
+ "libactivitymanager_aidl",
"libcamera_client",
"libfmq",
"libsensorprivacy",
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 99d10d2..eb24a93 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -41,7 +41,6 @@
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionController.h>
-#include <binder/ProcessInfoService.h>
#include <binder/IResultReceiver.h>
#include <binderthreadstate/CallerUtils.h>
#include <cutils/atomic.h>
@@ -57,6 +56,7 @@
#include <media/IMediaHTTPService.h>
#include <media/mediaplayer.h>
#include <mediautils/BatteryNotifier.h>
+#include <processinfo/ProcessInfoService.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -224,10 +224,16 @@
return OK;
}
-void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeStatus status) {
+void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeStatus status,
+ SystemCameraKind systemCameraKind) {
Mutex::Autolock lock(mStatusListenerLock);
-
for (auto& i : mListenerList) {
+ if (shouldSkipStatusUpdates(systemCameraKind, i->isVendorListener(), i->getListenerPid(),
+ i->getListenerUid())) {
+ ALOGV("Skipping torch callback for system-only camera device %s",
+ cameraId.c_str());
+ continue;
+ }
i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
}
}
@@ -317,7 +323,7 @@
Mutex::Autolock al(mTorchStatusMutex);
mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
- broadcastTorchModeStatus(id, TorchModeStatus::AVAILABLE_OFF);
+ broadcastTorchModeStatus(id, TorchModeStatus::AVAILABLE_OFF, deviceKind);
}
updateCameraNumAndIds();
@@ -478,12 +484,19 @@
void CameraService::onTorchStatusChanged(const String8& cameraId,
TorchModeStatus newStatus) {
+ SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+ status_t res = getSystemCameraKind(cameraId, &systemCameraKind);
+ if (res != OK) {
+ ALOGE("%s: Could not get system camera kind for camera id %s", __FUNCTION__,
+ cameraId.string());
+ return;
+ }
Mutex::Autolock al(mTorchStatusMutex);
- onTorchStatusChangedLocked(cameraId, newStatus);
+ onTorchStatusChangedLocked(cameraId, newStatus, systemCameraKind);
}
void CameraService::onTorchStatusChangedLocked(const String8& cameraId,
- TorchModeStatus newStatus) {
+ TorchModeStatus newStatus, SystemCameraKind systemCameraKind) {
ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
__FUNCTION__, cameraId.string(), newStatus);
@@ -532,8 +545,7 @@
}
}
}
-
- broadcastTorchModeStatus(cameraId, newStatus);
+ broadcastTorchModeStatus(cameraId, newStatus, systemCameraKind);
}
static bool hasPermissionsForSystemCamera(int callingPid, int callingUid) {
@@ -743,7 +755,7 @@
return Status::ok();
}
-int CameraService::getDeviceVersion(const String8& cameraId, int* facing) {
+int CameraService::getDeviceVersion(const String8& cameraId, int* facing, int* orientation) {
ATRACE_CALL();
int deviceVersion = 0;
@@ -760,6 +772,9 @@
res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
if (res != OK) return -1;
*facing = info.facing;
+ if (orientation) {
+ *orientation = info.orientation;
+ }
}
return deviceVersion;
@@ -1555,6 +1570,7 @@
sp<CLIENT> client = nullptr;
int facing = -1;
+ int orientation = 0;
bool isNdk = (clientPackageName.size() == 0);
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1620,7 +1636,7 @@
// give flashlight a chance to close devices if necessary.
mFlashlight->prepareDeviceOpen(cameraId);
- int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing);
+ int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing, /*out*/&orientation);
if (facing == -1) {
ALOGE("%s: Unable to get camera device \"%s\" facing", __FUNCTION__, cameraId.string());
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
@@ -1688,6 +1704,9 @@
// Set rotate-and-crop override behavior
if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
+ } else if (CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(clientPackageName,
+ orientation, facing)) {
+ client->setRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_90);
}
// Set camera muting behavior
@@ -1805,6 +1824,10 @@
String8 id = String8(cameraId.string());
int uid = CameraThreadState::getCallingUid();
+ if (shouldRejectSystemCameraConnection(id)) {
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Unable to set torch mode"
+ " for system only device %s: ", id.string());
+ }
// verify id is valid.
auto state = getCameraState(id);
if (state == nullptr) {
@@ -2161,6 +2184,11 @@
return shouldSkipStatusUpdates(deviceKind, isVendorListener, clientPid,
clientUid);}), cameraStatuses->end());
+ //cameraStatuses will have non-eligible camera ids removed.
+ std::set<String16> idsChosenForCallback;
+ for (const auto &s : *cameraStatuses) {
+ idsChosenForCallback.insert(String16(s.cameraId));
+ }
/*
* Immediately signal current torch status to this listener only
@@ -2170,7 +2198,11 @@
Mutex::Autolock al(mTorchStatusMutex);
for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) {
String16 id = String16(mTorchStatusMap.keyAt(i).string());
- listener->onTorchStatusChanged(mapToInterface(mTorchStatusMap.valueAt(i)), id);
+ // The camera id is visible to the client. Fine to send torch
+ // callback.
+ if (idsChosenForCallback.find(id) != idsChosenForCallback.end()) {
+ listener->onTorchStatusChanged(mapToInterface(mTorchStatusMap.valueAt(i)), id);
+ }
}
}
@@ -3740,7 +3772,7 @@
TorchModeStatus::AVAILABLE_OFF :
TorchModeStatus::NOT_AVAILABLE;
if (torchStatus != newTorchStatus) {
- onTorchStatusChangedLocked(cameraId, newTorchStatus);
+ onTorchStatusChangedLocked(cameraId, newTorchStatus, deviceKind);
}
}
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index dbfc6c3..98d4500 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -213,7 +213,8 @@
/////////////////////////////////////////////////////////////////////
// CameraDeviceFactory functionality
- int getDeviceVersion(const String8& cameraId, int* facing = NULL);
+ int getDeviceVersion(const String8& cameraId, int* facing = nullptr,
+ int* orientation = nullptr);
/////////////////////////////////////////////////////////////////////
// Shared utilities
@@ -990,7 +991,8 @@
// handle torch mode status change and invoke callbacks. mTorchStatusMutex
// should be locked.
void onTorchStatusChangedLocked(const String8& cameraId,
- hardware::camera::common::V1_0::TorchModeStatus newStatus);
+ hardware::camera::common::V1_0::TorchModeStatus newStatus,
+ SystemCameraKind systemCameraKind);
// get a camera's torch status. mTorchStatusMutex should be locked.
status_t getTorchStatusLocked(const String8 &cameraId,
@@ -1084,7 +1086,8 @@
void broadcastTorchModeStatus(const String8& cameraId,
- hardware::camera::common::V1_0::TorchModeStatus status);
+ hardware::camera::common::V1_0::TorchModeStatus status,
+ SystemCameraKind systemCameraKind);
void disconnectClient(const String8& id, sp<BasicClient> clientToDisconnect);
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 4c3ded6..ee764ec 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -158,7 +158,7 @@
res = device->createStream(mCallbackWindow,
params.previewWidth, params.previewHeight, callbackFormat,
HAL_DATASPACE_V0_JFIF, CAMERA_STREAM_ROTATION_0, &mCallbackStreamId,
- String8());
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index ff2e398..eed2654 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -151,7 +151,7 @@
params.pictureWidth, params.pictureHeight,
HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_V0_JFIF,
CAMERA_STREAM_ROTATION_0, &mCaptureStreamId,
- String8());
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for capture: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 3a709c9..02ac638 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -56,7 +56,7 @@
int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
int pictureWidth, pictureHeight;
- // Store the picture size before they are overriden by video snapshot
+ // Store the picture size before they are overridden by video snapshot
int pictureWidthLastSet, pictureHeightLastSet;
bool pictureSizeOverriden;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 8b1eb28..2d3597c 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -198,7 +198,8 @@
res = device->createStream(mPreviewWindow,
params.previewWidth, params.previewHeight,
CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
- CAMERA_STREAM_ROTATION_0, &mPreviewStreamId, String8());
+ CAMERA_STREAM_ROTATION_0, &mPreviewStreamId, String8(),
+ std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -384,7 +385,7 @@
params.videoWidth, params.videoHeight,
params.videoFormat, params.videoDataSpace,
CAMERA_STREAM_ROTATION_0, &mRecordingStreamId,
- String8());
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for recording: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 9fdc727..8e598f1 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -261,7 +261,7 @@
res = device->createStream(outSurface, params.fastInfo.maxZslSize.width,
params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
HAL_DATASPACE_UNKNOWN, CAMERA_STREAM_ROTATION_0, &mZslStreamId,
- String8());
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create ZSL stream: "
"%s (%d)", __FUNCTION__, client->getCameraId(),
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 8cccbb1..1b65d1a 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -125,8 +125,8 @@
/*listener*/this,
/*sendPartials*/true);
- auto deviceInfo = mDevice->info();
- camera_metadata_entry_t physicalKeysEntry = deviceInfo.find(
+ const CameraMetadata &deviceInfo = mDevice->info();
+ camera_metadata_ro_entry_t physicalKeysEntry = deviceInfo.find(
ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
if (physicalKeysEntry.count > 0) {
mSupportedPhysicalRequestKeys.insert(mSupportedPhysicalRequestKeys.begin(),
@@ -135,6 +135,17 @@
}
mProviderManager = providerPtr;
+ // Cache physical camera ids corresponding to this device and also the high
+ // resolution sensors in this device + physical camera ids
+ mProviderManager->isLogicalCamera(mCameraIdStr.string(), &mPhysicalCameraIds);
+ if (isUltraHighResolutionSensor(mCameraIdStr)) {
+ mHighResolutionSensors.insert(mCameraIdStr.string());
+ }
+ for (auto &physicalId : mPhysicalCameraIds) {
+ if (isUltraHighResolutionSensor(String8(physicalId.c_str()))) {
+ mHighResolutionSensors.insert(physicalId.c_str());
+ }
+ }
return OK;
}
@@ -186,6 +197,17 @@
return binder::Status::ok();
}
+static std::list<int> getIntersection(const std::unordered_set<int> &streamIdsForThisCamera,
+ const Vector<int> &streamIdsForThisRequest) {
+ std::list<int> intersection;
+ for (auto &streamId : streamIdsForThisRequest) {
+ if (streamIdsForThisCamera.find(streamId) != streamIdsForThisCamera.end()) {
+ intersection.emplace_back(streamId);
+ }
+ }
+ return intersection;
+}
+
binder::Status CameraDeviceClient::submitRequestList(
const std::vector<hardware::camera2::CaptureRequest>& requests,
bool streaming,
@@ -332,6 +354,24 @@
"Request settings are empty");
}
+ // Check whether the physical / logical stream has settings
+ // consistent with the sensor pixel mode(s) it was configured with.
+ // mCameraIdToStreamSet will only have ids that are high resolution
+ const auto streamIdSetIt = mHighResolutionCameraIdToStreamIdSet.find(it.id);
+ if (streamIdSetIt != mHighResolutionCameraIdToStreamIdSet.end()) {
+ std::list<int> streamIdsUsedInRequest = getIntersection(streamIdSetIt->second,
+ outputStreamIds);
+ if (!request.mIsReprocess &&
+ !isSensorPixelModeConsistent(streamIdsUsedInRequest, it.settings)) {
+ ALOGE("%s: Camera %s: Request settings CONTROL_SENSOR_PIXEL_MODE not "
+ "consistent with configured streams. Rejecting request.",
+ __FUNCTION__, it.id.c_str());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Request settings CONTROL_SENSOR_PIXEL_MODE are not consistent with "
+ "streams configured");
+ }
+ }
+
String8 physicalId(it.id.c_str());
if (physicalId != mDevice->getId()) {
auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),
@@ -494,7 +534,7 @@
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- res = SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
+ res = camera3::SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
mCameraIdStr);
if (!res.isOk()) {
return res;
@@ -560,8 +600,8 @@
binder::Status CameraDeviceClient::isSessionConfigurationSupported(
const SessionConfiguration& sessionConfiguration, bool *status /*out*/) {
- ATRACE_CALL();
+ ATRACE_CALL();
binder::Status res;
status_t ret = OK;
if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
@@ -573,7 +613,7 @@
}
auto operatingMode = sessionConfiguration.getOperatingMode();
- res = SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
+ res = camera3::SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
mCameraIdStr);
if (!res.isOk()) {
return res;
@@ -589,7 +629,7 @@
metadataGetter getMetadata = [this](const String8 &id) {return mDevice->infoPhysical(id);};
std::vector<std::string> physicalCameraIds;
mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
- res = SessionConfigurationUtils::convertToHALStreamCombination(sessionConfiguration,
+ res = camera3::SessionConfigurationUtils::convertToHALStreamCombination(sessionConfiguration,
mCameraIdStr, mDevice->info(), getMetadata, physicalCameraIds, streamConfiguration,
&earlyExit);
if (!res.isOk()) {
@@ -714,6 +754,13 @@
}
mCompositeStreamMap.removeItemsAt(compositeIndex);
}
+ for (auto &mapIt: mHighResolutionCameraIdToStreamIdSet) {
+ auto &streamSet = mapIt.second;
+ if (streamSet.find(streamId) != streamSet.end()) {
+ streamSet.erase(streamId);
+ break;
+ }
+ }
}
}
@@ -740,7 +787,7 @@
bool deferredConsumerOnly = deferredConsumer && numBufferProducers == 0;
bool isMultiResolution = outputConfiguration.isMultiResolution();
- res = SessionConfigurationUtils::checkSurfaceType(numBufferProducers, deferredConsumer,
+ res = camera3::SessionConfigurationUtils::checkSurfaceType(numBufferProducers, deferredConsumer,
outputConfiguration.getSurfaceType());
if (!res.isOk()) {
return res;
@@ -749,10 +796,8 @@
if (!mDevice.get()) {
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- std::vector<std::string> physicalCameraIds;
- mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
- res = SessionConfigurationUtils::checkPhysicalCameraId(physicalCameraIds, physicalCameraId,
- mCameraIdStr);
+ res = camera3::SessionConfigurationUtils::checkPhysicalCameraId(mPhysicalCameraIds,
+ physicalCameraId, mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -768,6 +813,8 @@
OutputStreamInfo streamInfo;
bool isStreamInfoValid = false;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (auto& bufferProducer : bufferProducers) {
// Don't create multiple streams for the same target surface
sp<IBinder> binder = IInterface::asBinder(bufferProducer);
@@ -780,8 +827,9 @@
}
sp<Surface> surface;
- res = SessionConfigurationUtils::createSurfaceFromGbp(streamInfo, isStreamInfoValid,
- surface, bufferProducer, mCameraIdStr, mDevice->infoPhysical(physicalCameraId));
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(streamInfo,
+ isStreamInfoValid, surface, bufferProducer, mCameraIdStr,
+ mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -793,10 +841,10 @@
binders.push_back(IInterface::asBinder(bufferProducer));
surfaces.push_back(surface);
}
-
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
std::vector<int> surfaceIds;
- bool isDepthCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]);
+ bool isDepthCompositeStream =
+ camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]);
bool isHeicCompisiteStream = camera3::HeicCompositeStream::isHeicCompositeStream(surfaces[0]);
if (isDepthCompositeStream || isHeicCompisiteStream) {
sp<CompositeStream> compositeStream;
@@ -809,8 +857,8 @@
err = compositeStream->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format,
static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
- isShared, isMultiResolution);
+ &streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,
+ outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution);
if (err == OK) {
mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()),
compositeStream);
@@ -819,8 +867,8 @@
err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format, streamInfo.dataSpace,
static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
- isShared, isMultiResolution);
+ &streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,
+ outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution);
}
if (err != OK) {
@@ -848,6 +896,16 @@
// Set transform flags to ensure preview to be rotated correctly.
res = setStreamTransformLocked(streamId);
+ // Fill in mHighResolutionCameraIdToStreamIdSet map
+ const String8 &cameraIdUsed =
+ physicalCameraId.size() != 0 ? physicalCameraId : mCameraIdStr;
+ const char *cameraIdUsedCStr = cameraIdUsed.string();
+ // Only needed for high resolution sensors
+ if (mHighResolutionSensors.find(cameraIdUsedCStr) !=
+ mHighResolutionSensors.end()) {
+ mHighResolutionCameraIdToStreamIdSet[cameraIdUsedCStr].insert(streamId);
+ }
+
*newStreamId = streamId;
}
@@ -884,10 +942,25 @@
std::vector<sp<Surface>> noSurface;
std::vector<int> surfaceIds;
String8 physicalCameraId(outputConfiguration.getPhysicalCameraId());
+ const String8 &cameraIdUsed =
+ physicalCameraId.size() != 0 ? physicalCameraId : mCameraIdStr;
+ // Here, we override sensor pixel modes
+ std::unordered_set<int32_t> overriddenSensorPixelModesUsed;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
+ if (camera3::SessionConfigurationUtils::checkAndOverrideSensorPixelModesUsed(
+ sensorPixelModesUsed, format, width, height, getStaticInfo(cameraIdUsed),
+ /*allowRounding*/ false, &overriddenSensorPixelModesUsed) != OK) {
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "sensor pixel modes used not valid for deferred stream");
+ }
+
err = mDevice->createStream(noSurface, /*hasDeferredConsumer*/true, width,
height, format, dataSpace,
static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds,
+ &streamId, physicalCameraId,
+ overriddenSensorPixelModesUsed,
+ &surfaceIds,
outputConfiguration.getSurfaceSetID(), isShared,
outputConfiguration.isMultiResolution(), consumerUsage);
@@ -900,9 +973,9 @@
// a separate list to track. Once the deferred surface is set, this id will be
// relocated to mStreamMap.
mDeferredStreams.push_back(streamId);
-
mStreamInfoMap.emplace(std::piecewise_construct, std::forward_as_tuple(streamId),
- std::forward_as_tuple(width, height, format, dataSpace, consumerUsage));
+ std::forward_as_tuple(width, height, format, dataSpace, consumerUsage,
+ overriddenSensorPixelModesUsed));
ALOGV("%s: Camera %s: Successfully created a new stream ID %d for a deferred surface"
" (%d x %d) stream with format 0x%x.",
@@ -912,6 +985,13 @@
res = setStreamTransformLocked(streamId);
*newStreamId = streamId;
+ // Fill in mHighResolutionCameraIdToStreamIdSet
+ const char *cameraIdUsedCStr = cameraIdUsed.string();
+ // Only needed for high resolution sensors
+ if (mHighResolutionSensors.find(cameraIdUsedCStr) !=
+ mHighResolutionSensors.end()) {
+ mHighResolutionCameraIdToStreamIdSet[cameraIdUsed.string()].insert(streamId);
+ }
}
return res;
}
@@ -1081,13 +1161,15 @@
newOutputsMap.removeItemsAt(idx);
}
}
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (size_t i = 0; i < newOutputsMap.size(); i++) {
OutputStreamInfo outInfo;
sp<Surface> surface;
- res = SessionConfigurationUtils::createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false,
- surface, newOutputsMap.valueAt(i), mCameraIdStr,
- mDevice->infoPhysical(physicalCameraId));
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(outInfo,
+ /*isStreamInfoValid*/ false, surface, newOutputsMap.valueAt(i), mCameraIdStr,
+ mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -1442,6 +1524,8 @@
}
std::vector<sp<Surface>> consumerSurfaces;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (auto& bufferProducer : bufferProducers) {
// Don't create multiple streams for the same target surface
ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
@@ -1452,9 +1536,9 @@
}
sp<Surface> surface;
- res = SessionConfigurationUtils::createSurfaceFromGbp(mStreamInfoMap[streamId],
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(mStreamInfoMap[streamId],
true /*isStreamInfoValid*/, surface, bufferProducer, mCameraIdStr,
- mDevice->infoPhysical(physicalId));
+ mDevice->infoPhysical(physicalId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -1936,4 +2020,54 @@
return ret;
}
+
+const CameraMetadata &CameraDeviceClient::getStaticInfo(const String8 &cameraId) {
+ if (mDevice->getId() == cameraId) {
+ return mDevice->info();
+ }
+ return mDevice->infoPhysical(cameraId);
+}
+
+bool CameraDeviceClient::isUltraHighResolutionSensor(const String8 &cameraId) {
+ const CameraMetadata &deviceInfo = getStaticInfo(cameraId);
+ return camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(deviceInfo);
+}
+
+bool CameraDeviceClient::isSensorPixelModeConsistent(
+ const std::list<int> &streamIdList, const CameraMetadata &settings) {
+ // First we get the sensorPixelMode from the settings metadata.
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = settings.find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT &&
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode not is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return false;
+ }
+ }
+ // Check whether each stream has max resolution allowed.
+ bool consistent = true;
+ for (auto it : streamIdList) {
+ auto const streamInfoIt = mStreamInfoMap.find(it);
+ if (streamInfoIt == mStreamInfoMap.end()) {
+ ALOGE("%s: stream id %d not created, skipping", __FUNCTION__, it);
+ return false;
+ }
+ consistent =
+ streamInfoIt->second.sensorPixelModesUsed.find(sensorPixelMode) !=
+ streamInfoIt->second.sensorPixelModesUsed.end();
+ if (!consistent) {
+ ALOGE("sensorPixelMode used %i not consistent with configured modes", sensorPixelMode);
+ for (auto m : streamInfoIt->second.sensorPixelModesUsed) {
+ ALOGE("sensor pixel mode used list: %i", m);
+ }
+ break;
+ }
+ }
+
+ return consistent;
+}
+
} // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 9f7a4af..adedf92 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -28,6 +28,7 @@
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
#include "CompositeStream.h"
+#include "utils/SessionConfigurationUtils.h"
using android::camera3::OutputStreamInfo;
using android::camera3::CompositeStream;
@@ -222,6 +223,13 @@
// Calculate the ANativeWindow transform from android.sensor.orientation
status_t getRotationTransformLocked(/*out*/int32_t* transform);
+ bool isUltraHighResolutionSensor(const String8 &cameraId);
+
+ bool isSensorPixelModeConsistent(const std::list<int> &streamIdList,
+ const CameraMetadata &settings);
+
+ const CameraMetadata &getStaticInfo(const String8 &cameraId);
+
private:
// StreamSurfaceId encapsulates streamId + surfaceId for a particular surface.
// streamId specifies the index of the stream the surface belongs to, and the
@@ -305,6 +313,8 @@
int32_t mRequestIdCounter;
+ std::vector<std::string> mPhysicalCameraIds;
+
// The list of output streams whose surfaces are deferred. We have to track them separately
// as there are no surfaces available and can not be put into mStreamMap. Once the deferred
// Surface is configured, the stream id will be moved to mStreamMap.
@@ -313,6 +323,12 @@
// stream ID -> outputStreamInfo mapping
std::unordered_map<int32_t, OutputStreamInfo> mStreamInfoMap;
+ // map high resolution camera id (logical / physical) -> list of stream ids configured
+ std::unordered_map<std::string, std::unordered_set<int>> mHighResolutionCameraIdToStreamIdSet;
+
+ // set of high resolution camera id (logical / physical)
+ std::unordered_set<std::string> mHighResolutionSensors;
+
KeyedVector<sp<IBinder>, sp<CompositeStream>> mCompositeStreamMap;
sp<CameraProviderManager> mProviderManager;
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index 515b7f2..4b840fc 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -47,7 +47,9 @@
status_t CompositeStream::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int * id, const String8& physicalCameraId,
- std::vector<int> * surfaceIds, int streamSetId, bool isShared, bool isMultiResolution) {
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> * surfaceIds,
+ int streamSetId, bool isShared, bool isMultiResolution) {
if (hasDeferredConsumer) {
ALOGE("%s: Deferred consumers not supported in case of composite streams!",
__FUNCTION__);
@@ -72,8 +74,8 @@
return BAD_VALUE;
}
- return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation, id,
- physicalCameraId, surfaceIds, streamSetId, isShared);
+ return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation,
+ id, physicalCameraId, sensorPixelModesUsed, surfaceIds, streamSetId, isShared);
}
status_t CompositeStream::deleteStream() {
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 1bf137a..600bd28 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -44,7 +44,9 @@
status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution);
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared, bool isMultiResolution);
status_t deleteStream();
@@ -55,7 +57,9 @@
virtual status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) = 0;
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) = 0;
// Release all internal streams and corresponding resources.
virtual status_t deleteInternalStreams() = 0;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 2c553f3..19b54e0 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -20,6 +20,7 @@
#include "api1/client2/JpegProcessor.h"
#include "common/CameraProviderManager.h"
+#include "utils/SessionConfigurationUtils.h"
#include <gui/Surface.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -78,7 +79,10 @@
}
}
- getSupportedDepthSizes(staticInfo, &mSupportedDepthSizes);
+ getSupportedDepthSizes(staticInfo, /*maxResolution*/false, &mSupportedDepthSizes);
+ if (SessionConfigurationUtils::isUltraHighResolutionSensor(staticInfo)) {
+ getSupportedDepthSizes(staticInfo, true, &mSupportedDepthSizesMaximumResolution);
+ }
}
}
@@ -484,17 +488,82 @@
return false;
}
+static bool setContains(std::unordered_set<int32_t> containerSet, int32_t value) {
+ return containerSet.find(value) != containerSet.end();
+}
+
+status_t DepthCompositeStream::checkAndGetMatchingDepthSize(size_t width, size_t height,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizes,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizesMaximumResolution,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ size_t *depthWidth, size_t *depthHeight) {
+ if (depthWidth == nullptr || depthHeight == nullptr) {
+ return BAD_VALUE;
+ }
+ size_t chosenDepthWidth = 0, chosenDepthHeight = 0;
+ bool hasDefaultSensorPixelMode =
+ setContains(sensorPixelModesUsed, ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+
+ bool hasMaximumResolutionSensorPixelMode =
+ setContains(sensorPixelModesUsed, ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+
+ if (!hasDefaultSensorPixelMode && !hasMaximumResolutionSensorPixelMode) {
+ ALOGE("%s: sensor pixel modes don't contain either maximum resolution or default modes",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (hasDefaultSensorPixelMode) {
+ auto ret = getMatchingDepthSize(width, height, depthSizes, &chosenDepthWidth,
+ &chosenDepthHeight);
+ if (ret != OK) {
+ ALOGE("%s: No matching depth stream size found", __FUNCTION__);
+ return ret;
+ }
+ }
+
+ if (hasMaximumResolutionSensorPixelMode) {
+ size_t depthWidth = 0, depthHeight = 0;
+ auto ret = getMatchingDepthSize(width, height,
+ depthSizesMaximumResolution, &depthWidth, &depthHeight);
+ if (ret != OK) {
+ ALOGE("%s: No matching max resolution depth stream size found", __FUNCTION__);
+ return ret;
+ }
+ // Both matching depth sizes should be the same.
+ if (chosenDepthWidth != 0 && chosenDepthWidth != depthWidth &&
+ chosenDepthHeight != depthHeight) {
+ ALOGE("%s: Maximum resolution sensor pixel mode and default sensor pixel mode don't"
+ " have matching depth sizes", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (chosenDepthWidth == 0) {
+ chosenDepthWidth = depthWidth;
+ chosenDepthHeight = depthHeight;
+ }
+ }
+ *depthWidth = chosenDepthWidth;
+ *depthHeight = chosenDepthHeight;
+ return OK;
+}
+
+
status_t DepthCompositeStream::createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int /*streamSetId*/, bool /*isShared*/) {
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int /*streamSetId*/, bool /*isShared*/) {
if (mSupportedDepthSizes.empty()) {
ALOGE("%s: This camera device doesn't support any depth map streams!", __FUNCTION__);
return INVALID_OPERATION;
}
size_t depthWidth, depthHeight;
- auto ret = getMatchingDepthSize(width, height, mSupportedDepthSizes, &depthWidth, &depthHeight);
+ auto ret =
+ checkAndGetMatchingDepthSize(width, height, mSupportedDepthSizes,
+ mSupportedDepthSizesMaximumResolution, sensorPixelModesUsed, &depthWidth,
+ &depthHeight);
if (ret != OK) {
ALOGE("%s: Failed to find an appropriate depth stream size!", __FUNCTION__);
return ret;
@@ -515,7 +584,7 @@
mBlobSurface = new Surface(producer);
ret = device->createStream(mBlobSurface, width, height, format, kJpegDataSpace, rotation,
- id, physicalCameraId, surfaceIds);
+ id, physicalCameraId, sensorPixelModesUsed, surfaceIds);
if (ret == OK) {
mBlobStreamId = *id;
mBlobSurfaceId = (*surfaceIds)[0];
@@ -531,7 +600,8 @@
mDepthSurface = new Surface(producer);
std::vector<int> depthSurfaceId;
ret = device->createStream(mDepthSurface, depthWidth, depthHeight, kDepthMapPixelFormat,
- kDepthMapDataSpace, rotation, &mDepthStreamId, physicalCameraId, &depthSurfaceId);
+ kDepthMapDataSpace, rotation, &mDepthStreamId, physicalCameraId, sensorPixelModesUsed,
+ &depthSurfaceId);
if (ret == OK) {
mDepthSurfaceId = depthSurfaceId[0];
} else {
@@ -749,13 +819,15 @@
return ((*depthWidth > 0) && (*depthHeight > 0)) ? OK : BAD_VALUE;
}
-void DepthCompositeStream::getSupportedDepthSizes(const CameraMetadata& ch,
+void DepthCompositeStream::getSupportedDepthSizes(const CameraMetadata& ch, bool maxResolution,
std::vector<std::tuple<size_t, size_t>>* depthSizes /*out*/) {
if (depthSizes == nullptr) {
return;
}
- auto entry = ch.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+ auto entry = ch.find(
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxResolution));
if (entry.count > 0) {
// Depth stream dimensions have four int32_t components
// (pixelformat, width, height, type)
@@ -779,30 +851,43 @@
}
std::vector<std::tuple<size_t, size_t>> depthSizes;
- getSupportedDepthSizes(ch, &depthSizes);
+ std::vector<std::tuple<size_t, size_t>> depthSizesMaximumResolution;
+ getSupportedDepthSizes(ch, /*maxResolution*/false, &depthSizes);
if (depthSizes.empty()) {
ALOGE("%s: No depth stream configurations present", __FUNCTION__);
return BAD_VALUE;
}
- size_t depthWidth, depthHeight;
- auto ret = getMatchingDepthSize(streamInfo.width, streamInfo.height, depthSizes, &depthWidth,
- &depthHeight);
+ if (SessionConfigurationUtils::isUltraHighResolutionSensor(ch)) {
+ getSupportedDepthSizes(ch, /*maxResolution*/true, &depthSizesMaximumResolution);
+ if (depthSizesMaximumResolution.empty()) {
+ ALOGE("%s: No depth stream configurations for maximum resolution present",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ }
+
+ size_t chosenDepthWidth = 0, chosenDepthHeight = 0;
+ auto ret = checkAndGetMatchingDepthSize(streamInfo.width, streamInfo.height, depthSizes,
+ depthSizesMaximumResolution, streamInfo.sensorPixelModesUsed, &chosenDepthWidth,
+ &chosenDepthHeight);
+
if (ret != OK) {
- ALOGE("%s: No matching depth stream size found", __FUNCTION__);
+ ALOGE("%s: Couldn't get matching depth sizes", __FUNCTION__);
return ret;
}
compositeOutput->clear();
compositeOutput->insert(compositeOutput->end(), 2, streamInfo);
+ // Sensor pixel modes should stay the same here. They're already overridden.
// Jpeg/Blob stream info
(*compositeOutput)[0].dataSpace = kJpegDataSpace;
(*compositeOutput)[0].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
// Depth stream info
- (*compositeOutput)[1].width = depthWidth;
- (*compositeOutput)[1].height = depthHeight;
+ (*compositeOutput)[1].width = chosenDepthWidth;
+ (*compositeOutput)[1].height = chosenDepthHeight;
(*compositeOutput)[1].format = kDepthMapPixelFormat;
(*compositeOutput)[1].dataSpace = kDepthMapDataSpace;
(*compositeOutput)[1].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 05bc504..a520bbf 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -51,7 +51,9 @@
status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) override;
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) override;
status_t deleteInternalStreams() override;
status_t configureStream() override;
status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector<int32_t>* /*out*/outputStreamIds,
@@ -86,11 +88,17 @@
};
// Helper methods
- static void getSupportedDepthSizes(const CameraMetadata& ch,
+ static void getSupportedDepthSizes(const CameraMetadata& ch, bool maxResolution,
std::vector<std::tuple<size_t, size_t>>* depthSizes /*out*/);
static status_t getMatchingDepthSize(size_t width, size_t height,
const std::vector<std::tuple<size_t, size_t>>& supporedDepthSizes,
size_t *depthWidth /*out*/, size_t *depthHeight /*out*/);
+ static status_t checkAndGetMatchingDepthSize(size_t width, size_t height,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizes,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizesMaximumResolution,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ size_t *depthWidth /*out*/, size_t *depthHeight /*out*/);
+
// Dynamic depth processing
status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out,
@@ -126,6 +134,7 @@
ssize_t mMaxJpegSize;
std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizes;
+ std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizesMaximumResolution;
std::vector<float> mIntrinsicCalibration, mLensDistortion;
bool mIsLogicalCamera;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 7d68485..582001d 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -36,6 +36,7 @@
#include "common/CameraDeviceBase.h"
#include "utils/ExifUtils.h"
+#include "utils/SessionConfigurationUtils.h"
#include "HeicEncoderInfoManager.h"
#include "HeicCompositeStream.h"
@@ -115,7 +116,9 @@
status_t HeicCompositeStream::createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int /*streamSetId*/, bool /*isShared*/) {
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int /*streamSetId*/, bool /*isShared*/) {
sp<CameraDeviceBase> device = mDevice.promote();
if (!device.get()) {
@@ -141,7 +144,8 @@
mStaticInfo = device->info();
res = device->createStream(mAppSegmentSurface, mAppSegmentMaxSize, 1, format,
- kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId, surfaceIds);
+ kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId,
+ sensorPixelModesUsed,surfaceIds);
if (res == OK) {
mAppSegmentSurfaceId = (*surfaceIds)[0];
} else {
@@ -177,7 +181,7 @@
int srcStreamFmt = mUseGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 :
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
res = device->createStream(mMainImageSurface, width, height, srcStreamFmt, kHeifDataSpace,
- rotation, id, physicalCameraId, &sourceSurfaceId);
+ rotation, id, physicalCameraId, sensorPixelModesUsed, &sourceSurfaceId);
if (res == OK) {
mMainImageSurfaceId = sourceSurfaceId[0];
mMainImageStreamId = *id;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index cbd9d21..1077a1f 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -46,7 +46,9 @@
status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) override;
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) override;
status_t deleteInternalStreams() override;
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 5acbb99..85b0cc2 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -164,6 +164,7 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, bool isMultiResolution = false,
@@ -180,6 +181,7 @@
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, bool isMultiResolution = false,
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index dfe2409..8942d05 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -686,9 +686,39 @@
}
}
-status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() {
- uint32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE;
- uint32_t depthSizesTag = ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS;
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags(
+ bool maxResolution) {
+ const int32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE;
+
+ const int32_t scalerSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t scalerMinFrameDurationsTag =
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+ const int32_t scalerStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, maxResolution);
+
+ const int32_t depthSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t depthStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, maxResolution);
+ const int32_t depthMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, maxResolution);
+
+ const int32_t dynamicDepthSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t dynamicDepthStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, maxResolution);
+ const int32_t dynamicDepthMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, maxResolution);
+
auto& c = mCameraCharacteristics;
std::vector<std::tuple<size_t, size_t>> supportedBlobSizes, supportedDepthSizes,
supportedDynamicDepthSizes, internalDepthSizes;
@@ -718,7 +748,7 @@
return BAD_VALUE;
}
- getSupportedSizes(c, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, HAL_PIXEL_FORMAT_BLOB,
+ getSupportedSizes(c, scalerSizesTag, HAL_PIXEL_FORMAT_BLOB,
&supportedBlobSizes);
getSupportedSizes(c, depthSizesTag, HAL_PIXEL_FORMAT_Y16, &supportedDepthSizes);
if (supportedBlobSizes.empty() || supportedDepthSizes.empty()) {
@@ -745,10 +775,10 @@
std::vector<int64_t> blobMinDurations, blobStallDurations;
std::vector<int64_t> dynamicDepthMinDurations, dynamicDepthStallDurations;
- getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS,
- HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthMinDurations);
- getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
- HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobMinDurations);
+ getSupportedDurations(c, depthMinFrameDurationsTag, HAL_PIXEL_FORMAT_Y16, internalDepthSizes,
+ &depthMinDurations);
+ getSupportedDurations(c, scalerMinFrameDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedDynamicDepthSizes, &blobMinDurations);
if (blobMinDurations.empty() || depthMinDurations.empty() ||
(depthMinDurations.size() != blobMinDurations.size())) {
ALOGE("%s: Unexpected number of available depth min durations! %zu vs. %zu",
@@ -756,10 +786,10 @@
return BAD_VALUE;
}
- getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS,
- HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthStallDurations);
- getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
- HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobStallDurations);
+ getSupportedDurations(c, depthStallDurationsTag, HAL_PIXEL_FORMAT_Y16, internalDepthSizes,
+ &depthStallDurations);
+ getSupportedDurations(c, scalerStallDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedDynamicDepthSizes, &blobStallDurations);
if (blobStallDurations.empty() || depthStallDurations.empty() ||
(depthStallDurations.size() != blobStallDurations.size())) {
ALOGE("%s: Unexpected number of available depth stall durations! %zu vs. %zu",
@@ -804,15 +834,14 @@
supportedChTags.reserve(chTags.count + 3);
supportedChTags.insert(supportedChTags.end(), chTags.data.i32,
chTags.data.i32 + chTags.count);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS);
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
- dynamicDepthEntries.data(), dynamicDepthEntries.size());
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS,
- dynamicDepthMinDurationEntries.data(), dynamicDepthMinDurationEntries.size());
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS,
- dynamicDepthStallDurationEntries.data(), dynamicDepthStallDurationEntries.size());
+ supportedChTags.push_back(dynamicDepthSizesTag);
+ supportedChTags.push_back(dynamicDepthMinFrameDurationsTag);
+ supportedChTags.push_back(dynamicDepthStallDurationsTag);
+ c.update(dynamicDepthSizesTag, dynamicDepthEntries.data(), dynamicDepthEntries.size());
+ c.update(dynamicDepthMinFrameDurationsTag, dynamicDepthMinDurationEntries.data(),
+ dynamicDepthMinDurationEntries.size());
+ c.update(dynamicDepthStallDurationsTag, dynamicDepthStallDurationEntries.data(),
+ dynamicDepthStallDurationEntries.size());
c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedChTags.data(),
supportedChTags.size());
@@ -1046,7 +1075,24 @@
return OK;
}
-status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags() {
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags(bool maxResolution) {
+ int32_t scalerStreamSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ int32_t scalerMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, maxResolution);
+
+ int32_t heicStreamSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, maxResolution);
+ int32_t heicMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, maxResolution);
+ int32_t heicStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, maxResolution);
+
auto& c = mCameraCharacteristics;
camera_metadata_entry halHeicSupport = c.find(ANDROID_HEIC_INFO_SUPPORTED);
@@ -1075,10 +1121,8 @@
std::vector<int64_t> heicDurations;
std::vector<int64_t> heicStallDurations;
- camera_metadata_entry halStreamConfigs =
- c.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- camera_metadata_entry minFrameDurations =
- c.find(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+ camera_metadata_entry halStreamConfigs = c.find(scalerStreamSizesTag);
+ camera_metadata_entry minFrameDurations = c.find(scalerMinFrameDurationsTag);
status_t res = fillHeicStreamCombinations(&heicOutputs, &heicDurations, &heicStallDurations,
halStreamConfigs, minFrameDurations);
@@ -1088,12 +1132,9 @@
return res;
}
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS,
- heicOutputs.data(), heicOutputs.size());
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS,
- heicDurations.data(), heicDurations.size());
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS,
- heicStallDurations.data(), heicStallDurations.size());
+ c.update(heicStreamSizesTag, heicOutputs.data(), heicOutputs.size());
+ c.update(heicMinFrameDurationsTag, heicDurations.data(), heicDurations.size());
+ c.update(heicStallDurationsTag, heicStallDurations.data(), heicStallDurations.size());
return OK;
}
@@ -1202,7 +1243,9 @@
interface = mServiceProxy->tryGetService(newProvider);
if (interface == nullptr) {
- ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
+ // The interface may not be started yet. In that case, this is not a
+ // fatal error.
+ ALOGW("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
newProvider.c_str());
return BAD_VALUE;
}
@@ -2005,16 +2048,20 @@
size_t numStreams = halCameraIdsAndStreamCombinations.size();
halCameraIdsAndStreamCombinations_2_6.resize(numStreams);
for (size_t i = 0; i < numStreams; i++) {
+ using namespace camera3;
auto const& combination = halCameraIdsAndStreamCombinations[i];
halCameraIdsAndStreamCombinations_2_6[i].cameraId = combination.cameraId;
bool success =
SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
- halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
- combination.streamConfiguration);
+ halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
+ combination.streamConfiguration);
if (!success) {
*isSupported = false;
return OK;
}
+ camera3::SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
+ combination.streamConfiguration);
}
ret = interface_2_6->isConcurrentStreamCombinationSupported(
halCameraIdsAndStreamCombinations_2_6, cb);
@@ -2220,6 +2267,21 @@
ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities: %s (%d)",
__FUNCTION__, strerror(-res), res);
}
+
+ if (camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(mCameraCharacteristics)) {
+ status_t status = addDynamicDepthTags(/*maxResolution*/true);
+ if (OK != status) {
+ ALOGE("%s: Failed appending dynamic depth tags for maximum resolution mode: %s (%d)",
+ __FUNCTION__, strerror(-status), status);
+ }
+
+ status = deriveHeicTags(/*maxResolution*/true);
+ if (OK != status) {
+ ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities for"
+ "maximum resolution mode: %s (%d)", __FUNCTION__, strerror(-status), status);
+ }
+ }
+
res = addRotateCropTags();
if (OK != res) {
ALOGE("%s: Unable to add default SCALER_ROTATE_AND_CROP tags: %s (%d)", __FUNCTION__,
@@ -2426,26 +2488,22 @@
status_t res;
Status callStatus;
::android::hardware::Return<void> ret;
- if (interface_3_7 != nullptr) {
- ret = interface_3_7->isStreamCombinationSupported_3_7(configuration,
+ auto halCb =
[&callStatus, &status] (Status s, bool combStatus) {
callStatus = s;
*status = combStatus;
- });
+ };
+ if (interface_3_7 != nullptr) {
+ ret = interface_3_7->isStreamCombinationSupported_3_7(configuration, halCb);
} else if (interface_3_5 != nullptr) {
hardware::camera::device::V3_4::StreamConfiguration configuration_3_4;
- bool success = SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ bool success = camera3::SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
configuration_3_4, configuration);
if (!success) {
*status = false;
return OK;
}
-
- ret = interface_3_5->isStreamCombinationSupported(configuration_3_4,
- [&callStatus, &status] (Status s, bool combStatus) {
- callStatus = s;
- *status = combStatus;
- });
+ ret = interface_3_5->isStreamCombinationSupported(configuration_3_4, halCb);
} else {
return INVALID_OPERATION;
}
@@ -2829,7 +2887,7 @@
if (res != OK) {
return res;
}
- metadataGetter getMetadata =
+ camera3::metadataGetter getMetadata =
[this](const String8 &id) {
CameraMetadata physicalDeviceInfo;
getCameraCharacteristicsLocked(id.string(), &physicalDeviceInfo);
@@ -2838,7 +2896,7 @@
std::vector<std::string> physicalCameraIds;
isLogicalCameraLocked(cameraIdAndSessionConfig.mCameraId, &physicalCameraIds);
bStatus =
- SessionConfigurationUtils::convertToHALStreamCombination(
+ camera3::SessionConfigurationUtils::convertToHALStreamCombination(
cameraIdAndSessionConfig.mSessionConfiguration,
String8(cameraIdAndSessionConfig.mCameraId.c_str()), deviceInfo, getMetadata,
physicalCameraIds, streamConfiguration, &shouldExit);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index fa9cc1c..12bda9b 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -556,8 +556,8 @@
void queryPhysicalCameraIds();
SystemCameraKind getSystemCameraKind();
status_t fixupMonochromeTags();
- status_t addDynamicDepthTags();
- status_t deriveHeicTags();
+ status_t addDynamicDepthTags(bool maxResolution = false);
+ status_t deriveHeicTags(bool maxResolution = false);
status_t addRotateCropTags();
status_t addPreCorrectionActiveArraySize();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 24d4611..bf7e597 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -60,6 +60,7 @@
#include "device3/Camera3SharedOutputStream.h"
#include "CameraService.h"
#include "utils/CameraThreadState.h"
+#include "utils/SessionConfigurationUtils.h"
#include "utils/TraceHFR.h"
#include "utils/CameraServiceProxyWrapper.h"
@@ -69,6 +70,7 @@
using namespace android::camera3;
using namespace android::hardware::camera;
using namespace android::hardware::camera::device::V3_2;
+using android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
namespace android {
@@ -489,8 +491,13 @@
const int STREAM_WIDTH_OFFSET = 1;
const int STREAM_HEIGHT_OFFSET = 2;
const int STREAM_IS_INPUT_OFFSET = 3;
+ bool isHighResolutionSensor =
+ camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(mDeviceInfo);
+ int32_t scalerSizesTag = isHighResolutionSensor ?
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION :
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
camera_metadata_ro_entry_t availableStreamConfigs =
- mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ mDeviceInfo.find(scalerSizesTag);
if (availableStreamConfigs.count == 0 ||
availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
return camera3::Size(0, 0);
@@ -628,6 +635,8 @@
ssize_t jpegBufferSize = scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) +
kMinJpegBufferSize;
if (jpegBufferSize > maxJpegBufferSize) {
+ ALOGI("%s: jpeg buffer size calculated is > maxJpeg bufferSize(%zd), clamping",
+ __FUNCTION__, maxJpegBufferSize);
jpegBufferSize = maxJpegBufferSize;
}
@@ -647,13 +656,17 @@
return maxBytesForPointCloud;
}
-ssize_t Camera3Device::getRawOpaqueBufferSize(int32_t width, int32_t height) const {
+ssize_t Camera3Device::getRawOpaqueBufferSize(int32_t width, int32_t height,
+ bool maxResolution) const {
const int PER_CONFIGURATION_SIZE = 3;
const int WIDTH_OFFSET = 0;
const int HEIGHT_OFFSET = 1;
const int SIZE_OFFSET = 2;
camera_metadata_ro_entry rawOpaqueSizes =
- mDeviceInfo.find(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
+ mDeviceInfo.find(
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE,
+ maxResolution));
size_t count = rawOpaqueSizes.count;
if (count == 0 || (count % PER_CONFIGURATION_SIZE)) {
ALOGE("%s: Camera %s: bad opaque RAW size static metadata length(%zu)!",
@@ -1325,8 +1338,9 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared,
- bool isMultiResolution, uint64_t consumerUsage) {
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
+ uint64_t consumerUsage) {
ATRACE_CALL();
if (consumer == nullptr) {
@@ -1338,14 +1352,26 @@
consumers.push_back(consumer);
return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
- format, dataSpace, rotation, id, physicalCameraId, surfaceIds, streamSetId,
- isShared, isMultiResolution, consumerUsage);
+ format, dataSpace, rotation, id, physicalCameraId, sensorPixelModesUsed, surfaceIds,
+ streamSetId, isShared, isMultiResolution, consumerUsage);
+}
+
+static bool isRawFormat(int format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ return true;
+ default:
+ return false;
+ }
}
status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
- const String8& physicalCameraId,
+ const String8& physicalCameraId, const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
uint64_t consumerUsage) {
ATRACE_CALL();
@@ -1399,6 +1425,12 @@
return BAD_VALUE;
}
+ if (isRawFormat(format) && sensorPixelModesUsed.size() > 1) {
+ // We can't use one stream with a raw format in both sensor pixel modes since its going to
+ // be found in only one sensor pixel mode.
+ ALOGE("%s: RAW opaque stream cannot be used with > 1 sensor pixel modes", __FUNCTION__);
+ return BAD_VALUE;
+ }
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
if (dataSpace == HAL_DATASPACE_DEPTH) {
@@ -1418,28 +1450,36 @@
}
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, blobBufferSize, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
- ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
+ bool maxResolution =
+ sensorPixelModesUsed.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ sensorPixelModesUsed.end();
+ ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height, maxResolution);
if (rawOpaqueBufferSize <= 0) {
SET_ERR_L("Invalid RAW opaque buffer size %zd", rawOpaqueBufferSize);
return BAD_VALUE;
}
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else if (isShared) {
newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
width, height, format, consumerUsage, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId, mUseHalBufManager);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ mUseHalBufManager);
} else if (consumers.size() == 0 && hasDeferredConsumer) {
newStream = new Camera3OutputStream(mNextStreamId,
width, height, format, consumerUsage, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
}
size_t consumerCount = consumers.size();
@@ -3221,7 +3261,7 @@
dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
dst3_2.rotation = mapToStreamRotation((camera_stream_rotation_t) src->rotation);
// For HidlSession version 3.5 or newer, the format and dataSpace sent
- // to HAL are original, not the overriden ones.
+ // to HAL are original, not the overridden ones.
if (mHidlSession_3_5 != nullptr) {
dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden() ?
cam3stream->getOriginalFormat() : src->format);
@@ -3238,7 +3278,12 @@
}
dst3_7.v3_4 = dst3_4;
dst3_7.groupId = cam3stream->getHalStreamGroupId();
-
+ dst3_7.sensorPixelModesUsed.resize(src->sensor_pixel_modes_used.size());
+ size_t j = 0;
+ for (int mode : src->sensor_pixel_modes_used) {
+ dst3_7.sensorPixelModesUsed[j++] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
+ }
activeStreams.insert(streamId);
// Create Buffer ID map if necessary
mBufferRecords.tryCreateBufferCache(streamId);
@@ -3255,13 +3300,15 @@
}
requestedConfiguration3_2.operationMode = operationMode;
requestedConfiguration3_4.operationMode = operationMode;
+ requestedConfiguration3_7.operationMode = operationMode;
+ size_t sessionParamSize = get_camera_metadata_size(sessionParams);
requestedConfiguration3_4.sessionParams.setToExternal(
reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
- get_camera_metadata_size(sessionParams));
+ sessionParamSize);
requestedConfiguration3_7.operationMode = operationMode;
requestedConfiguration3_7.sessionParams.setToExternal(
reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
- get_camera_metadata_size(sessionParams));
+ sessionParamSize);
// Invoke configureStreams
device::V3_3::HalStreamConfiguration finalConfiguration;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 855d2e3..d9e89fd 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -132,14 +132,17 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, bool isMultiResolution = 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, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, bool isMultiResolution = false,
@@ -190,7 +193,7 @@
ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
ssize_t getPointCloudBufferSize() const;
- ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height) const;
+ ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height, bool maxResolution) const;
// Methods called by subclasses
void notifyStatus(bool idle); // updates from StatusTracker
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
index 2196c7d..8cc6833 100644
--- a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
@@ -31,7 +31,7 @@
Camera3FakeStream::Camera3FakeStream(int id) :
Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, FAKE_WIDTH, FAKE_HEIGHT,
/*maxSize*/0, FAKE_FORMAT, FAKE_DATASPACE, FAKE_ROTATION,
- FAKE_ID) {
+ FAKE_ID, std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT}) {
}
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index a837900..0204d49 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -32,10 +32,12 @@
Camera3IOStreamBase::Camera3IOStreamBase(int id, camera_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId, bool isMultiResolution) :
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
Camera3Stream(id, type,
width, height, maxSize, format, dataSpace, rotation,
- physicalCameraId, setId, isMultiResolution),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mTotalBufferCount(0),
mHandoutTotalBufferCount(0),
mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 2e744ee..90c8a7b 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -36,6 +36,7 @@
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
public:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index b00a963..6d8317b 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -33,7 +33,8 @@
uint32_t width, uint32_t height, int format) :
Camera3IOStreamBase(id, CAMERA_STREAM_INPUT, width, height, /*maxSize*/0,
format, HAL_DATASPACE_UNKNOWN, CAMERA_STREAM_ROTATION_0,
- FAKE_ID) {
+ FAKE_ID,
+ std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT}) {
if (format == HAL_PIXEL_FORMAT_BLOB) {
ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 3ec3b6b..221bebb 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -44,10 +44,11 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId, bool isMultiResolution) :
Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
/*maxSize*/0, format, dataSpace, rotation,
- physicalCameraId, setId, isMultiResolution),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true),
@@ -70,11 +71,12 @@
sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
- nsecs_t timestampOffset, const String8& physicalCameraId, int setId,
- bool isMultiResolution) :
+ nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height, maxSize,
- format, dataSpace, rotation, physicalCameraId, setId,
- isMultiResolution),
+ format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
+ setId, isMultiResolution),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true),
@@ -104,10 +106,12 @@
uint32_t width, uint32_t height, int format,
uint64_t consumerUsage, android_dataspace dataSpace,
camera_stream_rotation_t rotation, nsecs_t timestampOffset,
- const String8& physicalCameraId, int setId, bool isMultiResolution) :
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
/*maxSize*/0, format, dataSpace, rotation,
- physicalCameraId, setId, isMultiResolution),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mConsumer(nullptr),
mTransform(0),
mTraceFirstBuffer(true),
@@ -142,12 +146,13 @@
android_dataspace dataSpace,
camera_stream_rotation_t rotation,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
uint64_t consumerUsage, nsecs_t timestampOffset,
int setId, bool isMultiResolution) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
format, dataSpace, rotation,
- physicalCameraId, setId, isMultiResolution),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mTransform(0),
mTraceFirstBuffer(true),
mUseMonoTimestamp(false),
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index c82f2a6..00e4854 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -87,8 +87,8 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
-
/**
* Set up a stream for formats that have a variable buffer size for the same
* dimensions, such as compressed JPEG.
@@ -99,8 +99,8 @@
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
-
/**
* Set up a stream with deferred consumer for formats that have 2 dimensions, such as
* RAW and YUV. The consumer must be set before using this stream for output. A valid
@@ -110,6 +110,7 @@
uint64_t consumerUsage, android_dataspace dataSpace,
camera_stream_rotation_t rotation, nsecs_t timestampOffset,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
virtual ~Camera3OutputStream();
@@ -234,6 +235,7 @@
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
uint64_t consumerUsage = 0, nsecs_t timestampOffset = 0,
int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 8aa5f1a..15cf7f4 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -32,9 +32,10 @@
uint64_t consumerUsage, android_dataspace dataSpace,
camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId, bool useHalBufManager) :
Camera3OutputStream(id, CAMERA_STREAM_OUTPUT, width, height,
- format, dataSpace, rotation, physicalCameraId,
+ format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
consumerUsage, timestampOffset, setId),
mUseHalBufManager(useHalBufManager) {
size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index a61316c..4b6341b 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -38,6 +38,7 @@
uint64_t consumerUsage, android_dataspace dataSpace,
camera_stream_rotation_t rotation, nsecs_t timestampOffset,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID,
bool useHalBufManager = false);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index c6e7002..02b6585 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -49,7 +49,9 @@
camera_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId, bool isMultiResolution) :
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
camera_stream(),
mId(id),
mSetId(setId),
@@ -84,6 +86,7 @@
camera_stream::rotation = rotation;
camera_stream::max_buffers = 0;
camera_stream::physical_camera_id = mPhysicalCameraId.string();
+ camera_stream::sensor_pixel_modes_used = sensorPixelModesUsed;
if ((format == HAL_PIXEL_FORMAT_BLOB || format == HAL_PIXEL_FORMAT_RAW_OPAQUE) &&
maxSize == 0) {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 45d8478..5a364ab 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -498,7 +498,9 @@
Camera3Stream(int id, camera_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
android_dataspace dataSpace, camera_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId, bool isMultiResolution);
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution);
wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 7e6a077..2d3397c 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -62,6 +62,8 @@
android_dataspace_t data_space;
camera_stream_rotation_t rotation;
const char* physical_camera_id;
+
+ std::unordered_set<int32_t> sensor_pixel_modes_used;
} camera_stream_t;
typedef struct camera_stream_buffer {
@@ -104,13 +106,15 @@
uint64_t consumerUsage;
bool finalized = false;
bool supportsOffline = false;
+ std::unordered_set<int32_t> sensorPixelModesUsed;
OutputStreamInfo() :
width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
consumerUsage(0) {}
OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
- uint64_t _consumerUsage) :
+ uint64_t _consumerUsage, const std::unordered_set<int32_t>& _sensorPixelModesUsed) :
width(_width), height(_height), format(_format),
- dataSpace(_dataSpace), consumerUsage(_consumerUsage) {}
+ dataSpace(_dataSpace), consumerUsage(_consumerUsage),
+ sensorPixelModesUsed(_sensorPixelModesUsed) {}
};
/**
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index 316303e..89dd115 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -22,13 +22,14 @@
#include <cmath>
#include "device3/DistortionMapper.h"
+#include "utils/SessionConfigurationUtils.h"
namespace android {
namespace camera3 {
-DistortionMapper::DistortionMapper() : mValidMapping(false), mValidGrids(false) {
+DistortionMapper::DistortionMapper() {
initRemappedKeys();
}
@@ -61,41 +62,81 @@
status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) {
std::lock_guard<std::mutex> lock(mMutex);
+ status_t res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/false);
+ if (res != OK) {
+ return res;
+ }
+
+ bool mMaxResolution = SessionConfigurationUtils::isUltraHighResolutionSensor(deviceInfo);
+ if (mMaxResolution) {
+ res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/true);
+ }
+ return res;
+}
+
+status_t DistortionMapper::setupStaticInfoLocked(const CameraMetadata &deviceInfo,
+ bool maxResolution) {
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+
camera_metadata_ro_entry_t array;
- array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ array = deviceInfo.find(
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, maxResolution));
if (array.count != 4) return BAD_VALUE;
float arrayX = static_cast<float>(array.data.i32[0]);
float arrayY = static_cast<float>(array.data.i32[1]);
- mArrayWidth = static_cast<float>(array.data.i32[2]);
- mArrayHeight = static_cast<float>(array.data.i32[3]);
+ mapperInfo->mArrayWidth = static_cast<float>(array.data.i32[2]);
+ mapperInfo->mArrayHeight = static_cast<float>(array.data.i32[3]);
- array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ array = deviceInfo.find(
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, maxResolution));
if (array.count != 4) return BAD_VALUE;
float activeX = static_cast<float>(array.data.i32[0]);
float activeY = static_cast<float>(array.data.i32[1]);
- mActiveWidth = static_cast<float>(array.data.i32[2]);
- mActiveHeight = static_cast<float>(array.data.i32[3]);
+ mapperInfo->mActiveWidth = static_cast<float>(array.data.i32[2]);
+ mapperInfo->mActiveHeight = static_cast<float>(array.data.i32[3]);
- mArrayDiffX = activeX - arrayX;
- mArrayDiffY = activeY - arrayY;
+ mapperInfo->mArrayDiffX = activeX - arrayX;
+ mapperInfo->mArrayDiffY = activeY - arrayY;
- return updateCalibration(deviceInfo);
+ return updateCalibration(deviceInfo, /*isStatic*/ true, maxResolution);
+}
+
+static bool doesSettingsHaveMaxResolution(const CameraMetadata *settings) {
+ if (settings == nullptr) {
+ return false;
+ }
+ // First we get the sensorPixelMode from the settings metadata.
+ camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ return (sensorPixelModeEntry.data.u8[0] == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ }
+ return false;
}
bool DistortionMapper::calibrationValid() const {
std::lock_guard<std::mutex> lock(mMutex);
-
- return mValidMapping;
+ bool isValid = mDistortionMapperInfo.mValidMapping;
+ if (mMaxResolution) {
+ isValid = isValid && mDistortionMapperInfoMaximumResolution.mValidMapping;
+ }
+ return isValid;
}
status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) {
std::lock_guard<std::mutex> lock(mMutex);
status_t res;
- if (!mValidMapping) return OK;
+ bool maxResolution = doesSettingsHaveMaxResolution(request);
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+
+ if (!mapperInfo->mValidMapping) return OK;
camera_metadata_entry_t e;
e = request->find(ANDROID_DISTORTION_CORRECTION_MODE);
@@ -107,27 +148,30 @@
if (weight == 0) {
continue;
}
- res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true);
+ res = mapCorrectedToRaw(e.data.i32 + j, 2, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
for (auto rect : kRectsToCorrect) {
e = request->find(rect);
- res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
+ res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
-
return OK;
}
status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) {
std::lock_guard<std::mutex> lock(mMutex);
+
+ bool maxResolution = doesSettingsHaveMaxResolution(result);
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
status_t res;
- if (!mValidMapping) return OK;
+ if (!mapperInfo->mValidMapping) return OK;
- res = updateCalibration(*result);
+ res = updateCalibration(*result, /*isStatic*/ false, maxResolution);
if (res != OK) {
ALOGE("Failure to update lens calibration information");
return INVALID_OPERATION;
@@ -143,18 +187,18 @@
if (weight == 0) {
continue;
}
- res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true);
+ res = mapRawToCorrected(e.data.i32 + j, 2, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
for (auto rect : kRectsToCorrect) {
e = result->find(rect);
- res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
+ res = mapRawRectToCorrected(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
for (auto pts : kResultPointsToCorrectNoClamp) {
e = result->find(pts);
- res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false);
+ res = mapRawToCorrected(e.data.i32, e.count / 2, mapperInfo, /*clamp*/false);
if (res != OK) return res;
}
}
@@ -164,25 +208,37 @@
// Utility methods; not guarded by mutex
-status_t DistortionMapper::updateCalibration(const CameraMetadata &result) {
+status_t DistortionMapper::updateCalibration(const CameraMetadata &result, bool isStatic,
+ bool maxResolution) {
camera_metadata_ro_entry_t calib, distortion;
+ DistortionMapperInfo *mapperInfo =
+ maxResolution ? &mDistortionMapperInfoMaximumResolution : &mDistortionMapperInfo;
+ // We only need maximum resolution version of LENS_INTRINSIC_CALIBRATION and
+ // LENS_DISTORTION since CaptureResults would still use the same key
+ // regardless of sensor pixel mode.
+ int calibrationKey =
+ SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_INTRINSIC_CALIBRATION,
+ maxResolution && isStatic);
+ int distortionKey =
+ SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_DISTORTION,
+ maxResolution && isStatic);
- calib = result.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
- distortion = result.find(ANDROID_LENS_DISTORTION);
+ calib = result.find(calibrationKey);
+ distortion = result.find(distortionKey);
if (calib.count != 5) return BAD_VALUE;
if (distortion.count != 5) return BAD_VALUE;
// Skip redoing work if no change to calibration fields
- if (mValidMapping &&
- mFx == calib.data.f[0] &&
- mFy == calib.data.f[1] &&
- mCx == calib.data.f[2] &&
- mCy == calib.data.f[3] &&
- mS == calib.data.f[4]) {
+ if (mapperInfo->mValidMapping &&
+ mapperInfo->mFx == calib.data.f[0] &&
+ mapperInfo->mFy == calib.data.f[1] &&
+ mapperInfo->mCx == calib.data.f[2] &&
+ mapperInfo->mCy == calib.data.f[3] &&
+ mapperInfo->mS == calib.data.f[4]) {
bool noChange = true;
for (size_t i = 0; i < distortion.count; i++) {
- if (mK[i] != distortion.data.f[i]) {
+ if (mapperInfo->mK[i] != distortion.data.f[i]) {
noChange = false;
break;
}
@@ -190,39 +246,39 @@
if (noChange) return OK;
}
- mFx = calib.data.f[0];
- mFy = calib.data.f[1];
- mCx = calib.data.f[2];
- mCy = calib.data.f[3];
- mS = calib.data.f[4];
+ mapperInfo->mFx = calib.data.f[0];
+ mapperInfo->mFy = calib.data.f[1];
+ mapperInfo->mCx = calib.data.f[2];
+ mapperInfo->mCy = calib.data.f[3];
+ mapperInfo->mS = calib.data.f[4];
- mInvFx = 1 / mFx;
- mInvFy = 1 / mFy;
+ mapperInfo->mInvFx = 1 / mapperInfo->mFx;
+ mapperInfo->mInvFy = 1 / mapperInfo->mFy;
for (size_t i = 0; i < distortion.count; i++) {
- mK[i] = distortion.data.f[i];
+ mapperInfo->mK[i] = distortion.data.f[i];
}
- mValidMapping = true;
+ mapperInfo->mValidMapping = true;
// Need to recalculate grid
- mValidGrids = false;
+ mapperInfo->mValidGrids = false;
return OK;
}
status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount,
- bool clamp, bool simple) {
- if (!mValidMapping) return INVALID_OPERATION;
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple) {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp);
+ if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, mapperInfo, clamp);
- if (!mValidGrids) {
- status_t res = buildGrids();
+ if (!mapperInfo->mValidGrids) {
+ status_t res = buildGrids(mapperInfo);
if (res != OK) return res;
}
for (int i = 0; i < coordCount * 2; i += 2) {
- const GridQuad *quad = findEnclosingQuad(coordPairs + i, mDistortedGrid);
+ const GridQuad *quad = findEnclosingQuad(coordPairs + i, mapperInfo->mDistortedGrid);
if (quad == nullptr) {
ALOGE("Raw to corrected mapping failure: No quad found for (%d, %d)",
*(coordPairs + i), *(coordPairs + i + 1));
@@ -258,8 +314,8 @@
// Clamp to within active array
if (clamp) {
- corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
- corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ corrX = std::min(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mapperInfo->mActiveHeight - 1, std::max(0.f, corrY));
}
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
@@ -270,19 +326,19 @@
}
status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
- bool clamp) const {
- if (!mValidMapping) return INVALID_OPERATION;
+ const DistortionMapperInfo *mapperInfo, bool clamp) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- float scaleX = mActiveWidth / mArrayWidth;
- float scaleY = mActiveHeight / mArrayHeight;
+ float scaleX = mapperInfo->mActiveWidth / mapperInfo->mArrayWidth;
+ float scaleY = mapperInfo->mActiveHeight / mapperInfo->mArrayHeight;
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
float corrX = x * scaleX;
float corrY = y * scaleY;
if (clamp) {
- corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
- corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ corrX = std::min(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mapperInfo->mActiveHeight - 1, std::max(0.f, corrY));
}
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
@@ -291,9 +347,9 @@
return OK;
}
-status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
- bool simple) {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple) {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
int32_t coords[4] = {
@@ -303,7 +359,7 @@
rects[i + 1] + rects[i + 3] - 1
};
- mapRawToCorrected(coords, 2, clamp, simple);
+ mapRawToCorrected(coords, 2, mapperInfo, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -315,60 +371,60 @@
return OK;
}
-status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp,
- bool simple) const {
- return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple);
+status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ return mapCorrectedToRawImpl(coordPairs, coordCount, mapperInfo, clamp, simple);
}
template<typename T>
-status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp,
- bool simple) const {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp);
+ if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, mapperInfo, clamp);
- float activeCx = mCx - mArrayDiffX;
- float activeCy = mCy - mArrayDiffY;
+ float activeCx = mapperInfo->mCx - mapperInfo->mArrayDiffX;
+ float activeCy = mapperInfo->mCy - mapperInfo->mArrayDiffY;
for (int i = 0; i < coordCount * 2; i += 2) {
// Move to normalized space from active array space
- float ywi = (coordPairs[i + 1] - activeCy) * mInvFy;
- float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx;
+ float ywi = (coordPairs[i + 1] - activeCy) * mapperInfo->mInvFy;
+ float xwi = (coordPairs[i] - activeCx - mapperInfo->mS * ywi) * mapperInfo->mInvFx;
// Apply distortion model to calculate raw image coordinates
+ const std::array<float, 5> &kK = mapperInfo->mK;
float rSq = xwi * xwi + ywi * ywi;
- float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
- float xc = xwi * Fr + (mK[3] * 2 * xwi * ywi) + mK[4] * (rSq + 2 * xwi * xwi);
- float yc = ywi * Fr + (mK[4] * 2 * xwi * ywi) + mK[3] * (rSq + 2 * ywi * ywi);
+ float Fr = 1.f + (kK[0] * rSq) + (kK[1] * rSq * rSq) + (kK[2] * rSq * rSq * rSq);
+ float xc = xwi * Fr + (kK[3] * 2 * xwi * ywi) + kK[4] * (rSq + 2 * xwi * xwi);
+ float yc = ywi * Fr + (kK[4] * 2 * xwi * ywi) + kK[3] * (rSq + 2 * ywi * ywi);
// Move back to image space
- float xr = mFx * xc + mS * yc + mCx;
- float yr = mFy * yc + mCy;
+ float xr = mapperInfo->mFx * xc + mapperInfo->mS * yc + mapperInfo->mCx;
+ float yr = mapperInfo->mFy * yc + mapperInfo->mCy;
// Clamp to within pre-correction active array
if (clamp) {
- xr = std::min(mArrayWidth - 1, std::max(0.f, xr));
- yr = std::min(mArrayHeight - 1, std::max(0.f, yr));
+ xr = std::min(mapperInfo->mArrayWidth - 1, std::max(0.f, xr));
+ yr = std::min(mapperInfo->mArrayHeight - 1, std::max(0.f, yr));
}
coordPairs[i] = static_cast<T>(std::round(xr));
coordPairs[i + 1] = static_cast<T>(std::round(yr));
}
-
return OK;
}
template<typename T>
status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount,
- bool clamp) const {
- if (!mValidMapping) return INVALID_OPERATION;
+ const DistortionMapperInfo *mapperInfo, bool clamp) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- float scaleX = mArrayWidth / mActiveWidth;
- float scaleY = mArrayHeight / mActiveHeight;
+ float scaleX = mapperInfo->mArrayWidth / mapperInfo->mActiveWidth;
+ float scaleY = mapperInfo->mArrayHeight / mapperInfo->mActiveHeight;
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
float rawX = x * scaleX;
float rawY = y * scaleY;
if (clamp) {
- rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX));
- rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY));
+ rawX = std::min(mapperInfo->mArrayWidth - 1, std::max(0.f, rawX));
+ rawY = std::min(mapperInfo->mArrayHeight - 1, std::max(0.f, rawY));
}
coordPairs[i] = static_cast<T>(std::round(rawX));
coordPairs[i + 1] = static_cast<T>(std::round(rawY));
@@ -377,9 +433,9 @@
return OK;
}
-status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
- bool simple) const {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
@@ -390,7 +446,7 @@
rects[i + 1] + rects[i + 3] - 1
};
- mapCorrectedToRaw(coords, 2, clamp, simple);
+ mapCorrectedToRaw(coords, 2, mapperInfo, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -402,37 +458,37 @@
return OK;
}
-status_t DistortionMapper::buildGrids() {
- if (mCorrectedGrid.size() != kGridSize * kGridSize) {
- mCorrectedGrid.resize(kGridSize * kGridSize);
- mDistortedGrid.resize(kGridSize * kGridSize);
+status_t DistortionMapper::buildGrids(DistortionMapperInfo *mapperInfo) {
+ if (mapperInfo->mCorrectedGrid.size() != kGridSize * kGridSize) {
+ mapperInfo->mCorrectedGrid.resize(kGridSize * kGridSize);
+ mapperInfo->mDistortedGrid.resize(kGridSize * kGridSize);
}
- float gridMargin = mArrayWidth * kGridMargin;
- float gridSpacingX = (mArrayWidth + 2 * gridMargin) / kGridSize;
- float gridSpacingY = (mArrayHeight + 2 * gridMargin) / kGridSize;
+ float gridMargin = mapperInfo->mArrayWidth * kGridMargin;
+ float gridSpacingX = (mapperInfo->mArrayWidth + 2 * gridMargin) / kGridSize;
+ float gridSpacingY = (mapperInfo->mArrayHeight + 2 * gridMargin) / kGridSize;
size_t index = 0;
float x = -gridMargin;
for (size_t i = 0; i < kGridSize; i++, x += gridSpacingX) {
float y = -gridMargin;
for (size_t j = 0; j < kGridSize; j++, y += gridSpacingY, index++) {
- mCorrectedGrid[index].src = nullptr;
- mCorrectedGrid[index].coords = {
+ mapperInfo->mCorrectedGrid[index].src = nullptr;
+ mapperInfo->mCorrectedGrid[index].coords = {
x, y,
x + gridSpacingX, y,
x + gridSpacingX, y + gridSpacingY,
x, y + gridSpacingY
};
- mDistortedGrid[index].src = &mCorrectedGrid[index];
- mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
- status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4,
- /*clamp*/false, /*simple*/false);
+ mapperInfo->mDistortedGrid[index].src = &(mapperInfo->mCorrectedGrid[index]);
+ mapperInfo->mDistortedGrid[index].coords = mapperInfo->mCorrectedGrid[index].coords;
+ status_t res = mapCorrectedToRawImpl(mapperInfo->mDistortedGrid[index].coords.data(), 4,
+ mapperInfo, /*clamp*/false, /*simple*/false);
if (res != OK) return res;
}
}
- mValidGrids = true;
+ mapperInfo->mValidGrids = true;
return OK;
}
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
index 5027bd0..96f4fda 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.h
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -37,13 +37,8 @@
DistortionMapper();
DistortionMapper(const DistortionMapper& other) :
- mValidMapping(other.mValidMapping), mValidGrids(other.mValidGrids),
- mFx(other.mFx), mFy(other.mFy), mCx(other.mCx), mCy(other.mCy), mS(other.mS),
- mInvFx(other.mInvFx), mInvFy(other.mInvFy), mK(other.mK),
- mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight),
- mActiveWidth(other.mActiveWidth), mActiveHeight(other.mActiveHeight),
- mArrayDiffX(other.mArrayDiffX), mArrayDiffY(other.mArrayDiffY),
- mCorrectedGrid(other.mCorrectedGrid), mDistortedGrid(other.mDistortedGrid) {
+ mDistortionMapperInfo(other.mDistortionMapperInfo),
+ mDistortionMapperInfoMaximumResolution(other.mDistortionMapperInfoMaximumResolution) {
initRemappedKeys(); }
void initRemappedKeys() override;
@@ -75,10 +70,14 @@
public: // Visible for testing. Not guarded by mutex; do not use concurrently
+
+ struct DistortionMapperInfo;
+
/**
* Update lens calibration from capture results or equivalent
*/
- status_t updateCalibration(const CameraMetadata &result);
+ status_t updateCalibration(const CameraMetadata &result, bool isStatic = false,
+ bool maxResolution = false);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -89,8 +88,8 @@
* clamp: Whether to clamp the result to the bounds of the active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp,
- bool simple = true);
+ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -101,8 +100,8 @@
* clamp: Whether to clamp the result to the bounds of the active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
- bool simple = true);
+ status_t mapRawRectToCorrected(int32_t *rects, int rectCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true);
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -113,8 +112,8 @@
* clamp: Whether to clamp the result to the bounds of the precorrection active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp,
- bool simple = true) const;
+ status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true) const;
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -125,8 +124,8 @@
* clamp: Whether to clamp the result to the bounds of the precorrection active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
- bool simple = true) const;
+ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true) const;
struct GridQuad {
// Source grid quad, or null
@@ -136,6 +135,28 @@
std::array<float, 8> coords;
};
+ struct DistortionMapperInfo {
+ bool mValidMapping = false;
+ bool mValidGrids = false;
+
+ // intrisic parameters, in pixels
+ float mFx, mFy, mCx, mCy, mS;
+ // pre-calculated inverses for speed
+ float mInvFx, mInvFy;
+ // radial/tangential distortion parameters
+ std::array<float, 5> mK;
+
+ // pre-correction active array dimensions
+ float mArrayWidth, mArrayHeight;
+ // active array dimensions
+ float mActiveWidth, mActiveHeight;
+ // corner offsets between pre-correction and active arrays
+ float mArrayDiffX, mArrayDiffY;
+
+ std::vector<GridQuad> mCorrectedGrid;
+ std::vector<GridQuad> mDistortedGrid;
+ };
+
// Find which grid quad encloses the point; returns null if none do
static const GridQuad* findEnclosingQuad(
const int32_t pt[2], const std::vector<GridQuad>& grid);
@@ -153,6 +174,11 @@
// if it is false, then an interpolation coordinate for edges E14 and E23 is found.
static float calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU);
+ DistortionMapperInfo *getMapperInfo(bool maxResolution = false) {
+ return maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+ };
+
private:
mutable std::mutex mMutex;
@@ -163,39 +189,28 @@
// Fuzziness for float inequality tests
constexpr static float kFloatFuzz = 1e-4;
+ bool mMaxResolution = false;
+
+ status_t setupStaticInfoLocked(const CameraMetadata &deviceInfo, bool maxResolution);
+
// Single implementation for various mapCorrectedToRaw methods
template<typename T>
- status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const;
+ status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const;
// Simple linear interpolation option
template<typename T>
- status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const;
+ status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp) const;
- status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const;
+ status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp) const;
// Utility to create reverse mapping grids
- status_t buildGrids();
+ status_t buildGrids(DistortionMapperInfo *mapperInfo);
-
- bool mValidMapping;
- bool mValidGrids;
-
- // intrisic parameters, in pixels
- float mFx, mFy, mCx, mCy, mS;
- // pre-calculated inverses for speed
- float mInvFx, mInvFy;
- // radial/tangential distortion parameters
- std::array<float, 5> mK;
-
- // pre-correction active array dimensions
- float mArrayWidth, mArrayHeight;
- // active array dimensions
- float mActiveWidth, mActiveHeight;
- // corner offsets between pre-correction and active arrays
- float mArrayDiffX, mArrayDiffY;
-
- std::vector<GridQuad> mCorrectedGrid;
- std::vector<GridQuad> mDistortedGrid;
+ DistortionMapperInfo mDistortionMapperInfo;
+ DistortionMapperInfo mDistortionMapperInfoMaximumResolution;
}; // class DistortionMapper
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
index 1bc2081..1a39510 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
#include "device3/ZoomRatioMapper.h"
+#include "utils/SessionConfigurationUtils.h"
namespace android {
@@ -128,43 +129,120 @@
return OK;
}
+static bool getArrayWidthAndHeight(const CameraMetadata *deviceInfo,
+ int32_t arrayTag, int32_t *width, int32_t *height) {
+ if (width == nullptr || height == nullptr) {
+ ALOGE("%s: width / height nullptr", __FUNCTION__);
+ return false;
+ }
+ camera_metadata_ro_entry_t entry;
+ entry = deviceInfo->find(arrayTag);
+ if (entry.count != 4) return false;
+ *width = entry.data.i32[2];
+ *height = entry.data.i32[3];
+ return true;
+}
+
ZoomRatioMapper::ZoomRatioMapper(const CameraMetadata* deviceInfo,
bool supportNativeZoomRatio, bool usePrecorrectArray) {
initRemappedKeys();
- camera_metadata_ro_entry_t entry;
+ int32_t arrayW = 0;
+ int32_t arrayH = 0;
+ int32_t arrayMaximumResolutionW = 0;
+ int32_t arrayMaximumResolutionH = 0;
+ int32_t activeW = 0;
+ int32_t activeH = 0;
+ int32_t activeMaximumResolutionW = 0;
+ int32_t activeMaximumResolutionH = 0;
- entry = deviceInfo->find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- if (entry.count != 4) return;
- int32_t arrayW = entry.data.i32[2];
- int32_t arrayH = entry.data.i32[3];
+ if (!getArrayWidthAndHeight(deviceInfo, ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ &arrayW, &arrayH)) {
+ ALOGE("%s: Couldn't get pre correction active array size", __FUNCTION__);
+ return;
+ }
+ if (!getArrayWidthAndHeight(deviceInfo, ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ &activeW, &activeH)) {
+ ALOGE("%s: Couldn't get active array size", __FUNCTION__);
+ return;
+ }
- entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- if (entry.count != 4) return;
- int32_t activeW = entry.data.i32[2];
- int32_t activeH = entry.data.i32[3];
+ bool isUltraHighResolutionSensor =
+ camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(*deviceInfo);
+ if (isUltraHighResolutionSensor) {
+ if (!getArrayWidthAndHeight(deviceInfo,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ &arrayMaximumResolutionW, &arrayMaximumResolutionH)) {
+ ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
+ __FUNCTION__);
+ return;
+ }
+ if (!getArrayWidthAndHeight(deviceInfo,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ &activeMaximumResolutionW, &activeMaximumResolutionH)) {
+ ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
+ __FUNCTION__);
+ return;
+ }
+ }
if (usePrecorrectArray) {
mArrayWidth = arrayW;
mArrayHeight = arrayH;
+ mArrayWidthMaximumResolution = arrayMaximumResolutionW;
+ mArrayHeightMaximumResolution = arrayMaximumResolutionH;
} else {
mArrayWidth = activeW;
mArrayHeight = activeH;
+ mArrayWidthMaximumResolution = activeMaximumResolutionW;
+ mArrayHeightMaximumResolution = activeMaximumResolutionH;
}
mHalSupportsZoomRatio = supportNativeZoomRatio;
- ALOGV("%s: array size: %d x %d, mHalSupportsZoomRatio %d",
- __FUNCTION__, mArrayWidth, mArrayHeight, mHalSupportsZoomRatio);
+ ALOGV("%s: array size: %d x %d, full res array size: %d x %d, mHalSupportsZoomRatio %d",
+ __FUNCTION__, mArrayWidth, mArrayHeight, mArrayWidthMaximumResolution,
+ mArrayHeightMaximumResolution, mHalSupportsZoomRatio);
mIsValid = true;
}
+status_t ZoomRatioMapper::getArrayDimensionsToBeUsed(const CameraMetadata *settings,
+ int32_t *arrayWidth, int32_t *arrayHeight) {
+ if (settings == nullptr || arrayWidth == nullptr || arrayHeight == nullptr) {
+ return BAD_VALUE;
+ }
+ // First we get the sensorPixelMode from the settings metadata.
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT &&
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return BAD_VALUE;
+ }
+ }
+ if (sensorPixelMode == ANDROID_SENSOR_PIXEL_MODE_DEFAULT) {
+ *arrayWidth = mArrayWidth;
+ *arrayHeight = mArrayHeight;
+ } else {
+ *arrayWidth = mArrayWidthMaximumResolution;
+ *arrayHeight = mArrayHeightMaximumResolution;
+ }
+ return OK;
+}
+
status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
if (!mIsValid) return INVALID_OPERATION;
status_t res = OK;
bool zoomRatioIs1 = true;
camera_metadata_entry_t entry;
-
+ int arrayHeight, arrayWidth = 0;
+ res = getArrayDimensionsToBeUsed(request, &arrayWidth, &arrayHeight);
+ if (res != OK) {
+ return res;
+ }
entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
if (entry.count == 1 && entry.data.f[0] != 1.0f) {
zoomRatioIs1 = false;
@@ -174,19 +252,19 @@
if (cropRegionEntry.count == 4) {
int cropWidth = cropRegionEntry.data.i32[2];
int cropHeight = cropRegionEntry.data.i32[3];
- if (cropWidth < mArrayWidth && cropHeight < mArrayHeight) {
+ if (cropWidth < arrayWidth && cropHeight < arrayHeight) {
cropRegionEntry.data.i32[0] = 0;
cropRegionEntry.data.i32[1] = 0;
- cropRegionEntry.data.i32[2] = mArrayWidth;
- cropRegionEntry.data.i32[3] = mArrayHeight;
+ cropRegionEntry.data.i32[2] = arrayWidth;
+ cropRegionEntry.data.i32[3] = arrayHeight;
}
}
}
if (mHalSupportsZoomRatio && zoomRatioIs1) {
- res = separateZoomFromCropLocked(request, false/*isResult*/);
+ res = separateZoomFromCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
} else if (!mHalSupportsZoomRatio && !zoomRatioIs1) {
- res = combineZoomAndCropLocked(request, false/*isResult*/);
+ res = combineZoomAndCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
}
// If CONTROL_ZOOM_RATIO is in request, but HAL doesn't support
@@ -203,10 +281,15 @@
status_t res = OK;
+ int arrayHeight, arrayWidth = 0;
+ res = getArrayDimensionsToBeUsed(result, &arrayWidth, &arrayHeight);
+ if (res != OK) {
+ return res;
+ }
if (mHalSupportsZoomRatio && requestedZoomRatioIs1) {
- res = combineZoomAndCropLocked(result, true/*isResult*/);
+ res = combineZoomAndCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
} else if (!mHalSupportsZoomRatio && !requestedZoomRatioIs1) {
- res = separateZoomFromCropLocked(result, true/*isResult*/);
+ res = separateZoomFromCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
} else {
camera_metadata_entry_t entry = result->find(ANDROID_CONTROL_ZOOM_RATIO);
if (entry.count == 0) {
@@ -218,16 +301,22 @@
return res;
}
-float ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata) {
+status_t ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata, float *zoomRatioRet,
+ int arrayWidth, int arrayHeight) {
+ if (metadata == nullptr || zoomRatioRet == nullptr) {
+ return BAD_VALUE;
+ }
float zoomRatio = 1.0;
camera_metadata_ro_entry_t entry;
entry = metadata->find(ANDROID_SCALER_CROP_REGION);
- if (entry.count != 4) return zoomRatio;
-
+ if (entry.count != 4) {
+ *zoomRatioRet = 1;
+ return OK;
+ }
// Center of the preCorrection/active size
- float arrayCenterX = mArrayWidth / 2.0;
- float arrayCenterY = mArrayHeight / 2.0;
+ float arrayCenterX = arrayWidth / 2.0;
+ float arrayCenterY = arrayHeight / 2.0;
// Re-map crop region to coordinate system centered to (arrayCenterX,
// arrayCenterY).
@@ -237,22 +326,30 @@
float cropRegionBottom = entry.data.i32[1] + entry.data.i32[3] - arrayCenterY;
// Calculate the scaling factor for left, top, bottom, right
- float zoomRatioLeft = std::max(mArrayWidth / (2 * cropRegionLeft), 1.0f);
- float zoomRatioTop = std::max(mArrayHeight / (2 * cropRegionTop), 1.0f);
- float zoomRatioRight = std::max(mArrayWidth / (2 * cropRegionRight), 1.0f);
- float zoomRatioBottom = std::max(mArrayHeight / (2 * cropRegionBottom), 1.0f);
+ float zoomRatioLeft = std::max(arrayWidth / (2 * cropRegionLeft), 1.0f);
+ float zoomRatioTop = std::max(arrayHeight / (2 * cropRegionTop), 1.0f);
+ float zoomRatioRight = std::max(arrayWidth / (2 * cropRegionRight), 1.0f);
+ float zoomRatioBottom = std::max(arrayHeight / (2 * cropRegionBottom), 1.0f);
// Use minimum scaling factor to handle letterboxing or pillarboxing
zoomRatio = std::min(std::min(zoomRatioLeft, zoomRatioRight),
std::min(zoomRatioTop, zoomRatioBottom));
ALOGV("%s: derived zoomRatio is %f", __FUNCTION__, zoomRatio);
- return zoomRatio;
+ *zoomRatioRet = zoomRatio;
+ return OK;
}
-status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult) {
- status_t res;
- float zoomRatio = deriveZoomRatio(metadata);
+status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult,
+ int arrayWidth, int arrayHeight) {
+ float zoomRatio = 1.0;
+ status_t res = deriveZoomRatio(metadata, &zoomRatio, arrayWidth, arrayHeight);
+
+ if (res != OK) {
+ ALOGE("%s: Failed to derive zoom ratio: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
// Update zoomRatio metadata tag
res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
@@ -272,12 +369,14 @@
continue;
}
// Top left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
// Bottom right (exclusive): Use adjacent inclusive pixel to
// calculate.
entry.data.i32[j+2] -= 1;
entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
entry.data.i32[j+2] += 1;
entry.data.i32[j+3] += 1;
}
@@ -285,20 +384,22 @@
for (auto rect : kRectsToCorrect) {
entry = metadata->find(rect);
- scaleRects(entry.data.i32, entry.count / 4, zoomRatio);
+ scaleRects(entry.data.i32, entry.count / 4, zoomRatio, arrayWidth, arrayHeight);
}
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/);
+ scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/,
+ arrayWidth, arrayHeight);
}
}
return OK;
}
-status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult) {
+status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult,
+ int arrayWidth, int arrayHeight) {
float zoomRatio = 1.0f;
camera_metadata_entry_t entry;
entry = metadata->find(ANDROID_CONTROL_ZOOM_RATIO);
@@ -307,7 +408,6 @@
}
// Unscale regions with zoomRatio
- status_t res;
for (auto region : kMeteringRegionsToCorrect) {
entry = metadata->find(region);
for (size_t j = 0; j < entry.count; j += 5) {
@@ -316,29 +416,32 @@
continue;
}
// Top-left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
// Bottom-right (exclusive): Use adjacent inclusive pixel to
// calculate.
entry.data.i32[j+2] -= 1;
entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
entry.data.i32[j+2] += 1;
entry.data.i32[j+3] += 1;
}
}
for (auto rect : kRectsToCorrect) {
entry = metadata->find(rect);
- scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio);
+ scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio, arrayWidth, arrayHeight);
}
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/);
+ scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/,
+ arrayWidth, arrayHeight);
}
}
zoomRatio = 1.0;
- res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ status_t res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
if (res != OK) {
return res;
}
@@ -347,7 +450,7 @@
}
void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, bool clamp) {
+ float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight) {
// A pixel's coordinate is represented by the position of its top-left corner.
// To avoid the rounding error, we use the coordinate for the center of the
// pixel instead:
@@ -360,18 +463,18 @@
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
- float xCentered = x - (mArrayWidth - 2) / 2;
- float yCentered = y - (mArrayHeight - 2) / 2;
+ float xCentered = x - (arrayWidth - 2) / 2;
+ float yCentered = y - (arrayHeight - 2) / 2;
float scaledX = xCentered * scaleRatio;
float scaledY = yCentered * scaleRatio;
- scaledX += (mArrayWidth - 2) / 2;
- scaledY += (mArrayHeight - 2) / 2;
+ scaledX += (arrayWidth - 2) / 2;
+ scaledY += (arrayHeight - 2) / 2;
coordPairs[i] = static_cast<int32_t>(std::round(scaledX));
coordPairs[i+1] = static_cast<int32_t>(std::round(scaledY));
// Clamp to within activeArray/preCorrectionActiveArray
if (clamp) {
- int32_t right = mArrayWidth - 1;
- int32_t bottom = mArrayHeight - 1;
+ int32_t right = arrayWidth - 1;
+ int32_t bottom = arrayHeight - 1;
coordPairs[i] =
std::min(right, std::max(0, coordPairs[i]));
coordPairs[i+1] =
@@ -382,7 +485,7 @@
}
void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
- float scaleRatio) {
+ float scaleRatio, int32_t arrayWidth, int32_t arrayHeight) {
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, l+width-1, t+height-1),
// where both top-left and bottom-right are inclusive.
@@ -394,9 +497,9 @@
};
// top-left
- scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/);
+ scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
// bottom-right
- scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/);
+ scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
// Map back to (l, t, width, height)
rects[i] = coords[0];
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
index 3769299..b7a9e41 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.h
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -68,22 +68,31 @@
public: // Visible for testing. Do not use concurently.
void scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, bool clamp);
+ float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight);
bool isValid() { return mIsValid; }
private:
// const after construction
bool mHalSupportsZoomRatio;
- // active array / pre-correction array dimension
+
+ // active array / pre-correction array dimension for default and maximum
+ // resolution modes.
int32_t mArrayWidth, mArrayHeight;
+ int32_t mArrayWidthMaximumResolution, mArrayHeightMaximumResolution;
bool mIsValid = false;
- float deriveZoomRatio(const CameraMetadata* metadata);
- void scaleRects(int32_t* rects, int rectCount, float scaleRatio);
+ status_t deriveZoomRatio(const CameraMetadata* metadata, float *zoomRatio, int arrayWidth,
+ int arrayHeight);
+ void scaleRects(int32_t* rects, int rectCount, float scaleRatio, int32_t arrayWidth,
+ int32_t arrayHeight);
- status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult);
- status_t combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult);
+ status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult, int arrayWidth,
+ int arrayHeight);
+ status_t combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult, int arrayWidth,
+ int arrayHeight);
+ status_t getArrayDimensionsToBeUsed(const CameraMetadata *settings, int32_t *arrayWidth,
+ int32_t *arrayHeight);
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp b/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
index 96bab4e..88ec85c 100644
--- a/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
+++ b/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
@@ -23,6 +23,7 @@
using namespace android;
using namespace android::camera3;
+using DistortionMapperInfo = android::camera3::DistortionMapper::DistortionMapperInfo;
int32_t testActiveArray[] = {100, 100, 1000, 750};
float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f };
@@ -62,10 +63,10 @@
for (int index = 0; fdp.remaining_bytes() > 0; index++) {
input.push_back(fdp.ConsumeIntegral<int32_t>());
}
-
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
// The size argument counts how many coordinate pairs there are, so
// it is expected to be 1/2 the size of the input.
- m.mapCorrectedToRaw(input.data(), input.size()/2, clamp, simple);
+ m.mapCorrectedToRaw(input.data(), input.size()/2, mapperInfo, clamp, simple);
return 0;
}
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
index 54935c9..8331136 100644
--- a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
@@ -27,7 +27,7 @@
using namespace android;
using namespace android::camera3;
-
+using DistortionMapperInfo = android::camera3::DistortionMapper::DistortionMapperInfo;
int32_t testActiveArray[] = {100, 100, 1000, 750};
int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770};
@@ -132,14 +132,15 @@
/*preCorrectionActiveArray*/ testActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(coords.data(), 5, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], basicCoords[i]);
}
- res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true);
+ res = m.mapRawToCorrected(coords.data(), 5, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
@@ -152,14 +153,14 @@
};
auto rectsOrig = rects;
- res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true);
+ res = m.mapCorrectedRectToRaw(rects.data(), 2, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
EXPECT_EQ(rects[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true);
+ res = m.mapRawRectToCorrected(rects.data(), 2, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
@@ -176,14 +177,17 @@
/*preCorrectionActiveArray*/ activeArray.data());
auto rectsOrig = activeArray;
- res = m.mapCorrectedRectToRaw(activeArray.data(), 1, /*clamp*/true, /*simple*/ true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedRectToRaw(activeArray.data(), 1, mapperInfo, /*clamp*/true,
+ /*simple*/ true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < activeArray.size(); i++) {
EXPECT_EQ(activeArray[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(activeArray.data(), 1, /*clamp*/true, /*simple*/ true);
+ res = m.mapRawRectToCorrected(activeArray.data(), 1, mapperInfo, /*clamp*/true,
+ /*simple*/ true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < activeArray.size(); i++) {
@@ -200,7 +204,8 @@
/*preCorrectionActiveArray*/ testPreCorrActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(coords.data(), 5, mapperInfo, /*clamp*/true, /*simple*/true);
ASSERT_EQ(res, OK);
ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0);
@@ -237,12 +242,13 @@
auto origCoords = randCoords;
base::Timer correctedToRawTimer;
- res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, mapperInfo, clamp, simple);
auto correctedToRawDurationMs = correctedToRawTimer.duration();
EXPECT_EQ(res, OK);
base::Timer rawToCorrectedTimer;
- res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple);
+ res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, mapperInfo, clamp, simple);
auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration();
EXPECT_EQ(res, OK);
@@ -363,7 +369,8 @@
using namespace openCvData;
- res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false,
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, mapperInfo, /*clamp*/false,
/*simple*/false);
for (size_t i = 0; i < rawCoords.size(); i+=2) {
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
index 4e94991..ff7aafd 100644
--- a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -182,7 +182,7 @@
// Verify 1.0x zoom doesn't change the coordinates
auto coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], originalCoords[i]);
}
@@ -199,7 +199,7 @@
(width - 1) * 5.0f / 4.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoords[i]), kMaxAllowedPixelError);
}
@@ -216,7 +216,7 @@
width - 1.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedInc[i]), kMaxAllowedPixelError);
}
@@ -233,7 +233,7 @@
width - 1.0f, height / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedExc[i]), kMaxAllowedPixelError);
}
@@ -250,7 +250,7 @@
(width - 1) * 5 / 8.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom-in
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 0557fcc..76927c0 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -120,6 +120,21 @@
proxyBinder->pingForUserUpdate();
}
+bool CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(
+ String16 packageName, int sensorOrientation, int lensFacing) {
+ sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ if (proxyBinder == nullptr) return true;
+ bool ret = true;
+ auto status = proxyBinder->isRotateAndCropOverrideNeeded(packageName, sensorOrientation,
+ lensFacing, &ret);
+ if (!status.isOk()) {
+ ALOGE("%s: Failed during top activity orientation query: %s", __FUNCTION__,
+ status.exceptionMessage().c_str());
+ }
+
+ return ret;
+}
+
void CameraServiceProxyWrapper::updateProxyDeviceState(const CameraSessionStats& sessionStats) {
sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
if (proxyBinder == nullptr) return;
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index 9525935..ad9db68 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -90,6 +90,10 @@
// Ping camera service proxy for user update
static void pingCameraServiceProxy();
+
+ // Check whether the current top activity needs a rotate and crop override.
+ static bool isRotateAndCropOverrideNeeded(String16 packageName, int sensorOrientation,
+ int lensFacing);
};
} // android
diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp
index 8a0303a..485705c 100644
--- a/services/camera/libcameraservice/utils/ExifUtils.cpp
+++ b/services/camera/libcameraservice/utils/ExifUtils.cpp
@@ -916,11 +916,25 @@
ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__);
}
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = metadata.find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT ||
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return false;
+ }
+ }
+ int32_t activeArrayTag = sensorPixelMode == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION ?
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION :
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE;
if (metadata.exists(ANDROID_SCALER_CROP_REGION) &&
- staticInfo.exists(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE)) {
+ staticInfo.exists(activeArrayTag)) {
entry = metadata.find(ANDROID_SCALER_CROP_REGION);
camera_metadata_ro_entry activeArrayEntry =
- staticInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ staticInfo.find(activeArrayTag);
if (!setDigitalZoomRatio(entry.data.i32[2], entry.data.i32[3],
activeArrayEntry.data.i32[2], activeArrayEntry.data.i32[3])) {
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 8f42a85..6dcf440 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -21,22 +21,115 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
-// Convenience methods for constructing binder::Status objects for error returns
-
-#define STATUS_ERROR(errorCode, errorString) \
- binder::Status::fromServiceSpecificError(errorCode, \
- String8::format("%s:%d: %s", __FUNCTION__, __LINE__, errorString))
-
-#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
- binder::Status::fromServiceSpecificError(errorCode, \
- String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, \
- __VA_ARGS__))
-
using android::camera3::OutputStreamInfo;
using android::camera3::OutputStreamInfo;
using android::hardware::camera2::ICameraDeviceUser;
+using android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
namespace android {
+namespace camera3 {
+
+void StreamConfiguration::getStreamConfigurations(
+ const CameraMetadata &staticInfo, int configuration,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm) {
+ if (scm == nullptr) {
+ ALOGE("%s: StreamConfigurationMap nullptr", __FUNCTION__);
+ return;
+ }
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_IS_INPUT_OFFSET = 3;
+
+ camera_metadata_ro_entry availableStreamConfigs = staticInfo.find(configuration);
+ for (size_t i = 0; i < availableStreamConfigs.count; i += 4) {
+ int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ StreamConfiguration sc = {format, width, height, isInput};
+ (*scm)[format].push_back(sc);
+ }
+}
+
+void StreamConfiguration::getStreamConfigurations(
+ const CameraMetadata &staticInfo, bool maxRes,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm) {
+ int32_t scalerKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxRes);
+
+ int32_t depthKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxRes);
+
+ int32_t dynamicDepthKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
+
+ int32_t heicKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
+
+ getStreamConfigurations(staticInfo, scalerKey, scm);
+ getStreamConfigurations(staticInfo, depthKey, scm);
+ getStreamConfigurations(staticInfo, dynamicDepthKey, scm);
+ getStreamConfigurations(staticInfo, heicKey, scm);
+}
+
+int32_t SessionConfigurationUtils::getAppropriateModeTag(int32_t defaultTag, bool maxResolution) {
+ if (!maxResolution) {
+ return defaultTag;
+ }
+ switch (defaultTag) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS:
+ return ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:
+ return ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:
+ return ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS;
+ case ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS;
+ case ANDROID_SENSOR_OPAQUE_RAW_SIZE:
+ return ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION;
+ case ANDROID_LENS_INTRINSIC_CALIBRATION:
+ return ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION;
+ case ANDROID_LENS_DISTORTION:
+ return ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION;
+ default:
+ ALOGE("%s: Tag %d doesn't have a maximum resolution counterpart", __FUNCTION__,
+ defaultTag);
+ return -1;
+ }
+ return -1;
+}
+
+
+StreamConfigurationPair
+SessionConfigurationUtils::getStreamConfigurationPair(const CameraMetadata &staticInfo) {
+ camera3::StreamConfigurationPair streamConfigurationPair;
+ camera3::StreamConfiguration::getStreamConfigurations(staticInfo, false,
+ &streamConfigurationPair.mDefaultStreamConfigurationMap);
+ camera3::StreamConfiguration::getStreamConfigurations(staticInfo, true,
+ &streamConfigurationPair.mMaximumResolutionStreamConfigurationMap);
+ return streamConfigurationPair;
+}
int64_t SessionConfigurationUtils::euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
int64_t d0 = x0 - x1;
@@ -45,15 +138,22 @@
}
bool SessionConfigurationUtils::roundBufferDimensionNearest(int32_t width, int32_t height,
- int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
- /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
+ int32_t format, android_dataspace dataSpace,
+ const CameraMetadata& info, bool maxResolution, /*out*/int32_t* outWidth,
+ /*out*/int32_t* outHeight) {
+ const int32_t depthSizesTag =
+ getAppropriateModeTag(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+ maxResolution);
+ const int32_t scalerSizesTag =
+ getAppropriateModeTag(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t heicSizesTag =
+ getAppropriateModeTag(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, maxResolution);
camera_metadata_ro_entry streamConfigs =
- (dataSpace == HAL_DATASPACE_DEPTH) ?
- info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) :
+ (dataSpace == HAL_DATASPACE_DEPTH) ? info.find(depthSizesTag) :
(dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_HEIF)) ?
- info.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS) :
- info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ info.find(heicSizesTag) :
+ info.find(scalerSizesTag);
int32_t bestWidth = -1;
int32_t bestHeight = -1;
@@ -128,11 +228,11 @@
binder::Status SessionConfigurationUtils::createSurfaceFromGbp(
OutputStreamInfo& streamInfo, bool isStreamInfoValid,
sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
- const String8 &cameraId, const CameraMetadata &physicalCameraMetadata) {
-
+ const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
+ const std::vector<int32_t> &sensorPixelModesUsed){
// bufferProducer must be non-null
if (gbp == nullptr) {
- String8 msg = String8::format("Camera %s: Surface is NULL", cameraId.string());
+ String8 msg = String8::format("Camera %s: Surface is NULL", logicalCameraId.string());
ALOGW("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -144,13 +244,13 @@
status_t err;
if ((err = gbp->getConsumerUsage(&consumerUsage)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface consumer usage: %s (%d)",
- cameraId.string(), strerror(-err), err);
+ logicalCameraId.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: %" PRIu64 ": Forcing asynchronous mode for"
- "stream", __FUNCTION__, cameraId.string(), consumerUsage);
+ "stream", __FUNCTION__, logicalCameraId.string(), consumerUsage);
useAsync = true;
}
@@ -169,26 +269,26 @@
android_dataspace dataSpace;
if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface width: %s (%d)",
- cameraId.string(), strerror(-err), err);
+ logicalCameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_HEIGHT, &height)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface height: %s (%d)",
- cameraId.string(), strerror(-err), err);
+ logicalCameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface format: %s (%d)",
- cameraId.string(), strerror(-err), err);
+ logicalCameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE,
reinterpret_cast<int*>(&dataSpace))) != OK) {
String8 msg = String8::format("Camera %s: Failed to query Surface dataspace: %s (%d)",
- cameraId.string(), strerror(-err), err);
+ logicalCameraId.string(), strerror(-err), err);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
@@ -199,16 +299,31 @@
((consumerUsage & GRALLOC_USAGE_HW_MASK) &&
((consumerUsage & GRALLOC_USAGE_SW_READ_MASK) == 0))) {
ALOGW("%s: Camera %s: Overriding format %#x to IMPLEMENTATION_DEFINED",
- __FUNCTION__, cameraId.string(), format);
+ __FUNCTION__, logicalCameraId.string(), format);
format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
}
+ std::unordered_set<int32_t> overriddenSensorPixelModes;
+ if (checkAndOverrideSensorPixelModesUsed(sensorPixelModesUsed, format, width, height,
+ physicalCameraMetadata, flexibleConsumer, &overriddenSensorPixelModes) != OK) {
+ String8 msg = String8::format("Camera %s: sensor pixel modes for stream with "
+ "format %#x are not valid",logicalCameraId.string(), format);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ bool foundInMaxRes = false;
+ if (overriddenSensorPixelModes.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ overriddenSensorPixelModes.end()) {
+ // we can use the default stream configuration map
+ foundInMaxRes = true;
+ }
// Round dimensions to the nearest dimensions available for this format
if (flexibleConsumer && isPublicFormat(format) &&
!SessionConfigurationUtils::roundBufferDimensionNearest(width, height,
- format, dataSpace, physicalCameraMetadata, /*out*/&width, /*out*/&height)) {
+ format, dataSpace, physicalCameraMetadata, foundInMaxRes, /*out*/&width,
+ /*out*/&height)) {
String8 msg = String8::format("Camera %s: No supported stream configurations with "
"format %#x defined, failed to create output stream",
- cameraId.string(), format);
+ logicalCameraId.string(), format);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -219,30 +334,31 @@
streamInfo.format = format;
streamInfo.dataSpace = dataSpace;
streamInfo.consumerUsage = consumerUsage;
+ streamInfo.sensorPixelModesUsed = overriddenSensorPixelModes;
return binder::Status::ok();
}
if (width != streamInfo.width) {
String8 msg = String8::format("Camera %s:Surface width doesn't match: %d vs %d",
- cameraId.string(), width, streamInfo.width);
+ logicalCameraId.string(), width, streamInfo.width);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (height != streamInfo.height) {
String8 msg = String8::format("Camera %s:Surface height doesn't match: %d vs %d",
- cameraId.string(), height, streamInfo.height);
+ logicalCameraId.string(), height, streamInfo.height);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (format != streamInfo.format) {
String8 msg = String8::format("Camera %s:Surface format doesn't match: %d vs %d",
- cameraId.string(), format, streamInfo.format);
+ logicalCameraId.string(), format, streamInfo.format);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dataSpace != streamInfo.dataSpace) {
String8 msg = String8::format("Camera %s:Surface dataSpace doesn't match: %d vs %d",
- cameraId.string(), dataSpace, streamInfo.dataSpace);
+ logicalCameraId.string(), dataSpace, streamInfo.dataSpace);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -251,7 +367,7 @@
if (consumerUsage != streamInfo.consumerUsage) {
String8 msg = String8::format(
"Camera %s:Surface usage flag doesn't match %" PRIu64 " vs %" PRIu64 "",
- cameraId.string(), consumerUsage, streamInfo.consumerUsage);
+ logicalCameraId.string(), consumerUsage, streamInfo.consumerUsage);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -259,7 +375,6 @@
return binder::Status::ok();
}
-
void SessionConfigurationUtils::mapStreamInfo(const OutputStreamInfo &streamInfo,
camera3::camera_stream_rotation_t rotation, String8 physicalId,
int32_t groupId, hardware::camera::device::V3_7::Stream *stream /*out*/) {
@@ -280,6 +395,12 @@
stream->v3_4.physicalCameraId = std::string(physicalId.string());
stream->v3_4.bufferSize = 0;
stream->groupId = groupId;
+ stream->sensorPixelModesUsed.resize(streamInfo.sensorPixelModesUsed.size());
+ size_t idx = 0;
+ for (auto mode : streamInfo.sensorPixelModesUsed) {
+ stream->sensorPixelModesUsed[idx++] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
+ }
}
binder::Status SessionConfigurationUtils::checkPhysicalCameraId(
@@ -394,6 +515,11 @@
streamConfiguration.streams.resize(streamCount);
size_t streamIdx = 0;
if (isInputValid) {
+ hardware::hidl_vec<CameraMetadataEnumAndroidSensorPixelMode> defaultSensorPixelModes;
+ defaultSensorPixelModes.resize(1);
+ defaultSensorPixelModes[0] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(
+ ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
streamConfiguration.streams[streamIdx++] = {{{/*streamId*/0,
hardware::camera::device::V3_2::StreamType::INPUT,
static_cast<uint32_t> (sessionConfiguration.getInputWidth()),
@@ -401,7 +527,7 @@
Camera3Device::mapToPixelFormat(sessionConfiguration.getInputFormat()),
/*usage*/ 0, HAL_DATASPACE_UNKNOWN,
hardware::camera::device::V3_2::StreamRotation::ROTATION_0},
- /*physicalId*/ nullptr, /*bufferSize*/0}, /*groupId*/-1};
+ /*physicalId*/ nullptr, /*bufferSize*/0}, /*groupId*/-1, defaultSensorPixelModes};
streamConfiguration.multiResolutionInputImage =
sessionConfiguration.inputIsMultiResolution();
}
@@ -411,6 +537,12 @@
it.getGraphicBufferProducers();
bool deferredConsumer = it.isDeferred();
String8 physicalCameraId = String8(it.getPhysicalCameraId());
+
+ std::vector<int32_t> sensorPixelModesUsed = it.getSensorPixelModesUsed();
+ const CameraMetadata &physicalDeviceInfo = getMetadata(physicalCameraId);
+ const CameraMetadata &metadataChosen =
+ physicalCameraId.size() > 0 ? physicalDeviceInfo : deviceInfo;
+
size_t numBufferProducers = bufferProducers.size();
bool isStreamInfoValid = false;
int32_t groupId = it.isMultiResolution() ? it.getSurfaceSetID() : -1;
@@ -436,6 +568,15 @@
if (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) {
streamInfo.consumerUsage |= GraphicBuffer::USAGE_HW_COMPOSER;
}
+ if (checkAndOverrideSensorPixelModesUsed(sensorPixelModesUsed,
+ streamInfo.format, streamInfo.width,
+ streamInfo.height, metadataChosen, false /*flexibleConsumer*/,
+ &streamInfo.sensorPixelModesUsed) != OK) {
+ ALOGE("%s: Deferred surface sensor pixel modes not valid",
+ __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Deferred surface sensor pixel modes not valid");
+ }
mapStreamInfo(streamInfo, camera3::CAMERA_STREAM_ROTATION_0, physicalCameraId, groupId,
&streamConfiguration.streams[streamIdx++]);
isStreamInfoValid = true;
@@ -447,10 +588,8 @@
for (auto& bufferProducer : bufferProducers) {
sp<Surface> surface;
- const CameraMetadata &physicalDeviceInfo = getMetadata(physicalCameraId);
res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
- logicalCameraId,
- physicalCameraId.size() > 0 ? physicalDeviceInfo : deviceInfo );
+ logicalCameraId, metadataChosen, sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -465,6 +604,7 @@
// additional internal camera streams.
std::vector<OutputStreamInfo> compositeStreams;
if (isDepthCompositeStream) {
+ // TODO: Take care of composite streams.
ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
deviceInfo, &compositeStreams);
} else {
@@ -505,7 +645,97 @@
}
}
return binder::Status::ok();
+}
+static bool inStreamConfigurationMap(int format, int width, int height,
+ const std::unordered_map<int, std::vector<camera3::StreamConfiguration>> &sm) {
+ auto scs = sm.find(format);
+ if (scs == sm.end()) {
+ return false;
+ }
+ for (auto &sc : scs->second) {
+ if (sc.width == width && sc.height == height && sc.isInput == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static std::unordered_set<int32_t> convertToSet(const std::vector<int32_t> &sensorPixelModesUsed) {
+ return std::unordered_set<int32_t>(sensorPixelModesUsed.begin(), sensorPixelModesUsed.end());
+}
+
+status_t SessionConfigurationUtils::checkAndOverrideSensorPixelModesUsed(
+ const std::vector<int32_t> &sensorPixelModesUsed, int format, int width, int height,
+ const CameraMetadata &staticInfo, bool flexibleConsumer,
+ std::unordered_set<int32_t> *overriddenSensorPixelModesUsed) {
+ if (!isUltraHighResolutionSensor(staticInfo)) {
+ overriddenSensorPixelModesUsed->clear();
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+
+ StreamConfigurationPair streamConfigurationPair = getStreamConfigurationPair(staticInfo);
+ const std::unordered_set<int32_t> &sensorPixelModesUsedSet =
+ convertToSet(sensorPixelModesUsed);
+ bool isInDefaultStreamConfigurationMap =
+ inStreamConfigurationMap(format, width, height,
+ streamConfigurationPair.mDefaultStreamConfigurationMap);
+
+ bool isInMaximumResolutionStreamConfigurationMap =
+ inStreamConfigurationMap(format, width, height,
+ streamConfigurationPair.mMaximumResolutionStreamConfigurationMap);
+
+ // Case 1: The client has not changed the sensor mode defaults. In this case, we check if the
+ // size + format of the OutputConfiguration is found exclusively in 1.
+ // If yes, add that sensorPixelMode to overriddenSensorPixelModes.
+ // If no, add 'DEFAULT' to sensorPixelMode. This maintains backwards
+ // compatibility.
+ if (sensorPixelModesUsedSet.size() == 0) {
+ // Ambiguous case, default to only 'DEFAULT' mode.
+ if (isInDefaultStreamConfigurationMap && isInMaximumResolutionStreamConfigurationMap) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+ // We don't allow flexible consumer for max resolution mode.
+ if (isInMaximumResolutionStreamConfigurationMap) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ return OK;
+ }
+ if (isInDefaultStreamConfigurationMap || (flexibleConsumer && width < ROUNDING_WIDTH_CAP)) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+
+ // Case2: The app has set sensorPixelModesUsed, we need to verify that they
+ // are valid / err out.
+ if (sensorPixelModesUsedSet.find(ANDROID_SENSOR_PIXEL_MODE_DEFAULT) !=
+ sensorPixelModesUsedSet.end() && !isInDefaultStreamConfigurationMap) {
+ return BAD_VALUE;
+ }
+
+ if (sensorPixelModesUsedSet.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ sensorPixelModesUsedSet.end() && !isInMaximumResolutionStreamConfigurationMap) {
+ return BAD_VALUE;
+ }
+ *overriddenSensorPixelModesUsed = sensorPixelModesUsedSet;
+ return OK;
+}
+
+bool SessionConfigurationUtils::isUltraHighResolutionSensor(const CameraMetadata &deviceInfo) {
+ camera_metadata_ro_entry_t entryCap;
+ entryCap = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ // Go through the capabilities and check if it has
+ // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+ for (size_t i = 0; i < entryCap.count; ++i) {
+ uint8_t capability = entryCap.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR) {
+ return true;
+ }
+ }
+ return false;
}
bool SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
@@ -531,4 +761,5 @@
return true;
}
-}// namespace android
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index 36e1dd7..863a0cd 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -22,24 +22,60 @@
#include <camera/camera2/SessionConfiguration.h>
#include <camera/camera2/SubmitInfo.h>
#include <android/hardware/camera/device/3.7/types.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.7/ICameraDeviceSession.h>
#include <device3/Camera3StreamInterface.h>
#include <stdint.h>
+// Convenience methods for constructing binder::Status objects for error returns
+
+#define STATUS_ERROR(errorCode, errorString) \
+ binder::Status::fromServiceSpecificError(errorCode, \
+ String8::format("%s:%d: %s", __FUNCTION__, __LINE__, errorString))
+
+#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
+ binder::Status::fromServiceSpecificError(errorCode, \
+ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, \
+ __VA_ARGS__))
+
namespace android {
+namespace camera3 {
typedef std::function<CameraMetadata (const String8 &)> metadataGetter;
+class StreamConfiguration {
+public:
+ int32_t format;
+ int32_t width;
+ int32_t height;
+ int32_t isInput;
+ static void getStreamConfigurations(
+ const CameraMetadata &static_info, bool maxRes,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm);
+ static void getStreamConfigurations(
+ const CameraMetadata &static_info, int configuration,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm);
+};
+
+// Holds the default StreamConfigurationMap and Maximum resolution
+// StreamConfigurationMap for a camera device.
+struct StreamConfigurationPair {
+ std::unordered_map<int, std::vector<camera3::StreamConfiguration>>
+ mDefaultStreamConfigurationMap;
+ std::unordered_map<int, std::vector<camera3::StreamConfiguration>>
+ mMaximumResolutionStreamConfigurationMap;
+};
+
class SessionConfigurationUtils {
public:
-
static int64_t euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
// Find the closest dimensions for a given format in available stream configurations with
// a width <= ROUNDING_WIDTH_CAP
static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format,
- android_dataspace dataSpace, const CameraMetadata& info,
+ android_dataspace dataSpace, const CameraMetadata& info, bool maxResolution,
/*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
//check if format is not custom format
@@ -50,7 +86,8 @@
static binder::Status createSurfaceFromGbp(
camera3::OutputStreamInfo& streamInfo, bool isStreamInfoValid,
sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
- const String8 &cameraId, const CameraMetadata &physicalCameraMetadata);
+ const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
+ const std::vector<int32_t> &sensorPixelModesUsed);
static void mapStreamInfo(const camera3::OutputStreamInfo &streamInfo,
camera3::camera_stream_rotation_t rotation, String8 physicalId, int32_t groupId,
@@ -86,10 +123,23 @@
hardware::camera::device::V3_4::StreamConfiguration &streamConfigV34,
const hardware::camera::device::V3_7::StreamConfiguration &streamConfigV37);
+ static StreamConfigurationPair getStreamConfigurationPair(const CameraMetadata &metadata);
+
+ static status_t checkAndOverrideSensorPixelModesUsed(
+ const std::vector<int32_t> &sensorPixelModesUsed, int format, int width, int height,
+ const CameraMetadata &staticInfo, bool flexibleConsumer,
+ std::unordered_set<int32_t> *overriddenSensorPixelModesUsed);
+
+ static bool isUltraHighResolutionSensor(const CameraMetadata &deviceInfo);
+
+ static int32_t getAppropriateModeTag(int32_t defaultTag, bool maxResolution = false);
+
static const int32_t MAX_SURFACES_PER_STREAM = 4;
static const int32_t ROUNDING_WIDTH_CAP = 1920;
+
};
+} // camera3
} // android
#endif
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 4ef87e4..ca918a9 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -140,7 +140,6 @@
static constexpr const char * const AAudioStreamFields[] {
"mediametrics_aaudiostream_reported",
- "caller_name",
"path",
"direction",
"frames_per_burst",
@@ -156,6 +155,8 @@
"format_app",
"format_device",
"log_session_id",
+ "sample_rate",
+ "content_type",
};
/**
@@ -932,12 +933,6 @@
const std::shared_ptr<const android::mediametrics::Item> &item, CallerPath path) const {
const std::string& key = item->getKey();
- std::string callerNameStr;
- mAudioAnalytics.mAnalyticsState->timeMachine().get(
- key, AMEDIAMETRICS_PROP_CALLERNAME, &callerNameStr);
-
- const auto callerName = types::lookup<types::CALLER_NAME, int32_t>(callerNameStr);
-
std::string directionStr;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_DIRECTION, &directionStr);
@@ -994,8 +989,16 @@
std::string logSessionId;
// TODO: log logSessionId
+ int32_t sampleRate = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
+
+ std::string contentTypeStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentTypeStr);
+ const auto contentType = types::lookup<types::CONTENT_TYPE, int32_t>(contentTypeStr);
+
LOG(LOG_LEVEL) << "key:" << key
- << " caller_name:" << callerName << "(" << callerNameStr << ")"
<< " path:" << path
<< " direction:" << direction << "(" << directionStr << ")"
<< " frames_per_burst:" << framesPerBurst
@@ -1010,14 +1013,15 @@
<< " device_type:" << serializedDeviceTypes
<< " format_app:" << formatApp
<< " format_device: " << formatDevice << "(" << formatDeviceStr << ")"
- << " log_session_id: " << logSessionId;
+ << " log_session_id: " << logSessionId
+ << " sample_rate: " << sampleRate
+ << " content_type: " << contentType << "(" << contentTypeStr << ")";
if (mAudioAnalytics.mDeliverStatistics) {
android::util::BytesField bf_serialized(
serializedDeviceTypes.c_str(), serializedDeviceTypes.size());
const auto result = sendToStatsd(
CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
- , callerName
, path
, direction
, framesPerBurst
@@ -1033,12 +1037,13 @@
, formatApp
, formatDevice
, logSessionId.c_str()
+ , sampleRate
+ , contentType
);
std::stringstream ss;
ss << "result:" << result;
const auto fieldsStr = printFields(AAudioStreamFields,
CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
- , callerName
, path
, direction
, framesPerBurst
@@ -1054,6 +1059,8 @@
, formatApp
, formatDevice
, logSessionId.c_str()
+ , sampleRate
+ , contentType
);
ss << " " << fieldsStr;
std::string str = ss.str();
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index 44e96ec..1756c98 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -158,9 +158,9 @@
// DO NOT MODIFY VALUES(OK to add new ones).
// This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
static std::unordered_map<std::string, int32_t> map {
- // UNKNOWN is -1
- {"AAUDIO_DIRECTION_OUTPUT", 0},
- {"AAUDIO_DIRECTION_INPUT", 1},
+ // UNKNOWN is 0
+ {"AAUDIO_DIRECTION_OUTPUT", 1 /* AAUDIO_DIRECTION_OUTPUT + 1 */},
+ {"AAUDIO_DIRECTION_INPUT", 2 /* AAUDIO_DIRECTION_INPUT + 1*/},
};
return map;
}
@@ -169,7 +169,7 @@
// DO NOT MODIFY VALUES(OK to add new ones).
// This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
static std::unordered_map<std::string, int32_t> map {
- // UNKNOWN is -1
+ // UNKNOWN is 0
{"AAUDIO_PERFORMANCE_MODE_NONE", 10},
{"AAUDIO_PERFORMANCE_MODE_POWER_SAVING", 11},
{"AAUDIO_PERFORMANCE_MODE_LOW_LATENCY", 12},
@@ -181,9 +181,9 @@
// DO NOT MODIFY VALUES(OK to add new ones).
// This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
static std::unordered_map<std::string, int32_t> map {
- // UNKNOWN is -1
- {"AAUDIO_SHARING_MODE_EXCLUSIVE", 0},
- {"AAUDIO_SHARING_MODE_SHARED", 1},
+ // UNKNOWN is 0
+ {"AAUDIO_SHARING_MODE_EXCLUSIVE", 1 /* AAUDIO_SHARING_MODE_EXCLUSIVE + 1 */},
+ {"AAUDIO_SHARING_MODE_SHARED", 2 /* AAUDIO_SHARING_MODE_SHARED + 1 */},
};
return map;
}
@@ -484,7 +484,7 @@
auto& map = getAAudioDirection();
auto it = map.find(direction);
if (it == map.end()) {
- return -1; // return unknown
+ return 0; // return unknown
}
return it->second;
}
@@ -506,7 +506,7 @@
auto& map = getAAudioPerformanceMode();
auto it = map.find(performanceMode);
if (it == map.end()) {
- return -1; // return unknown
+ return 0; // return unknown
}
return it->second;
}
@@ -528,7 +528,7 @@
auto& map = getAAudioSharingMode();
auto it = map.find(sharingMode);
if (it == map.end()) {
- return -1; // return unknown
+ return 0; // return unknown
}
return it->second;
}
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index bfc722e..7ee731e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -23,6 +23,7 @@
#include <pwd.h> //getpwuid
+#include <android-base/stringprintf.h>
#include <android/content/pm/IPackageManagerNative.h> // package info
#include <audio_utils/clock.h> // clock conversions
#include <binder/IPCThreadState.h> // get calling uid
@@ -37,6 +38,7 @@
namespace android {
+using base::StringPrintf;
using mediametrics::Item;
using mediametrics::startsWith;
@@ -211,14 +213,12 @@
status_t MediaMetricsService::dump(int fd, const Vector<String16>& args)
{
- String8 result;
-
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- result.appendFormat("Permission Denial: "
+ const std::string result = StringPrintf("Permission Denial: "
"can't dump MediaMetricsService from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return NO_ERROR;
}
@@ -250,17 +250,18 @@
// dumpsys media.metrics audiotrack,codec
// or dumpsys media.metrics audiotrack codec
- result.append("Recognized parameters:\n");
- result.append("--all show all records\n");
- result.append("--clear clear out saved records\n");
- result.append("--heap show heap usage (top 100)\n");
- result.append("--help display help\n");
- result.append("--prefix X process records for component X\n");
- result.append("--since X X < 0: records from -X seconds in the past\n");
- result.append(" X = 0: ignore\n");
- result.append(" X > 0: records from X seconds since Unix epoch\n");
- result.append("--unreachable show unreachable memory (leaks)\n");
- write(fd, result.string(), result.size());
+ static constexpr char result[] =
+ "Recognized parameters:\n"
+ "--all show all records\n"
+ "--clear clear out saved records\n"
+ "--heap show heap usage (top 100)\n"
+ "--help display help\n"
+ "--prefix X process records for component X\n"
+ "--since X X < 0: records from -X seconds in the past\n"
+ " X = 0: ignore\n"
+ " X > 0: records from X seconds since Unix epoch\n"
+ "--unreachable show unreachable memory (leaks)\n";
+ write(fd, result, std::size(result));
return NO_ERROR;
} else if (args[i] == prefixOption) {
++i;
@@ -286,7 +287,7 @@
unreachable = true;
}
}
-
+ std::stringstream result;
{
std::lock_guard _l(mLock);
@@ -295,21 +296,22 @@
mItems.clear();
mAudioAnalytics.clear();
} else {
- result.appendFormat("Dump of the %s process:\n", kServiceName);
+ result << StringPrintf("Dump of the %s process:\n", kServiceName);
const char *prefixptr = prefix.size() > 0 ? prefix.c_str() : nullptr;
- dumpHeaders(result, sinceNs, prefixptr);
- dumpQueue(result, sinceNs, prefixptr);
+ result << dumpHeaders(sinceNs, prefixptr);
+ result << dumpQueue(sinceNs, prefixptr);
// TODO: maybe consider a better way of dumping audio analytics info.
const int32_t linesToDump = all ? INT32_MAX : 1000;
auto [ dumpString, lines ] = mAudioAnalytics.dump(linesToDump, sinceNs, prefixptr);
- result.append(dumpString.c_str());
+ result << dumpString;
if (lines == linesToDump) {
- result.append("-- some lines may be truncated --\n");
+ result << "-- some lines may be truncated --\n";
}
}
}
- write(fd, result.string(), result.size());
+ const std::string str = result.str();
+ write(fd, str.c_str(), str.size());
// Check heap and unreachable memory outside of lock.
if (heap) {
@@ -327,38 +329,37 @@
}
// dump headers
-void MediaMetricsService::dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix)
+std::string MediaMetricsService::dumpHeaders(int64_t sinceNs, const char* prefix)
{
+ std::stringstream result;
if (mediametrics::Item::isEnabled()) {
- result.append("Metrics gathering: enabled\n");
+ result << "Metrics gathering: enabled\n";
} else {
- result.append("Metrics gathering: DISABLED via property\n");
+ result << "Metrics gathering: DISABLED via property\n";
}
- result.appendFormat(
+ result << StringPrintf(
"Since Boot: Submissions: %lld Accepted: %lld\n",
(long long)mItemsSubmitted.load(), (long long)mItemsFinalized);
- result.appendFormat(
+ result << StringPrintf(
"Records Discarded: %lld (by Count: %lld by Expiration: %lld)\n",
(long long)mItemsDiscarded, (long long)mItemsDiscardedCount,
(long long)mItemsDiscardedExpire);
if (prefix != nullptr) {
- result.appendFormat("Restricting to prefix %s", prefix);
+ result << "Restricting to prefix " << prefix << "\n";
}
if (sinceNs != 0) {
- result.appendFormat(
- "Emitting Queue entries more recent than: %lld\n",
- (long long)sinceNs);
+ result << "Emitting Queue entries more recent than: " << sinceNs << "\n";
}
+ return result.str();
}
// TODO: should prefix be a set<string>?
-void MediaMetricsService::dumpQueue(String8 &result, int64_t sinceNs, const char* prefix)
+std::string MediaMetricsService::dumpQueue(int64_t sinceNs, const char* prefix)
{
if (mItems.empty()) {
- result.append("empty\n");
- return;
+ return "empty\n";
}
-
+ std::stringstream result;
int slot = 0;
for (const auto &item : mItems) { // TODO: consider std::lower_bound() on mItems
if (item->getTimestamp() < sinceNs) { // sinceNs == 0 means all items shown
@@ -369,9 +370,10 @@
__func__, item->getKey().c_str(), prefix);
continue;
}
- result.appendFormat("%5d: %s\n", slot, item->toString().c_str());
+ result << StringPrintf("%5d: %s\n", slot, item->toString().c_str());
slot++;
}
+ return result.str();
}
//
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index 8bc8019..6234656 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -100,8 +100,8 @@
bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
// support for generating output
- void dumpQueue(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
- void dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ std::string dumpQueue(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ std::string dumpHeaders(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
// support statsd pushed atoms
static bool isPullable(const std::string &key);
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index 926de3e..db61061 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -74,6 +74,9 @@
"ResourceManagerService.cpp",
"ResourceObserverService.cpp",
"ServiceLog.cpp",
+
+ // TODO: convert to AIDL?
+ "IMediaResourceMonitor.cpp",
],
shared_libs: [
diff --git a/services/mediaresourcemanager/IMediaResourceMonitor.cpp b/services/mediaresourcemanager/IMediaResourceMonitor.cpp
new file mode 100644
index 0000000..42d7feb
--- /dev/null
+++ b/services/mediaresourcemanager/IMediaResourceMonitor.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 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 "IMediaResourceMonitor.h"
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <sys/types.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
+public:
+ explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
+ : BpInterface<IMediaResourceMonitor>(impl) {}
+
+ virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaResourceMonitor::getInterfaceDescriptor());
+ data.writeInt32(pid);
+ data.writeInt32(type);
+ remote()->transact(NOTIFY_RESOURCE_GRANTED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
+
+// ----------------------------------------------------------------------
+
+// NOLINTNEXTLINE(google-default-arguments)
+status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ switch(code) {
+ case NOTIFY_RESOURCE_GRANTED: {
+ CHECK_INTERFACE(IMediaResourceMonitor, data, reply);
+ int32_t pid = data.readInt32();
+ const int32_t type = data.readInt32();
+ notifyResourceGranted(/*in*/ pid, /*in*/ type);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------
+
+} // namespace android
diff --git a/services/mediaresourcemanager/IMediaResourceMonitor.h b/services/mediaresourcemanager/IMediaResourceMonitor.h
new file mode 100644
index 0000000..f92d557
--- /dev/null
+++ b/services/mediaresourcemanager/IMediaResourceMonitor.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#pragma once
+
+#ifndef __ANDROID_VNDK__
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IMediaResourceMonitor : public IInterface {
+public:
+ DECLARE_META_INTERFACE(MediaResourceMonitor)
+
+ // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
+ enum {
+ TYPE_VIDEO_CODEC = 0,
+ TYPE_AUDIO_CODEC = 1,
+ };
+
+ virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
+
+ enum {
+ NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
+public:
+ // NOLINTNEXTLINE(google-default-arguments)
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+} // namespace android
+
+#else // __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif // __ANDROID_VNDK__
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 289cffd..953686b 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -21,7 +21,6 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
-#include <binder/IMediaResourceMonitor.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/sched_policy.h>
@@ -36,6 +35,7 @@
#include <sys/time.h>
#include <unistd.h>
+#include "IMediaResourceMonitor.h"
#include "ResourceManagerService.h"
#include "ResourceObserverService.h"
#include "ServiceLog.h"
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index 4df5a9f..cb180ec 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -25,6 +25,7 @@
],
shared_libs: [
+ "libactivitymanager_aidl",
"libbinder",
"libbinder_ndk",
"liblog",
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 3f7d8d6..20e4bfb 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -434,6 +434,34 @@
session.request.destinationFilePath == destinationFilePath));
}
+ template <bool expectation = success>
+ bool addClientUid(int32_t sessionId, uid_t clientUid) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ Status status = mClient->addClientUid(sessionId, clientUid, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+
+ return status.isOk() && (result == shouldSucceed);
+ }
+
+ template <bool expectation = success>
+ bool getClientUids(int32_t sessionId, std::vector<int32_t>* clientUids) {
+ constexpr bool shouldSucceed = (expectation == success);
+ std::optional<std::vector<int32_t>> aidl_return;
+ Status status = mClient->getClientUids(sessionId, &aidl_return);
+
+ EXPECT_TRUE(status.isOk());
+ bool success = (aidl_return != std::nullopt);
+ if (success) {
+ *clientUids = *aidl_return;
+ }
+ EXPECT_EQ(success, shouldSucceed);
+
+ return status.isOk() && (success == shouldSucceed);
+ }
+
int32_t mClientId;
pid_t mClientPid;
uid_t mClientUid;
@@ -500,8 +528,24 @@
EXPECT_TRUE(mClient3->unregisterClient().isOk());
}
+ const char* prepareOutputFile(const char* path) {
+ deleteFile(path);
+ return path;
+ }
+
void deleteFile(const char* path) { unlink(path); }
+ void dismissKeyguard() {
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ }
+
+ void stopAppPackages() {
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+ }
+
std::shared_ptr<IMediaTranscodingService> mService;
std::shared_ptr<TestClientCallback> mClient1;
std::shared_ptr<TestClientCallback> mClient2;
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
index 0550d77..e9eebe2 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -33,7 +33,7 @@
namespace media {
-constexpr int64_t kPaddingUs = 400000;
+constexpr int64_t kPaddingUs = 1000000;
constexpr int64_t kSessionWithPaddingUs = 10000000 + kPaddingUs;
constexpr int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
@@ -56,8 +56,7 @@
registerMultipleClients();
const char* srcPath = "bad_file_uri";
- const char* dstPath = OUTPATH(TestInvalidSource);
- deleteFile(dstPath);
+ const char* dstPath = prepareOutputFile(OUTPATH(TestInvalidSource));
// Submit one session.
EXPECT_TRUE(
@@ -73,8 +72,7 @@
TEST_F(MediaTranscodingServiceRealTest, TestPassthru) {
registerMultipleClients();
- const char* dstPath = OUTPATH(TestPassthru);
- deleteFile(dstPath);
+ const char* dstPath = prepareOutputFile(OUTPATH(TestPassthru));
// Submit one session.
EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath));
@@ -89,8 +87,7 @@
TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideo) {
registerMultipleClients();
- const char* dstPath = OUTPATH(TestTranscodeVideo);
- deleteFile(dstPath);
+ const char* dstPath = prepareOutputFile(OUTPATH(TestTranscodeVideo));
// Submit one session.
EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath, TranscodingSessionPriority::kNormal,
@@ -106,8 +103,7 @@
TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideoProgress) {
registerMultipleClients();
- const char* dstPath = OUTPATH(TestTranscodeVideoProgress);
- deleteFile(dstPath);
+ const char* dstPath = prepareOutputFile(OUTPATH(TestTranscodeVideoProgress));
// Submit one session.
EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath, TranscodingSessionPriority::kNormal,
@@ -134,11 +130,9 @@
const char* srcPath0 = kLongSrcPath;
const char* srcPath1 = kShortSrcPath;
- const char* dstPath0 = OUTPATH(TestCancelImmediately_Session0);
- const char* dstPath1 = OUTPATH(TestCancelImmediately_Session1);
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestCancelImmediately_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestCancelImmediately_Session1));
- deleteFile(dstPath0);
- deleteFile(dstPath1);
// Submit one session, should start immediately.
EXPECT_TRUE(
mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
@@ -166,11 +160,9 @@
const char* srcPath0 = kLongSrcPath;
const char* srcPath1 = kShortSrcPath;
- const char* dstPath0 = OUTPATH(TestCancelWhileRunning_Session0);
- const char* dstPath1 = OUTPATH(TestCancelWhileRunning_Session1);
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestCancelWhileRunning_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestCancelWhileRunning_Session1));
- deleteFile(dstPath0);
- deleteFile(dstPath1);
// Submit two sessions, session 0 should start immediately, session 1 should be queued.
EXPECT_TRUE(
mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
@@ -197,10 +189,8 @@
const char* srcPath0 = kLongSrcPath;
const char* srcPath1 = kShortSrcPath;
- const char* dstPath0 = OUTPATH(TestPauseResumeSingleClient_Session0);
- const char* dstPath1 = OUTPATH(TestPauseResumeSingleClient_Session1);
- deleteFile(dstPath0);
- deleteFile(dstPath1);
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestPauseResumeSingleClient_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestPauseResumeSingleClient_Session1));
// Submit one offline session, should start immediately.
EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kUnspecified,
@@ -244,20 +234,15 @@
TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeMultiClients) {
ALOGD("TestPauseResumeMultiClients starting...");
- EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
- EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+ dismissKeyguard();
+ stopAppPackages();
registerMultipleClients();
const char* srcPath0 = kLongSrcPath;
const char* srcPath1 = kShortSrcPath;
- const char* dstPath0 = OUTPATH(TestPauseResumeMultiClients_Client0);
- const char* dstPath1 = OUTPATH(TestPauseResumeMultiClients_Client1);
- deleteFile(dstPath0);
- deleteFile(dstPath1);
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestPauseResumeMultiClients_Client0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestPauseResumeMultiClients_Client1));
ALOGD("Moving app A to top...");
EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
@@ -294,12 +279,177 @@
unregisterMultipleClients();
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
- EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+ stopAppPackages();
ALOGD("TestPauseResumeMultiClients finished.");
}
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneForeground) {
+ ALOGD("TestUidGoneForeground starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill foreground app, using only 1 uid.
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit sessions to Client1 (app A).
+ ALOGD("Submitting sessions to client1 (app A) ...");
+ EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_TRUE(mClient1->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::NoEvent);
+
+ // Kill app A, expect to see A's session pause followed by B's session start,
+ // then A's session cancelled with error code kUidGoneCancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneForeground finished.");
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneForegroundMultiUids) {
+ ALOGD("TestUidGoneForegroundMultiUids starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill foreground app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+ // Make app A also requesting session 1.
+ EXPECT_TRUE(mClient2->addClientUid(1, mClient1->mClientUid));
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 1));
+
+ // Kill app A, CLIENT(2)'s session 1 should continue because it's also requested by app B.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+
+ // Kill app B, sessions should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 1));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneForegroundMultiUids finished.");
+}
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneBackground) {
+ ALOGD("TestUidGoneBackground starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill background app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Kill app B, all its sessions should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 1));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneBackground finished.");
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneBackgroundMultiUids) {
+ ALOGD("TestUidGoneBackgroundMultiUids starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill background app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+ // Make app A also requesting session 1.
+ EXPECT_TRUE(mClient2->addClientUid(1, mClient1->mClientUid));
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 1));
+
+ // Kill app B, CLIENT(2)'s session 1 should continue to run, session 0 on
+ // the other hand should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneBackgroundMultiUids finished.");
+}
+
} // namespace media
} // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index c8994ac..cb354f4 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -36,6 +36,7 @@
#include <iostream>
#include <list>
+#include <unordered_set>
#include "MediaTranscodingServiceTestHelper.h"
#include "SimulatedTranscoder.h"
@@ -255,6 +256,54 @@
unregisterMultipleClients();
}
+TEST_F(MediaTranscodingServiceSimulatedTest, TestAddGetClientUids) {
+ registerMultipleClients();
+
+ std::vector<int32_t> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file"));
+
+ // Should have mClientUid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(0, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)mClient1->mClientUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, kInvalidClientUid));
+
+ // Adding mClientUid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, mClient1->mClientUid));
+
+ // Submit one offline session.
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1",
+ TranscodingSessionPriority::kUnspecified));
+
+ // Should not have any uids in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 0u);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID), should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, IMediaTranscodingService::USE_CALLING_UID));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)ownUid);
+
+ // Adding mClientUid should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient1->mClientUid));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2u);
+ EXPECT_EQ(uidSet.count(ownUid), 1u);
+ EXPECT_EQ(uidSet.count(mClient1->mClientUid), 1u);
+
+ unregisterMultipleClients();
+}
+
TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineSessions) {
registerMultipleClients();
@@ -378,6 +427,53 @@
ALOGD("TestTranscodingUidPolicy finished.");
}
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingUidPolicyWithMultipleClientUids) {
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids starting...");
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ registerMultipleClients();
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit 3 requests.
+ ALOGD("Submitting session to client1 (app A)...");
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+
+ // mClient1's Session 0 should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Add client2 (app B)'s uid to mClient1's session 1.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient2->mClientUid));
+
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+ // mClient1's session 0 should pause, session 1 should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ ALOGD("Moving app A back to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids finished.");
+}
+
TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingThermalPolicy) {
ALOGD("TestTranscodingThermalPolicy starting...");
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 501e8c0..0d453cf 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -111,7 +111,7 @@
if (!endpoint->isConnected()) {
ALOGD("%s() call safeReleaseCloseFromCallback()", __func__);
// Release and close under a lock with no check for callback collisions.
- endpoint->getStreamInternal()->safeReleaseCloseFromCallback();
+ endpoint->getStreamInternal()->safeReleaseCloseInternal();
}
return result;
diff --git a/services/tuner/TunerDescrambler.cpp b/services/tuner/TunerDescrambler.cpp
index 16338db..bdf826c 100644
--- a/services/tuner/TunerDescrambler.cpp
+++ b/services/tuner/TunerDescrambler.cpp
@@ -67,8 +67,9 @@
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
}
- Result res = mDescrambler->addPid(getHidlDemuxPid(pid),
- static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter());
+ sp<IFilter> halFilter = (optionalSourceFilter == NULL)
+ ? NULL : static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter();
+ Result res = mDescrambler->addPid(getHidlDemuxPid(pid), halFilter);
if (res != Result::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
@@ -82,8 +83,9 @@
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
}
- Result res = mDescrambler->removePid(getHidlDemuxPid(pid),
- static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter());
+ sp<IFilter> halFilter = (optionalSourceFilter == NULL)
+ ? NULL : static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter();
+ Result res = mDescrambler->removePid(getHidlDemuxPid(pid), halFilter);
if (res != Result::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}