Merge "Fix null pointer dereference in Camera2Client::stopPreviewL" into main
diff --git a/Android.bp b/Android.bp
index 0c7ed6e..c716a06 100644
--- a/Android.bp
+++ b/Android.bp
@@ -135,6 +135,7 @@
aidl_interface {
name: "av-audio-types-aidl",
+ unstable: true,
host_supported: true,
vendor_available: true,
double_loadable: true,
@@ -154,28 +155,4 @@
sdk_version: "module_current",
},
},
- versions_with_info: [
- {
- version: "1",
- imports: ["android.hardware.audio.core-V2"],
- },
- ],
- frozen: false,
-
-}
-
-latest_av_audio_types_aidl = "av-audio-types-aidl-V2"
-
-cc_defaults {
- name: "latest_av_audio_types_aidl_ndk_shared",
- shared_libs: [
- latest_av_audio_types_aidl + "-ndk",
- ],
-}
-
-cc_defaults {
- name: "latest_av_audio_types_aidl_ndk_static",
- static_libs: [
- latest_av_audio_types_aidl + "-ndk",
- ],
}
diff --git a/aidl/com/android/media/permission/PermissionEnum.aidl b/aidl/com/android/media/permission/PermissionEnum.aidl
index b08db44..7badb87 100644
--- a/aidl/com/android/media/permission/PermissionEnum.aidl
+++ b/aidl/com/android/media/permission/PermissionEnum.aidl
@@ -37,5 +37,6 @@
CAPTURE_TUNER_AUDIO_INPUT = 11,
CAPTURE_VOICE_COMMUNICATION_OUTPUT = 12,
BLUETOOTH_CONNECT = 13,
- ENUM_SIZE = 14, // Not for actual usage, used by Java
+ BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION = 14,
+ ENUM_SIZE = 15, // Not for actual usage, used by Java
}
diff --git a/aidl_api/av-audio-types-aidl/1/.hash b/aidl_api/av-audio-types-aidl/1/.hash
deleted file mode 100644
index 0002682..0000000
--- a/aidl_api/av-audio-types-aidl/1/.hash
+++ /dev/null
@@ -1 +0,0 @@
-ef1bc5ed9db445fbfc116cdec6e6ad081458ee40
diff --git a/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl
deleted file mode 100644
index a9aa2c1..0000000
--- a/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-// the interface (from the latest frozen version), the build system will
-// prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media.audio;
-/* @hide */
-interface IHalAdapterVendorExtension {
- @utf8InCpp String[] parseVendorParameterIds(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeys);
- void parseVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeysAndValues, out android.hardware.audio.core.VendorParameter[] syncParameters, out android.hardware.audio.core.VendorParameter[] asyncParameters);
- android.hardware.audio.core.VendorParameter[] parseBluetoothA2dpReconfigureOffload(in @utf8InCpp String rawValue);
- android.hardware.audio.core.VendorParameter[] parseBluetoothLeReconfigureOffload(in @utf8InCpp String rawValue);
- @utf8InCpp String processVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in android.hardware.audio.core.VendorParameter[] parameters);
- enum ParameterScope {
- MODULE = 0,
- STREAM = 1,
- }
-}
diff --git a/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl
deleted file mode 100644
index a9aa2c1..0000000
--- a/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-// the interface (from the latest frozen version), the build system will
-// prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media.audio;
-/* @hide */
-interface IHalAdapterVendorExtension {
- @utf8InCpp String[] parseVendorParameterIds(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeys);
- void parseVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeysAndValues, out android.hardware.audio.core.VendorParameter[] syncParameters, out android.hardware.audio.core.VendorParameter[] asyncParameters);
- android.hardware.audio.core.VendorParameter[] parseBluetoothA2dpReconfigureOffload(in @utf8InCpp String rawValue);
- android.hardware.audio.core.VendorParameter[] parseBluetoothLeReconfigureOffload(in @utf8InCpp String rawValue);
- @utf8InCpp String processVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in android.hardware.audio.core.VendorParameter[] parameters);
- enum ParameterScope {
- MODULE = 0,
- STREAM = 1,
- }
-}
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 5dddd29..f9c1a8a 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -920,6 +920,7 @@
clientAttribution.deviceId = mDeviceContext.deviceId;
clientAttribution.packageName = "";
clientAttribution.attributionTag = std::nullopt;
+ clientAttribution.token = sp<BBinder>::make();
// No way to get package name from native.
// Send a zero length package name and let camera service figure it out from UID
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index c820a2c..96fb3e3 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -7,6 +7,7 @@
flag {
name: "aidl_hal_input_surface"
+ is_exported: true
namespace: "codec_fwk"
description: "Feature flags for enabling AIDL HAL InputSurface handling"
bug: "201479783"
diff --git a/media/codec2/components/apv/C2SoftApvDec.cpp b/media/codec2/components/apv/C2SoftApvDec.cpp
index 0064cec..77305ce 100644
--- a/media/codec2/components/apv/C2SoftApvDec.cpp
+++ b/media/codec2/components/apv/C2SoftApvDec.cpp
@@ -279,6 +279,10 @@
if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
}
+ if (isHalPixelFormatSupported((AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210)) {
+ pixelFormats.push_back(AHARDWAREBUFFER_FORMAT_YCbCr_P210);
+ }
+
// If color format surface isn't added to supported formats, there is no way to know
// when the color-format is configured to surface. This is necessary to be able to
// choose 10-bit format while decoding 10-bit clips in surface mode.
@@ -374,6 +378,8 @@
(void)mayBlock;
ALOGV("%s", __FUNCTION__);
// take default values for all unspecified fields, and coded values for specified ones
+ ALOGV("%s - coded range: %u, primaries: %u, transfer: %u, matrix: %u",
+ __func__, coded.v.range, coded.v.primaries, coded.v.transfer, coded.v.matrix);
me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
me.set().primaries =
coded.v.primaries == PRIMARIES_UNSPECIFIED ? def.v.primaries : coded.v.primaries;
@@ -717,6 +723,22 @@
}
}
+static void copyBufferP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY,
+ const uint16_t *srcUV, size_t srcYStride, size_t srcUVStride, size_t dstYStride,
+ size_t dstUVStride, size_t width, size_t height) {
+ for (size_t y = 0; y < height; ++y) {
+ memcpy(dstY, srcY, width * sizeof(uint16_t));
+ srcY += srcYStride;
+ dstY += dstYStride;
+ }
+
+ for (size_t y = 0; y < height; ++y) {
+ memcpy(dstUV, srcUV, width * sizeof(uint16_t));
+ srcUV += srcUVStride;
+ dstUV += dstUVStride;
+ }
+}
+
static void copyBufferFromYUV422ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV,
const uint8_t* srcY, const uint8_t* srcU,
const uint8_t* srcV, size_t srcYStride, size_t srcUStride,
@@ -992,28 +1014,81 @@
}
}
+void C2SoftApvDec::getVuiParams(VuiColorAspects* buffer) {
+ VuiColorAspects vuiColorAspects;
+ vuiColorAspects.primaries = buffer->primaries;
+ vuiColorAspects.transfer = buffer->transfer;
+ vuiColorAspects.coeffs = buffer->coeffs;
+ vuiColorAspects.fullRange = buffer->fullRange;
+
+ // convert vui aspects to C2 values if changed
+ if (!(vuiColorAspects == mBitstreamColorAspects)) {
+ mBitstreamColorAspects = vuiColorAspects;
+ ColorAspects sfAspects;
+ C2StreamColorAspectsInfo::input codedAspects = { 0u };
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+ vuiColorAspects.fullRange, sfAspects);
+ if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+ codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+ codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+ codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+ codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+ }
+ ALOGV("colorAspects: primaries:%d, transfer:%d, coeffs:%d, fullRange:%d",
+ codedAspects.primaries, codedAspects.transfer, codedAspects.matrix,
+ codedAspects.range);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+ }
+}
+
status_t C2SoftApvDec::outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
const std::unique_ptr<C2Work>& work) {
if (!(work && pool)) return BAD_VALUE;
- oapv_imgb_t* imgbOutput;
+ oapv_imgb_t* imgbOutput = nullptr;
std::shared_ptr<C2GraphicBlock> block;
if (ofrms.num_frms > 0) {
- oapv_frm_t* frm = &ofrms.frm[0];
- imgbOutput = frm->imgb;
+ for(int i = 0; i < ofrms.num_frms; i++) {
+ oapv_frm_t* frm = &ofrms.frm[0];
+ if(frm->pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) {
+ imgbOutput = frm->imgb;
+ break;
+ }
+ }
+ if(imgbOutput == nullptr) {
+ ALOGW("No OAPV primary frame");
+ return false;
+ }
} else {
ALOGW("No output frames");
return false;
}
bool isMonochrome = OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CS_YCBCR400;
+ // TODO: use bitstream color aspect after vui parsing
+ VuiColorAspects colorAspect;
+ colorAspect.primaries = 2;
+ colorAspect.transfer = 2;
+ colorAspect.coeffs = 2;
+ colorAspect.fullRange = 1;
+ getVuiParams(&colorAspect);
+
uint32_t format = HAL_PIXEL_FORMAT_YV12;
std::shared_ptr<C2StreamColorAspectsInfo::output> codedColorAspects;
if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10 &&
mPixelFormatInfo->value != HAL_PIXEL_FORMAT_YCBCR_420_888) {
IntfImpl::Lock lock = mIntf->lock();
codedColorAspects = mIntf->getColorAspects_l();
+
bool allowRGBA1010102 = false;
if (codedColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
codedColorAspects->matrix == C2Color::MATRIX_BT2020 &&
@@ -1070,7 +1145,34 @@
size_t dstUStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
size_t dstVStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
- if (format == HAL_PIXEL_FORMAT_YCBCR_P010) {
+ if(format == AHARDWAREBUFFER_FORMAT_YCbCr_P210) {
+ if(OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10) {
+ const uint16_t *srcY = (const uint16_t *)imgbOutput->a[0];
+ const uint16_t *srcU = (const uint16_t *)imgbOutput->a[1];
+ const uint16_t *srcV = (const uint16_t *)imgbOutput->a[2];
+ size_t srcYStride = imgbOutput->s[0] / 2;
+ size_t srcUStride = imgbOutput->s[1] / 2;
+ size_t srcVStride = imgbOutput->s[2] / 2;
+ dstYStride /= 2;
+ dstUStride /= 2;
+ dstVStride /= 2;
+ ALOGV("OAPV_CS_P210 buffer");
+ copyBufferP210((uint16_t *)dstY, (uint16_t *)dstU, srcY, srcU,
+ srcYStride, srcUStride, dstYStride, dstUStride, mWidth, mHeight);
+ } else {
+ ALOGE("Not supported convder from bd:%d, format: %d(%s), to format: %d(%s)",
+ OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs),
+ OAPV_CS_GET_FORMAT(imgbOutput->cs),
+ OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420 ?
+ "YUV420" : (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422 ?
+ "YUV422" : "UNKNOWN"),
+ format,
+ format == HAL_PIXEL_FORMAT_YCBCR_P010 ?
+ "P010" : (format == HAL_PIXEL_FORMAT_YCBCR_420_888 ?
+ "YUV420" : (format == HAL_PIXEL_FORMAT_YV12 ? "YV12" : "UNKNOWN"))
+ );
+ }
+ } else if(format == HAL_PIXEL_FORMAT_YCBCR_P010) {
if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10) {
const uint16_t* srcY = (const uint16_t*)imgbOutput->a[0];
const uint16_t* srcU = (const uint16_t*)imgbOutput->a[1];
diff --git a/media/codec2/components/apv/C2SoftApvDec.h b/media/codec2/components/apv/C2SoftApvDec.h
index f5beb8f..05afdb2 100644
--- a/media/codec2/components/apv/C2SoftApvDec.h
+++ b/media/codec2/components/apv/C2SoftApvDec.h
@@ -118,6 +118,26 @@
uint32_t mHeight;
bool mSignalledOutputEos;
bool mSignalledError;
+ // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+ // converting them to C2 values for each frame
+ struct VuiColorAspects {
+ uint8_t primaries;
+ uint8_t transfer;
+ uint8_t coeffs;
+ uint8_t fullRange;
+
+ // default color aspects
+ VuiColorAspects()
+ : primaries(C2Color::PRIMARIES_UNSPECIFIED),
+ transfer(C2Color::TRANSFER_UNSPECIFIED),
+ coeffs(C2Color::MATRIX_UNSPECIFIED),
+ fullRange(C2Color::RANGE_UNSPECIFIED) { }
+
+ bool operator==(const VuiColorAspects &o) {
+ return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs
+ && fullRange == o.fullRange;
+ }
+ } mBitstreamColorAspects;
oapvd_t oapvdHandle;
oapvm_t oapvmHandle;
@@ -126,6 +146,8 @@
int outputCsp;
+ void getVuiParams(VuiColorAspects* buffer);
+
C2_DO_NOT_COPY(C2SoftApvDec);
};
diff --git a/media/codec2/components/apv/C2SoftApvEnc.cpp b/media/codec2/components/apv/C2SoftApvEnc.cpp
index a61cfd6..d6a9597 100644
--- a/media/codec2/components/apv/C2SoftApvEnc.cpp
+++ b/media/codec2/components/apv/C2SoftApvEnc.cpp
@@ -221,11 +221,14 @@
.withSetter(CodedColorAspectsSetter, mColorAspects)
.build());
std::vector<uint32_t> pixelFormats = {
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
};
if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
}
+ if (isHalPixelFormatSupported((AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210)) {
+ pixelFormats.push_back(AHARDWAREBUFFER_FORMAT_YCbCr_P210);
+ }
addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
.withDefault(new C2StreamPixelFormatInfo::input(
0u, HAL_PIXEL_FORMAT_YCBCR_P010))
@@ -623,12 +626,6 @@
return C2_NO_MEMORY;
}
- /* Calculate SDR to HDR mapping values */
- mSdrToHdrMapping.clear();
- for (int32_t i = 0; i < 256; i++) {
- mSdrToHdrMapping.push_back((uint16_t)(i * 1023 / 255 + 0.5));
- }
-
mStarted = true;
mInitEncoder = true;
return C2_OK;
@@ -692,26 +689,48 @@
switch (layout.type) {
case C2PlanarLayout::TYPE_RGB:
- [[fallthrough]];
- case C2PlanarLayout::TYPE_RGBA: {
- // TODO: Add RGBA1010102 support
ALOGE("Not supported RGB color format");
return C2_BAD_VALUE;
+ case C2PlanarLayout::TYPE_RGBA: {
+ [[fallthrough]];
+ }
+ case C2PlanarLayout::TYPE_YUVA: {
+ ALOGV("Convert from ABGR2101010 to P210");
+ uint16_t *dstY, *dstU, *dstV;
+ dstY = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+ dstU = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+ dstV = (uint16_t*)inputFrames->frm[0].imgb->a[2];
+ convertRGBA1010102ToYUV420Planar16(dstY, dstU, dstV, (uint32_t*)(input->data()[0]),
+ layout.planes[layout.PLANE_Y].rowInc / 4, width,
+ height, mColorAspects->matrix,
+ mColorAspects->range);
+ break;
}
case C2PlanarLayout::TYPE_YUV: {
if (IsP010(*input)) {
if (mColorFormat == OAPV_CF_YCBCR422) {
ColorConvertP010ToYUV422P10le(input, inputFrames->frm[0].imgb);
} else if (mColorFormat == OAPV_CF_PLANAR2) {
- ColorConvertP010ToP210(input, inputFrames->frm[0].imgb);
+ uint16_t *srcY = (uint16_t*)(input->data()[0]);
+ uint16_t *srcUV = (uint16_t*)(input->data()[1]);
+ uint16_t *dstY = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+ uint16_t *dstUV = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+ convertP010ToP210(dstY, dstUV, srcY, srcUV,
+ input->width(), input->width(), input->width(),
+ input->height());
} else {
ALOGE("Not supported color format. %d", mColorFormat);
return C2_BAD_VALUE;
}
} else if (IsNV12(*input)) {
- ColorConvertNv12ToP210(input, inputFrames->frm[0].imgb);
- } else if (IsNV21(*input)) {
- ColorConvertNv12ToP210(input, inputFrames->frm[0].imgb);
+ uint8_t *srcY = (uint8_t*)input->data()[0];
+ uint8_t *srcUV = (uint8_t*)input->data()[1];
+ uint16_t *dstY = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+ uint16_t *dstUV = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+ convertSemiPlanar8ToP210(dstY, dstUV, srcY, srcUV,
+ input->width(), input->width(), input->width(),
+ input->width(), input->width(), input->height(),
+ CONV_FORMAT_I420);
} else if (IsYUV420(*input)) {
return C2_BAD_VALUE;
} else if (IsI420(*input)) {
@@ -731,46 +750,6 @@
return C2_OK;
}
-void C2SoftApvEnc::ColorConvertNv12ToP210(const C2GraphicView* const input, oapv_imgb_t* imgb) {
- auto width = input->width();
- auto height = input->height();
-
- auto* yPlane = (uint8_t*)input->data()[0];
- auto* uvPlane = (uint8_t*)input->data()[1];
-
- auto* dst = (uint16_t*)imgb->a[0];
- int32_t lumaOffset = 0;
- for (int32_t y = 0; y < height; ++y) {
- for (int32_t x = 0; x < width; ++x) {
- lumaOffset = y * width + x;
- dst[lumaOffset] = (mSdrToHdrMapping[yPlane[lumaOffset]] << 6) |
- ((mSdrToHdrMapping[yPlane[lumaOffset]] & 0x300) >> 3);
- }
- }
-
- auto* dst_uv = (uint16_t*)imgb->a[1];
- uint32_t uvDstStride = width;
- int32_t srcOffset = 0;
- int32_t dstOffset1 = 0, dstOffset2 = 0;
- int32_t tmp1 = 0, tmp2 = 0;
- for (int32_t y = 0; y < height / 2; ++y) {
- for (int32_t x = 0; x < width; x += 2) {
- srcOffset = y * width + x;
- dstOffset1 = (y * 2) * width + x;
- dstOffset2 = ((y * 2) + 1) * width + x;
-
- tmp1 = (mSdrToHdrMapping[uvPlane[srcOffset]] << 6) |
- ((mSdrToHdrMapping[uvPlane[srcOffset]] & 0x300) >> 3);
- tmp2 = (mSdrToHdrMapping[uvPlane[srcOffset + 1]] << 6) |
- ((mSdrToHdrMapping[uvPlane[srcOffset + 1]] & 0x300) >> 3);
- dst_uv[dstOffset1] = (uint16_t)tmp1;
- dst_uv[dstOffset1 + 1] = (uint16_t)tmp2;
- dst_uv[dstOffset2] = (uint16_t)tmp1;
- dst_uv[dstOffset2 + 1] = (uint16_t)tmp2;
- }
- }
-}
-
C2Config::level_t C2SoftApvEnc::decisionApvLevel(int32_t width, int32_t height, int32_t fps,
int32_t bitrate, int32_t band) {
C2Config::level_t level = C2Config::LEVEL_APV_1_BAND_0;
@@ -885,30 +864,6 @@
return level;
}
-void C2SoftApvEnc::ColorConvertP010ToP210(const C2GraphicView* const input, oapv_imgb_t* imgb) {
- auto width = input->width();
- auto height = input->height();
-
- auto* yPlane = (uint8_t*)input->data()[0];
- auto* uvPlane = (uint8_t*)input->data()[1];
- uint32_t uvStride = width * 2;
-
- auto* src = yPlane;
- auto* dst = (uint8_t*)imgb->a[0];
- std::memcpy(dst, src, width * height * 2);
-
- auto* dst_uv = (uint8_t*)imgb->a[1];
- int32_t offset1 = 0, offset2 = 0;
- for (int32_t y = 0; y < height / 2; ++y) {
- offset1 = (y * 2) * uvStride;
- offset2 = (y * 2 + 1) * uvStride;
- src = uvPlane + (y * uvStride);
-
- std::memcpy(dst_uv + offset1, src, uvStride);
- std::memcpy(dst_uv + offset2, src, uvStride);
- }
-}
-
void C2SoftApvEnc::ColorConvertP010ToYUV422P10le(const C2GraphicView* const input,
oapv_imgb_t* imgb) {
uint32_t width = input->width();
diff --git a/media/codec2/components/apv/C2SoftApvEnc.h b/media/codec2/components/apv/C2SoftApvEnc.h
index 441c663..fc4ad7d 100644
--- a/media/codec2/components/apv/C2SoftApvEnc.h
+++ b/media/codec2/components/apv/C2SoftApvEnc.h
@@ -65,9 +65,7 @@
void showEncoderParams(oapve_cdesc_t* cdsc);
- void ColorConvertNv12ToP210(const C2GraphicView* const input, oapv_imgb_t* imgb);
void ColorConvertP010ToYUV422P10le(const C2GraphicView* const input, oapv_imgb_t* imgb);
- void ColorConvertP010ToP210(const C2GraphicView* const input, oapv_imgb_t* imgb);
void createCsdData(const std::unique_ptr<C2Work>& work, oapv_bitb_t* bitb,
uint32_t encodedSize);
@@ -106,7 +104,6 @@
oapve_t mEncoderId;
oapvm_t mMetaId;
uint8_t* mBitstreamBuf = nullptr;
- std::vector<uint16_t> mSdrToHdrMapping;
bool mReceivedFirstFrame = false;
C2_DO_NOT_COPY(C2SoftApvEnc);
};
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index aec6523..a03f24f 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -463,6 +463,19 @@
}
}
+void convertP010ToP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY, const uint16_t *srcUV,
+ size_t srcUVStride, size_t dstUVStride, size_t width, size_t height) {
+ std::memcpy(dstY, srcY, width * height * sizeof(uint16_t));
+
+ int32_t offsetTop, offsetBot;
+ for (size_t y = 0; y < (height + 1) / 2; ++y) {
+ offsetTop = (y * 2) * dstUVStride;
+ offsetBot = (y * 2 + 1) * dstUVStride;
+ std::memcpy(dstUV + offsetTop, srcUV + (y * srcUVStride), srcUVStride * sizeof(uint16_t));
+ std::memcpy(dstUV + offsetBot, srcUV + (y * srcUVStride), srcUVStride * sizeof(uint16_t));
+ }
+}
+
static const int16_t bt709Matrix_10bit[2][3][3] = {
{ { 218, 732, 74 }, { -117, -395, 512 }, { 512, -465, -47 } }, /* RANGE_FULL */
{ { 186, 627, 63 }, { -103, -345, 448 }, { 448, -407, -41 } }, /* RANGE_LIMITED */
@@ -517,6 +530,45 @@
}
}
+void convertRGBA1010102ToP210(uint16_t* dstY, uint16_t* dstUV, const uint32_t* srcRGBA,
+ size_t srcRGBStride, size_t width, size_t height,
+ C2Color::matrix_t colorMatrix, C2Color::range_t colorRange) {
+ uint16_t r, g, b;
+ int32_t i32Y, i32U, i32V;
+ uint16_t zeroLvl = colorRange == C2Color::RANGE_FULL ? 0 : 64;
+ uint16_t maxLvlLuma = colorRange == C2Color::RANGE_FULL ? 1023 : 940;
+ uint16_t maxLvlChroma = colorRange == C2Color::RANGE_FULL ? 1023 : 960;
+ // set default range as limited
+ if (colorRange != C2Color::RANGE_FULL) {
+ colorRange = C2Color::RANGE_LIMITED;
+ }
+ const int16_t(*weights)[3] = (colorMatrix == C2Color::MATRIX_BT709)
+ ? bt709Matrix_10bit[colorRange - 1]
+ : bt2020Matrix_10bit[colorRange - 1];
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; ++x) {
+ b = (srcRGBA[x] >> 20) & 0x3FF;
+ g = (srcRGBA[x] >> 10) & 0x3FF;
+ r = srcRGBA[x] & 0x3FF;
+
+ i32Y = ((r * weights[0][0] + g * weights[0][1] + b * weights[0][2] + 512) >> 10) +
+ zeroLvl;
+ dstY[x] = (CLIP3(zeroLvl, i32Y, maxLvlLuma) << 6) & 0xFFC0;
+ if (x % 2 == 0) {
+ i32U = ((r * weights[1][0] + g * weights[1][1] + b * weights[1][2] + 512) >> 10) +
+ 512;
+ i32V = ((r * weights[2][0] + g * weights[2][1] + b * weights[2][2] + 512) >> 10) +
+ 512;
+ dstUV[x] = (CLIP3(zeroLvl, i32U, maxLvlChroma) << 6) & 0xFFC0;
+ dstUV[x + 1] = (CLIP3(zeroLvl, i32V, maxLvlChroma) << 6) & 0xFFC0;
+ }
+ }
+ srcRGBA += srcRGBStride;
+ dstY += width;
+ }
+}
+
void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
size_t srcVStride, size_t dstStride, size_t width,
@@ -631,6 +683,36 @@
isMonochrome);
}
}
+
+void convertSemiPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+ const uint8_t *srcY, const uint8_t *srcUV,
+ size_t srcYStride, size_t srcUVStride,
+ size_t dstYStride, size_t dstUVStride,
+ uint32_t width, uint32_t height,
+ CONV_FORMAT_T format) {
+ if (format != CONV_FORMAT_I420) {
+ ALOGE("No support for semi-planar8 to P210. format is %d", format);
+ return;
+ }
+
+ for (int32_t y = 0; y < height; ++y) {
+ for (int32_t x = 0; x < width; ++x) {
+ dstY[x] = ((uint16_t)((double)srcY[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+ }
+ dstY += dstYStride;
+ srcY += srcYStride;
+ }
+
+ for (int32_t y = 0; y < height / 2; ++y) {
+ for (int32_t x = 0; x < width; ++x) {
+ dstUV[x] = dstUV[dstUVStride + x] =
+ ((uint16_t)((double)srcUV[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+ }
+ srcUV += srcUVStride;
+ dstUV += dstUVStride << 1;
+ }
+}
+
std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
mQueue.pop_front();
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
index b28c47e..4306e55 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -68,10 +68,19 @@
size_t dstUStride, size_t dstVStride, size_t width,
size_t height, bool isMonochrome = false);
+void convertP010ToP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY,
+ const uint16_t *srcUV, size_t srcUVStride, size_t dstUVStride,
+ size_t width, size_t height);
+
void convertRGBA1010102ToYUV420Planar16(uint16_t* dstY, uint16_t* dstU, uint16_t* dstV,
const uint32_t* srcRGBA, size_t srcRGBStride, size_t width,
size_t height, C2Color::matrix_t colorMatrix,
C2Color::range_t colorRange);
+
+void convertRGBA1010102ToP210(uint16_t* dstY, uint16_t* dstUV, const uint32_t* srcRGBA,
+ size_t srcRGBStride, size_t width, size_t height,
+ C2Color::matrix_t colorMatrix, C2Color::range_t colorRange);
+
void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
size_t srcVStride, size_t dstStride, size_t width,
@@ -96,6 +105,12 @@
size_t srcUStride, size_t srcVStride, size_t dstYStride,
size_t dstUStride, size_t dstVStride, uint32_t width, uint32_t height,
bool isMonochrome, CONV_FORMAT_T format);
+void convertSemiPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+ const uint8_t *srcY, const uint8_t *srcUV,
+ size_t srcYStride, size_t srcUVStride,
+ size_t dstYStride, size_t dstUVStride,
+ uint32_t width, uint32_t height,
+ CONV_FORMAT_T format);
class SimpleC2Component
: public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 80735cb..6348e42 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -21,7 +21,9 @@
#include <utils/Trace.h>
#include <codec2/aidl/GraphicBufferAllocator.h>
+#include <codec2/common/HalSelection.h>
#include <codec2/hidl/client.h>
+
#include <C2Debug.h>
#include <C2BufferPriv.h>
#include <C2Config.h> // for C2StreamUsageTuning
@@ -1832,9 +1834,7 @@
std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
char const* serviceName) {
- int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
- "debug.stagefright.c2inputsurface", int32_t(0));
- if (inputSurfaceSetting <= 0) {
+ if (!IsCodec2AidlInputSurfaceSelected()) {
return nullptr;
}
size_t index = GetServiceNames().size();
diff --git a/media/codec2/hal/common/Android.bp b/media/codec2/hal/common/Android.bp
index 0638363..886391b 100644
--- a/media/codec2/hal/common/Android.bp
+++ b/media/codec2/hal/common/Android.bp
@@ -56,13 +56,17 @@
"libaconfig_storage_read_api_cc",
],
- static_libs: ["aconfig_mediacodec_flags_c_lib"],
+ static_libs: [
+ "aconfig_mediacodec_flags_c_lib",
+ "android.media.codec-aconfig-cc",
+ ],
}
cc_defaults {
name: "libcodec2_hal_selection",
static_libs: [
"aconfig_mediacodec_flags_c_lib",
+ "android.media.codec-aconfig-cc",
"libcodec2_hal_selection_static",
],
shared_libs: [
diff --git a/media/codec2/hal/common/HalSelection.cpp b/media/codec2/hal/common/HalSelection.cpp
index d3ea181..5bf4fbe 100644
--- a/media/codec2/hal/common/HalSelection.cpp
+++ b/media/codec2/hal/common/HalSelection.cpp
@@ -21,6 +21,7 @@
// NOTE: due to dependency from mainline modules cannot use libsysprop
// #include <android/sysprop/MediaProperties.sysprop.h>
#include <android-base/properties.h>
+#include <android_media_codec.h>
#include <com_android_media_codec_flags.h>
#include <codec2/common/HalSelection.h>
@@ -66,4 +67,20 @@
return false;
}
+bool IsCodec2AidlInputSurfaceSelected() {
+ if (!IsCodec2AidlHalSelected()) {
+ return false;
+ }
+
+ int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
+ "debug.stagefright.c2inputsurface", int32_t(0));
+ if (inputSurfaceSetting <= 0) {
+ return false;
+ }
+ if (!android::media::codec::provider_->aidl_hal_input_surface()) {
+ return false;
+ }
+ return true;
+}
+
} // namespace android
diff --git a/media/codec2/hal/common/include/codec2/common/HalSelection.h b/media/codec2/hal/common/include/codec2/common/HalSelection.h
index 7c77515..bf0c7c5 100644
--- a/media/codec2/hal/common/include/codec2/common/HalSelection.h
+++ b/media/codec2/hal/common/include/codec2/common/HalSelection.h
@@ -22,6 +22,9 @@
// Returns true iff AIDL c2 HAL is selected for the system
bool IsCodec2AidlHalSelected();
+// Returns true iff AIDL c2 InputSurface interface is selected for the system
+bool IsCodec2AidlInputSurfaceSelected();
+
} // namespace android
#endif // CODEC2_HAL_SELECTION_H
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index a943626..897a696 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -1907,7 +1907,7 @@
bottom = c2_min(bottom, height);
if (right > left && bottom > top) {
C2Rect rect(right - left, bottom - top);
- rect.at(left, top);
+ rect = rect.at(left, top);
c2QpOffsetRects.push_back(C2QpOffsetRectStruct(rect, offset));
} else {
ALOGE("Rects configuration %s is not valid.", box);
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 692f700..fd242a1 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -740,6 +740,7 @@
pixelFormatMap[HAL_PIXEL_FORMAT_YCBCR_P010] = COLOR_FormatYUVP010;
pixelFormatMap[HAL_PIXEL_FORMAT_RGBA_1010102] = COLOR_Format32bitABGR2101010;
pixelFormatMap[HAL_PIXEL_FORMAT_RGBA_FP16] = COLOR_Format64bitABGRFloat;
+ pixelFormatMap[AHARDWAREBUFFER_FORMAT_YCbCr_P210] = COLOR_FormatYUVP210;
std::shared_ptr<C2StoreFlexiblePixelFormatDescriptorsInfo> pixelFormatInfo;
std::vector<std::unique_ptr<C2Param>> heapParams;
diff --git a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
index 7a33af4..aa87e97 100644
--- a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
@@ -64,6 +64,13 @@
return kVendorApiLevel >= __ANDROID_API_T__;
}
+static bool isP210Allowed() {
+ static const int32_t kVendorApiLevel =
+ base::GetIntProperty<int32_t>("ro.vendor.api_level", 0);
+
+ return kVendorApiLevel > __ANDROID_API_V__;
+}
+
bool isHalPixelFormatSupported(AHardwareBuffer_Format format) {
// HAL_PIXEL_FORMAT_YCBCR_P010 requirement was added in T VSR, although it could have been
// supported prior to this.
@@ -76,6 +83,12 @@
return false;
}
+ // P210 is not available before Android B
+ if (format == (AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210 &&
+ !isP210Allowed()) {
+ return false;
+ }
+
// Default scenario --- the consumer is display or GPU
const AHardwareBuffer_Desc consumableForDisplayOrGpu = {
.width = 320,
diff --git a/media/codec2/tests/aidl/GraphicsTracker_test.cpp b/media/codec2/tests/aidl/GraphicsTracker_test.cpp
index 9008086..da79277 100644
--- a/media/codec2/tests/aidl/GraphicsTracker_test.cpp
+++ b/media/codec2/tests/aidl/GraphicsTracker_test.cpp
@@ -92,8 +92,7 @@
sp<IGraphicBufferConsumer> consumer = mConsumer.promote();
if (consumer != nullptr && consumer->acquireBuffer(&buffer, 0) == android::NO_ERROR) {
::usleep(kRenderDelayUs);
- consumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, buffer.mFence);
+ consumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, buffer.mFence);
}
}
void onBuffersReleased() override {}
@@ -438,8 +437,7 @@
// Consume one buffer and release
BufferItem item;
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, item.mFence));
// Nothing to consume
ASSERT_NE(OK, mConsumer->acquireBuffer(&item, 0));
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index b8dadb4..cfa3011 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1379,13 +1379,14 @@
media::GetInputForAttrResponse response;
- status_t status = statusTFromBinderStatus(
- aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl, attributionSource,
- configAidl, flagsAidl, selectedDeviceIdAidl, &response));
- if (status != NO_ERROR) {
+ const Status res = aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl,
+ attributionSource, configAidl, flagsAidl,
+ selectedDeviceIdAidl, &response);
+ if (!res.isOk()) {
+ ALOGE("getInputForAttr error: %s", res.toString8().c_str());
*config = VALUE_OR_RETURN_STATUS(
aidl2legacy_AudioConfigBase_audio_config_base_t(response.config, true /*isInput*/));
- return status;
+ return statusTFromBinderStatus(res);
}
*input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.input));
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index ddef852..00f3929 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -201,11 +201,11 @@
"latest_android_hardware_audio_core_sounddose_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
"latest_android_media_audio_common_types_ndk_shared",
- "latest_av_audio_types_aidl_ndk_shared",
],
shared_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
+ "av-audio-types-aidl-ndk",
"com.android.media.audio-aconfig-cc",
"libaudio_aidl_conversion_common_cpp",
"libaudio_aidl_conversion_common_ndk",
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index c7c6536..ac3975e 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -23,6 +23,7 @@
#include <fmq/AidlMessageQueue.h>
#include <system/audio_aidl_utils.h>
+#include <system/audio_effects/aidl_effects_utils.h>
#include <utils/Log.h>
#include "EffectProxy.h"
@@ -41,7 +42,8 @@
EffectProxy::EffectProxy(const AudioUuid& uuid, const std::vector<Descriptor>& descriptors,
const std::shared_ptr<IFactory>& factory)
- : mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
+ : mSharedCapability(buildDescriptorCapability(descriptors)),
+ mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
mSubEffects(
[](const std::vector<Descriptor>& descs, const std::shared_ptr<IFactory>& factory) {
std::vector<SubEffect> subEffects;
@@ -163,6 +165,7 @@
ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
*desc = mSubEffects[mActiveSubIdx].descriptor;
+ desc->capability = mSharedCapability;
desc->common = mDescriptorCommon;
return ndk::ScopedAStatus::ok();
}
@@ -182,6 +185,7 @@
}
desc->common = buildDescriptorCommon(uuid, subEffectDescs);
+ desc->capability = buildDescriptorCapability(subEffectDescs);
return ndk::ScopedAStatus::ok();
}
@@ -216,6 +220,20 @@
return swCommon;
}
+// Build a shared Descriptor capability with all sub-effects.
+Capability EffectProxy::buildDescriptorCapability(const std::vector<Descriptor>& subEffectDescs) {
+ std::optional<Capability> cap = subEffectDescs[0].capability;
+ for (size_t i = 1; i < subEffectDescs.size(); i++) {
+ cap = findSharedCapability(cap.value(), subEffectDescs[i].capability);
+ if (!cap) {
+ ALOGE("%s failed to find the shared capability at %zu", __func__, i);
+ return subEffectDescs[0].capability;
+ }
+ }
+
+ return cap.value();
+}
+
// Handle with active sub-effect first, only send to other sub-effects when success
ndk::ScopedAStatus EffectProxy::command(CommandId id) {
return runWithActiveSubEffectThenOthers(
@@ -323,6 +341,8 @@
prefixSpace += " ";
base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
mDescriptorCommon.toString().c_str());
+ base::StringAppendF(&ss, "%sDescriptorCapability: %s\n", prefixSpace.c_str(),
+ mSharedCapability.toString().c_str());
base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
for (size_t i = 0; i < mSubEffects.size(); i++) {
diff --git a/media/libaudiohal/impl/EffectProxy.h b/media/libaudiohal/impl/EffectProxy.h
index 9b9e8f1..6736104 100644
--- a/media/libaudiohal/impl/EffectProxy.h
+++ b/media/libaudiohal/impl/EffectProxy.h
@@ -108,6 +108,8 @@
std::string toString(size_t indent = 0) const;
private:
+ // The shared capability of all sub-effects
+ const ::aidl::android::hardware::audio::effect::Capability mSharedCapability;
// Proxy descriptor common part, copy from one sub-effect, and update the implementation UUID to
// proxy UUID, proxy descriptor capability part comes from the active sub-effect capability
const ::aidl::android::hardware::audio::effect::Descriptor::Common mDescriptorCommon;
@@ -146,6 +148,11 @@
const std::vector<::aidl::android::hardware::audio::effect::Descriptor>&
subEffectDescs);
+ // build a shared capability with all sub-effect descriptors
+ static ::aidl::android::hardware::audio::effect::Capability buildDescriptorCapability(
+ const std::vector<::aidl::android::hardware::audio::effect::Descriptor>&
+ subEffectDescs);
+
// close and release all sub-effects
~EffectProxy();
};
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionDynamicsProcessing.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionDynamicsProcessing.cpp
index f77c093..711050d 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionDynamicsProcessing.cpp
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionDynamicsProcessing.cpp
@@ -25,6 +25,7 @@
#include <media/AidlConversionNdk.h>
#include <media/AidlConversionEffect.h>
#include <system/audio_effect.h>
+#include <system/audio_effects/aidl_effects_utils.h>
#include <system/audio_effects/effect_dynamicsprocessing.h>
#include <Utils.h>
#include <utils/Log.h>
@@ -38,8 +39,10 @@
using ::aidl::android::getParameterSpecificField;
using ::aidl::android::aidl_utils::statusTFromBinderStatus;
using ::aidl::android::hardware::audio::effect::Capability;
+using ::aidl::android::hardware::audio::effect::clampParameter;
using ::aidl::android::hardware::audio::effect::DynamicsProcessing;
using ::aidl::android::hardware::audio::effect::Parameter;
+using ::aidl::android::hardware::audio::effect::Range;
using ::aidl::android::hardware::audio::effect::toString;
using ::aidl::android::hardware::audio::effect::VendorExtension;
using ::android::status_t;
@@ -126,7 +129,14 @@
}
}
- return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+ std::optional<Parameter> clamped =
+ clampParameter<Range::dynamicsProcessing, Parameter::Specific::dynamicsProcessing>(
+ aidlParam, getDescriptor().capability);
+ if (!clamped) {
+ ALOGE("%s failed to clamp parameters: %s", __func__, aidlParam.toString().c_str());
+ return BAD_VALUE;
+ }
+ return statusTFromBinderStatus(mEffect->setParameter(clamped.value()));
}
status_t AidlConversionDp::getParameter(EffectParamWriter& param) {
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 06dd27b..83a2b68 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -260,6 +260,7 @@
srcs: [
"AudioCapabilities.cpp",
"CodecCapabilities.cpp",
+ "VideoCapabilities.cpp",
"CodecCapabilitiesUtils.cpp",
],
diff --git a/media/libmedia/AudioCapabilities.cpp b/media/libmedia/AudioCapabilities.cpp
index 1a92307..dbbe9e8 100644
--- a/media/libmedia/AudioCapabilities.cpp
+++ b/media/libmedia/AudioCapabilities.cpp
@@ -30,19 +30,19 @@
return mBitrateRange;
}
-const std::vector<int>& AudioCapabilities::getSupportedSampleRates() const {
+const std::vector<int32_t>& AudioCapabilities::getSupportedSampleRates() const {
return mSampleRates;
}
-const std::vector<Range<int>>&
+const std::vector<Range<int32_t>>&
AudioCapabilities::getSupportedSampleRateRanges() const {
return mSampleRateRanges;
}
-int AudioCapabilities::getMaxInputChannelCount() const {
- int overallMax = 0;
+int32_t AudioCapabilities::getMaxInputChannelCount() const {
+ int32_t overallMax = 0;
for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
- int lmax = mInputChannelRanges[i].upper();
+ int32_t lmax = mInputChannelRanges[i].upper();
if (lmax > overallMax) {
overallMax = lmax;
}
@@ -50,10 +50,10 @@
return overallMax;
}
-int AudioCapabilities::getMinInputChannelCount() const {
- int overallMin = MAX_INPUT_CHANNEL_COUNT;
+int32_t AudioCapabilities::getMinInputChannelCount() const {
+ int32_t overallMin = MAX_INPUT_CHANNEL_COUNT;
for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
- int lmin = mInputChannelRanges[i].lower();
+ int32_t lmin = mInputChannelRanges[i].lower();
if (lmin < overallMin) {
overallMin = lmin;
}
@@ -61,7 +61,7 @@
return overallMin;
}
-const std::vector<Range<int>>&
+const std::vector<Range<int32_t>>&
AudioCapabilities::getInputChannelCountRanges() const {
return mInputChannelRanges;
}
@@ -86,40 +86,41 @@
}
void AudioCapabilities::initWithPlatformLimits() {
- mBitrateRange = Range<int>(0, INT32_MAX);
- mInputChannelRanges.push_back(Range<int>(1, MAX_INPUT_CHANNEL_COUNT));
+ mBitrateRange = Range<int32_t>(0, INT32_MAX);
+ mInputChannelRanges.push_back(Range<int32_t>(1, MAX_INPUT_CHANNEL_COUNT));
- const int minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
- const int maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
- mSampleRateRanges.push_back(Range<int>(minSampleRate, maxSampleRate));
+ const int32_t minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
+ const int32_t maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
+ mSampleRateRanges.push_back(Range<int32_t>(minSampleRate, maxSampleRate));
}
-bool AudioCapabilities::supports(std::optional<int> sampleRate,
- std::optional<int> inputChannels) {
+bool AudioCapabilities::supports(std::optional<int32_t> sampleRate,
+ std::optional<int32_t> inputChannels) {
// channels and sample rates are checked orthogonally
if (inputChannels
&& !std::any_of(mInputChannelRanges.begin(), mInputChannelRanges.end(),
- [inputChannels](const Range<int> &a) { return a.contains(inputChannels.value()); })) {
+ [inputChannels](const Range<int32_t> &a) {
+ return a.contains(inputChannels.value()); })) {
return false;
}
if (sampleRate
&& !std::any_of(mSampleRateRanges.begin(), mSampleRateRanges.end(),
- [sampleRate](const Range<int> &a) { return a.contains(sampleRate.value()); })) {
+ [sampleRate](const Range<int32_t> &a) { return a.contains(sampleRate.value()); })) {
return false;
}
return true;
}
-bool AudioCapabilities::isSampleRateSupported(int sampleRate) {
- return supports(std::make_optional<int>(sampleRate), std::nullopt);
+bool AudioCapabilities::isSampleRateSupported(int32_t sampleRate) {
+ return supports(std::make_optional<int32_t>(sampleRate), std::nullopt);
}
-void AudioCapabilities::limitSampleRates(std::vector<int> rates) {
- std::vector<Range<int>> sampleRateRanges;
+void AudioCapabilities::limitSampleRates(std::vector<int32_t> rates) {
+ std::vector<Range<int32_t>> sampleRateRanges;
std::sort(rates.begin(), rates.end());
- for (int rate : rates) {
- if (supports(std::make_optional<int>(rate), std::nullopt /* channels */)) {
- sampleRateRanges.push_back(Range<int>(rate, rate));
+ for (int32_t rate : rates) {
+ if (supports(std::make_optional<int32_t>(rate), std::nullopt /* channels */)) {
+ sampleRateRanges.push_back(Range<int32_t>(rate, rate));
}
}
mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, sampleRateRanges);
@@ -133,11 +134,11 @@
}
}
-void AudioCapabilities::limitSampleRates(std::vector<Range<int>> rateRanges) {
+void AudioCapabilities::limitSampleRates(std::vector<Range<int32_t>> rateRanges) {
sortDistinctRanges(&rateRanges);
mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
// check if all values are discrete
- for (Range<int> range: mSampleRateRanges) {
+ for (Range<int32_t> range: mSampleRateRanges) {
if (range.lower() != range.upper()) {
mSampleRates.clear();
return;
@@ -147,10 +148,10 @@
}
void AudioCapabilities::applyLevelLimits() {
- std::vector<int> sampleRates;
- std::optional<Range<int>> sampleRateRange;
- std::optional<Range<int>> bitRates;
- int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+ std::vector<int32_t> sampleRates;
+ std::optional<Range<int32_t>> sampleRateRange;
+ std::optional<Range<int32_t>> bitRates;
+ int32_t maxChannels = MAX_INPUT_CHANNEL_COUNT;
// const char *mediaType = mMediaType.c_str();
if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MPEG)) {
@@ -158,15 +159,15 @@
8000, 11025, 12000,
16000, 22050, 24000,
32000, 44100, 48000 };
- bitRates = Range<int>(8000, 320000);
+ bitRates = Range<int32_t>(8000, 320000);
maxChannels = 2;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)) {
sampleRates = { 8000 };
- bitRates = Range<int>(4750, 12200);
+ bitRates = Range<int32_t>(4750, 12200);
maxChannels = 1;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)) {
sampleRates = { 16000 };
- bitRates = Range<int>(6600, 23850);
+ bitRates = Range<int32_t>(6600, 23850);
maxChannels = 1;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
sampleRates = {
@@ -175,32 +176,32 @@
22050, 24000, 32000,
44100, 48000, 64000,
88200, 96000 };
- bitRates = Range<int>(8000, 510000);
+ bitRates = Range<int32_t>(8000, 510000);
maxChannels = 48;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_VORBIS)) {
- bitRates = Range<int>(32000, 500000);
- sampleRateRange = Range<int>(8000, 192000);
+ bitRates = Range<int32_t>(32000, 500000);
+ sampleRateRange = Range<int32_t>(8000, 192000);
maxChannels = 255;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_OPUS)) {
- bitRates = Range<int>(6000, 510000);
+ bitRates = Range<int32_t>(6000, 510000);
sampleRates = { 8000, 12000, 16000, 24000, 48000 };
maxChannels = 255;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_RAW)) {
- sampleRateRange = Range<int>(1, 192000);
- bitRates = Range<int>(1, 10000000);
+ sampleRateRange = Range<int32_t>(1, 192000);
+ bitRates = Range<int32_t>(1, 10000000);
maxChannels = MAX_NUM_CHANNELS;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
- sampleRateRange = Range<int>(1, 655350);
+ sampleRateRange = Range<int32_t>(1, 655350);
// lossless codec, so bitrate is ignored
maxChannels = 255;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
|| base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)) {
sampleRates = { 8000 };
- bitRates = Range<int>(64000, 64000);
+ bitRates = Range<int32_t>(64000, 64000);
// platform allows multiple channels for this format
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
sampleRates = { 8000 };
- bitRates = Range<int>(13000, 13000);
+ bitRates = Range<int32_t>(13000, 13000);
maxChannels = 1;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC3)) {
maxChannels = 6;
@@ -208,34 +209,34 @@
maxChannels = 16;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3_JOC)) {
sampleRates = { 48000 };
- bitRates = Range<int>(32000, 6144000);
+ bitRates = Range<int32_t>(32000, 6144000);
maxChannels = 16;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC4)) {
sampleRates = { 44100, 48000, 96000, 192000 };
- bitRates = Range<int>(16000, 2688000);
+ bitRates = Range<int32_t>(16000, 2688000);
maxChannels = 24;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS)) {
sampleRates = { 44100, 48000 };
- bitRates = Range<int>(96000, 1524000);
+ bitRates = Range<int32_t>(96000, 1524000);
maxChannels = 6;
} else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_HD)) {
for (ProfileLevel profileLevel: mProfileLevels) {
switch (profileLevel.mProfile) {
case DTS_HDProfileLBR:
sampleRates = { 22050, 24000, 44100, 48000 };
- bitRates = Range<int>(32000, 768000);
+ bitRates = Range<int32_t>(32000, 768000);
break;
case DTS_HDProfileHRA:
case DTS_HDProfileMA:
sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range<int>(96000, 24500000);
+ bitRates = Range<int32_t>(96000, 24500000);
break;
default:
ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
mMediaType.c_str());
mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range<int>(96000, 24500000);
+ bitRates = Range<int32_t>(96000, 24500000);
}
}
maxChannels = 8;
@@ -244,12 +245,12 @@
switch (profileLevel.mProfile) {
case DTS_UHDProfileP2:
sampleRates = { 48000 };
- bitRates = Range<int>(96000, 768000);
+ bitRates = Range<int32_t>(96000, 768000);
maxChannels = 10;
break;
case DTS_UHDProfileP1:
sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range<int>(96000, 24500000);
+ bitRates = Range<int32_t>(96000, 24500000);
maxChannels = 32;
break;
default:
@@ -257,7 +258,7 @@
mMediaType.c_str());
mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range<int>(96000, 24500000);
+ bitRates = Range<int32_t>(96000, 24500000);
maxChannels = 32;
}
}
@@ -270,24 +271,24 @@
if (!sampleRates.empty()) {
limitSampleRates(sampleRates);
} else if (sampleRateRange) {
- std::vector<Range<int>> rateRanges = { sampleRateRange.value() };
+ std::vector<Range<int32_t>> rateRanges = { sampleRateRange.value() };
limitSampleRates(rateRanges);
}
- Range<int> channelRange = Range<int>(1, maxChannels);
- std::vector<Range<int>> inputChannels = { channelRange };
+ Range<int32_t> channelRange = Range<int32_t>(1, maxChannels);
+ std::vector<Range<int32_t>> inputChannels = { channelRange };
applyLimits(inputChannels, bitRates);
}
void AudioCapabilities::applyLimits(
- const std::vector<Range<int>> &inputChannels,
+ const std::vector<Range<int32_t>> &inputChannels,
const std::optional<Range<int32_t>> &bitRates) {
// clamp & make a local copy
- std::vector<Range<int>> inputChannelsCopy(inputChannels.size());
+ std::vector<Range<int32_t>> inputChannelsCopy(inputChannels.size());
for (int i = 0; i < inputChannels.size(); i++) {
- int lower = inputChannels[i].clamp(1);
- int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
- inputChannelsCopy[i] = Range<int>(lower, upper);
+ int32_t lower = inputChannels[i].clamp(1);
+ int32_t upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ inputChannelsCopy[i] = Range<int32_t>(lower, upper);
}
// sort, intersect with existing, & save channel list
@@ -300,16 +301,16 @@
}
void AudioCapabilities::parseFromInfo(const sp<AMessage> &format) {
- int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
- std::vector<Range<int>> channels = { Range<int>(1, maxInputChannels) };
+ int32_t maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ std::vector<Range<int32_t>> channels = { Range<int32_t>(1, maxInputChannels) };
std::optional<Range<int32_t>> bitRates = POSITIVE_INT32;
AString rateAString;
if (format->findString("sample-rate-ranges", &rateAString)) {
std::vector<std::string> rateStrings = base::Split(std::string(rateAString.c_str()), ",");
- std::vector<Range<int>> rateRanges;
+ std::vector<Range<int32_t>> rateRanges;
for (std::string rateString : rateStrings) {
- std::optional<Range<int>> rateRange = ParseIntRange(rateString);
+ std::optional<Range<int32_t>> rateRange = Range<int32_t>::Parse(rateString);
if (!rateRange) {
continue;
}
@@ -322,9 +323,9 @@
AString valueStr;
if (format->findString("channel-ranges", &valueStr)) {
std::vector<std::string> channelStrings = base::Split(std::string(valueStr.c_str()), ",");
- std::vector<Range<int>> channelRanges;
+ std::vector<Range<int32_t>> channelRanges;
for (std::string channelString : channelStrings) {
- std::optional<Range<int>> channelRange = ParseIntRange(channelString);
+ std::optional<Range<int32_t>> channelRange = Range<int32_t>::Parse(channelString);
if (!channelRange) {
continue;
}
@@ -332,24 +333,25 @@
}
channels = channelRanges;
} else if (format->findString("channel-range", &valueStr)) {
- std::optional<Range<int>> oneRange = ParseIntRange(std::string(valueStr.c_str()));
+ std::optional<Range<int32_t>> oneRange
+ = Range<int32_t>::Parse(std::string(valueStr.c_str()));
if (oneRange) {
channels = { oneRange.value() };
}
} else if (format->findString("max-channel-count", &valueStr)) {
maxInputChannels = std::atoi(valueStr.c_str());
if (maxInputChannels == 0) {
- channels = { Range<int>(0, 0) };
+ channels = { Range<int32_t>(0, 0) };
} else {
- channels = { Range<int>(1, maxInputChannels) };
+ channels = { Range<int32_t>(1, maxInputChannels) };
}
} else if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
maxInputChannels = 0;
- channels = { Range<int>(0, 0) };
+ channels = { Range<int32_t>(0, 0) };
}
if (format->findString("bitrate-range", &valueStr)) {
- std::optional<Range<int32_t>> parsedBitrate = ParseIntRange(valueStr.c_str());
+ std::optional<Range<int32_t>> parsedBitrate = Range<int32_t>::Parse(valueStr.c_str());
if (parsedBitrate) {
bitRates = bitRates.value().intersect(parsedBitrate.value());
}
@@ -374,11 +376,11 @@
bool AudioCapabilities::supportsFormat(const sp<AMessage> &format) {
int32_t sampleRateValue;
- std::optional<int> sampleRate = format->findInt32(KEY_SAMPLE_RATE, &sampleRateValue)
- ? std::make_optional<int>(sampleRateValue) : std::nullopt;
+ std::optional<int32_t> sampleRate = format->findInt32(KEY_SAMPLE_RATE, &sampleRateValue)
+ ? std::make_optional<int32_t>(sampleRateValue) : std::nullopt;
int32_t channelsValue;
- std::optional<int> channels = format->findInt32(KEY_CHANNEL_COUNT, &channelsValue)
- ? std::make_optional<int>(channelsValue) : std::nullopt;
+ std::optional<int32_t> channels = format->findInt32(KEY_CHANNEL_COUNT, &channelsValue)
+ ? std::make_optional<int32_t>(channelsValue) : std::nullopt;
if (!supports(sampleRate, channels)) {
return false;
diff --git a/media/libmedia/CodecCapabilitiesUtils.cpp b/media/libmedia/CodecCapabilitiesUtils.cpp
index edfc9be..01bb24e 100644
--- a/media/libmedia/CodecCapabilitiesUtils.cpp
+++ b/media/libmedia/CodecCapabilitiesUtils.cpp
@@ -16,38 +16,146 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "CodecCapabilitiesUtils"
+
+#include <android-base/properties.h>
#include <utils/Log.h>
#include <algorithm>
#include <cmath>
-#include <regex>
+#include <cstdlib>
#include <string>
#include <vector>
#include <media/CodecCapabilitiesUtils.h>
-
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
namespace android {
-std::optional<Range<int>> ParseIntRange(const std::string &str) {
+// VideoSize
+
+VideoSize::VideoSize(int32_t width, int32_t height) : mWidth(width), mHeight(height) {}
+
+VideoSize::VideoSize() : mWidth(0), mHeight(0) {}
+
+int32_t VideoSize::getWidth() const { return mWidth; }
+
+int32_t VideoSize::getHeight() const { return mHeight; }
+
+bool VideoSize::equals(VideoSize other) const {
+ return mWidth == other.mWidth && mHeight == other.mHeight;
+}
+
+bool VideoSize::empty() const {
+ return mWidth <= 0 || mHeight <= 0;
+}
+
+std::string VideoSize::toString() const {
+ return std::to_string(mWidth) + "x" + std::to_string(mHeight);
+}
+
+std::optional<VideoSize> VideoSize::ParseSize(std::string str) {
if (str.empty()) {
- ALOGW("could not parse empty integer range");
return std::nullopt;
}
- int lower, upper;
- std::regex regex("([0-9]+)-([0-9]+)");
+
+ std::regex regex("([0-9]+)([*x])([0-9]+)");
std::smatch match;
if (std::regex_match(str, match, regex)) {
- lower = std::atoi(match[1].str().c_str());
- upper = std::atoi(match[2].str().c_str());
- } else if (std::atoi(str.c_str()) != 0) {
- lower = upper = std::atoi(str.c_str());
+ long int w = strtol(match[1].str().c_str(), NULL, 10);
+ long int h = strtol(match[3].str().c_str(), NULL, 10);
+ return std::make_optional(VideoSize(w, h));
} else {
- ALOGW("could not parse integer range: %s", str.c_str());
+ ALOGW("could not parse size %s", str.c_str());
return std::nullopt;
}
- return std::make_optional<Range<int>>(lower, upper);
+}
+
+std::optional<std::pair<VideoSize, VideoSize>> VideoSize::ParseSizeRange(const std::string str) {
+ size_t ix = str.find_first_of('-');
+ if (ix != std::string::npos) {
+ std::optional<VideoSize> lowerOpt = VideoSize::ParseSize(str.substr(0, ix));
+ std::optional<VideoSize> upperOpt = VideoSize::ParseSize(str.substr(ix + 1));
+ if (!lowerOpt || !upperOpt) {
+ return std::nullopt;
+ }
+ return std::make_optional(
+ std::pair<VideoSize, VideoSize>(lowerOpt.value(), upperOpt.value()));
+ } else {
+ std::optional<VideoSize> opt = VideoSize::ParseSize(str);
+ if (!opt) {
+ return std::nullopt;
+ }
+ return std::make_optional(std::pair<VideoSize, VideoSize>(opt.value(), opt.value()));
+ }
+}
+
+Range<int32_t> VideoSize::GetAllowedDimensionRange() {
+#ifdef __LP64__
+ return Range<int32_t>(1, 32768);
+#else
+ int32_t value = base::GetIntProperty("media.resolution.limit.32bit", (int32_t)4096);
+ return Range<int32_t>(1, value);
+#endif
+}
+
+// Rational
+
+std::optional<Rational> Rational::Parse(std::string str) {
+ if (str.compare("NaN") == 0) {
+ return std::make_optional(NaN);
+ } else if (str.compare("Infinity") == 0) {
+ return std::make_optional(POSITIVE_INFINITY);
+ } else if (str.compare("-Infinity") == 0) {
+ return std::make_optional(NEGATIVE_INFINITY);
+ }
+
+ std::regex regex("([0-9]+)([:/])([0-9]+)");
+ std::smatch match;
+ if (std::regex_match(str, match, regex)) {
+ long int numerator = strtol(match[1].str().c_str(), NULL, 10);
+ long int denominator = strtol(match[3].str().c_str(), NULL, 10);
+ return std::make_optional(Rational(numerator, denominator));
+ } else {
+ ALOGW("could not parse string: %s to Rational", str.c_str());
+ return std::nullopt;
+ }
+}
+
+Rational Rational::scale(int32_t num, int32_t den) {
+ int32_t common = std::gcd(num, den);
+ num /= common;
+ den /= common;
+ return Rational(
+ (int32_t)(mNumerator * (double)num), // saturate to int
+ (int32_t)(mDenominator * (double)den)); // saturate to int
+}
+
+Range<Rational> Rational::ScaleRange(Range<Rational> range, int32_t num, int32_t den) {
+ if (num == den) {
+ return range;
+ }
+ return Range(
+ range.lower().scale(num, den),
+ range.upper().scale(num, den));
+}
+
+std::optional<Range<Rational>> Rational::ParseRange(const std::string str) {
+ size_t ix = str.find_first_of('-');
+ if (ix != std::string::npos) {
+ std::optional<Rational> lower = Parse(str.substr(0, ix));
+ std::optional<Rational> upper = Parse(str.substr(ix + 1));
+ if (!lower || !upper) {
+ return std::nullopt;
+ }
+ return std::make_optional<Range<Rational>>(lower.value(), upper.value());
+ } else {
+ std::optional<Rational> value = Parse(str);
+ if (!value) {
+ return std::nullopt;
+ }
+ return std::make_optional<Range<Rational>>(value.value(), value.value());
+ }
}
} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/VideoCapabilities.cpp b/media/libmedia/VideoCapabilities.cpp
new file mode 100644
index 0000000..bd26b8c
--- /dev/null
+++ b/media/libmedia/VideoCapabilities.cpp
@@ -0,0 +1,1702 @@
+/*
+ * Copyright 2024, 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 "VideoCapabilities"
+
+#include <android-base/strings.h>
+
+#include <media/CodecCapabilities.h>
+#include <media/VideoCapabilities.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include <utils/Errors.h>
+
+namespace android {
+
+static const Range<int64_t> POSITIVE_INT64 = Range((int64_t)1, INT64_MAX);
+static const Range<int32_t> BITRATE_RANGE = Range<int32_t>(0, 500000000);
+static const Range<int32_t> FRAME_RATE_RANGE = Range<int32_t>(0, 960);
+static const Range<Rational> POSITIVE_RATIONALS =
+ Range<Rational>(Rational((int32_t)1, INT32_MAX), Rational(INT32_MAX, (int32_t)1));
+
+const Range<int32_t>& VideoCapabilities::getBitrateRange() const {
+ return mBitrateRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedWidths() const {
+ return mWidthRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedHeights() const {
+ return mHeightRange;
+}
+
+int32_t VideoCapabilities::getWidthAlignment() const {
+ return mWidthAlignment;
+}
+
+int32_t VideoCapabilities::getHeightAlignment() const {
+ return mHeightAlignment;
+}
+
+int32_t VideoCapabilities::getSmallerDimensionUpperLimit() const {
+ return mSmallerDimensionUpperLimit;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedFrameRates() const {
+ return mFrameRateRange;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedWidthsFor(int32_t height) const {
+ Range<int32_t> range = mWidthRange;
+ if (!mHeightRange.contains(height)
+ || (height % mHeightAlignment) != 0) {
+ ALOGE("unsupported height");
+ return std::nullopt;
+ }
+ const int32_t heightInBlocks = divUp(height, mBlockHeight);
+
+ // constrain by block count and by block aspect ratio
+ const int32_t minWidthInBlocks = std::max(
+ divUp(mBlockCountRange.lower(), heightInBlocks),
+ (int32_t)std::ceil(mBlockAspectRatioRange.lower().asDouble()
+ * heightInBlocks));
+ const int32_t maxWidthInBlocks = std::min(
+ mBlockCountRange.upper() / heightInBlocks,
+ (int32_t)(mBlockAspectRatioRange.upper().asDouble()
+ * heightInBlocks));
+ range = range.intersect(
+ (minWidthInBlocks - 1) * mBlockWidth + mWidthAlignment,
+ maxWidthInBlocks * mBlockWidth);
+
+ // constrain by smaller dimension limit
+ if (height > mSmallerDimensionUpperLimit) {
+ range = range.intersect(1, mSmallerDimensionUpperLimit);
+ }
+
+ // constrain by aspect ratio
+ range = range.intersect(
+ (int32_t)std::ceil(mAspectRatioRange.lower().asDouble()
+ * height),
+ (int32_t)(mAspectRatioRange.upper().asDouble() * height));
+ return range;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedHeightsFor(int32_t width) const {
+ Range<int32_t> range = mHeightRange;
+ if (!mWidthRange.contains(width)
+ || (width % mWidthAlignment) != 0) {
+ ALOGE("unsupported width");
+ return std::nullopt;
+ }
+ const int32_t widthInBlocks = divUp(width, mBlockWidth);
+
+ // constrain by block count and by block aspect ratio
+ const int32_t minHeightInBlocks = std::max(
+ divUp(mBlockCountRange.lower(), widthInBlocks),
+ (int32_t)std::ceil(widthInBlocks /
+ mBlockAspectRatioRange.upper().asDouble()));
+ const int32_t maxHeightInBlocks = std::min(
+ mBlockCountRange.upper() / widthInBlocks,
+ (int32_t)(widthInBlocks /
+ mBlockAspectRatioRange.lower().asDouble()));
+ range = range.intersect(
+ (minHeightInBlocks - 1) * mBlockHeight + mHeightAlignment,
+ maxHeightInBlocks * mBlockHeight);
+
+ // constrain by smaller dimension limit
+ if (width > mSmallerDimensionUpperLimit) {
+ range = range.intersect(1, mSmallerDimensionUpperLimit);
+ }
+
+ // constrain by aspect ratio
+ range = range.intersect(
+ (int32_t)std::ceil(width /
+ mAspectRatioRange.upper().asDouble()),
+ (int32_t)(width / mAspectRatioRange.lower().asDouble()));
+ return range;
+}
+
+std::optional<Range<double>> VideoCapabilities::getSupportedFrameRatesFor(
+ int32_t width, int32_t height) const {
+ if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */)) {
+ ALOGE("Unsupported size. width: %d, height: %d", width, height);
+ return std::nullopt;
+ }
+
+ const int32_t blockCount =
+ divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+
+ return std::make_optional(Range(
+ std::max(mBlocksPerSecondRange.lower() / (double) blockCount,
+ (double) mFrameRateRange.lower()),
+ std::min(mBlocksPerSecondRange.upper() / (double) blockCount,
+ (double) mFrameRateRange.upper())));
+}
+
+int32_t VideoCapabilities::getBlockCount(int32_t width, int32_t height) const {
+ return divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+}
+
+std::optional<VideoSize> VideoCapabilities::findClosestSize(
+ int32_t width, int32_t height) const {
+ int32_t targetBlockCount = getBlockCount(width, height);
+ std::optional<VideoSize> closestSize;
+ int32_t minDiff = INT32_MAX;
+ for (const auto &[size, range] : mMeasuredFrameRates) {
+ int32_t diff = std::abs(targetBlockCount -
+ getBlockCount(size.getWidth(), size.getHeight()));
+ if (diff < minDiff) {
+ minDiff = diff;
+ closestSize = size;
+ }
+ }
+ return closestSize;
+}
+
+std::optional<Range<double>> VideoCapabilities::estimateFrameRatesFor(
+ int32_t width, int32_t height) const {
+ std::optional<VideoSize> size = findClosestSize(width, height);
+ if (!size) {
+ return std::nullopt;
+ }
+ auto rangeItr = mMeasuredFrameRates.find(size.value());
+ if (rangeItr == mMeasuredFrameRates.end()) {
+ return std::nullopt;
+ }
+ Range<int64_t> range = rangeItr->second;
+ double ratio = getBlockCount(size.value().getWidth(), size.value().getHeight())
+ / (double)std::max(getBlockCount(width, height), 1);
+ return std::make_optional(Range(range.lower() * ratio, range.upper() * ratio));
+}
+
+std::optional<Range<double>> VideoCapabilities::getAchievableFrameRatesFor(
+ int32_t width, int32_t height) const {
+ if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */)) {
+ ALOGE("Unsupported size. width: %d, height: %d", width, height);
+ return std::nullopt;
+ }
+
+ if (mMeasuredFrameRates.empty()) {
+ ALOGW("Codec did not publish any measurement data.");
+ return std::nullopt;
+ }
+
+ return estimateFrameRatesFor(width, height);
+}
+
+// VideoCapabilities::PerformancePoint
+
+int32_t VideoCapabilities::PerformancePoint::getMaxMacroBlocks() const {
+ return saturateInt64ToInt32(mWidth * (int64_t)mHeight);
+}
+
+int32_t VideoCapabilities::PerformancePoint::getWidth() const {
+ return mWidth;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getHeight() const {
+ return mHeight;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getMaxFrameRate() const {
+ return mMaxFrameRate;
+}
+
+int64_t VideoCapabilities::PerformancePoint::getMaxMacroBlockRate() const {
+ return mMaxMacroBlockRate;
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getBlockSize() const {
+ return mBlockSize;
+}
+
+std::string VideoCapabilities::PerformancePoint::toString() const {
+ int64_t blockWidth = 16 * (int64_t)mBlockSize.getWidth();
+ int64_t blockHeight = 16 * (int64_t)mBlockSize.getHeight();
+ int32_t origRate = (int32_t)divUp(mMaxMacroBlockRate, (int64_t)getMaxMacroBlocks());
+ std::string info = std::to_string(mWidth * (int64_t)16) + "x"
+ + std::to_string(mHeight * (int64_t)16) + "@" + std::to_string(origRate);
+ if (origRate < mMaxFrameRate) {
+ info += ", max " + std::to_string(mMaxFrameRate) + "fps";
+ }
+ if (blockWidth > 16 || blockHeight > 16) {
+ info += ", " + std::to_string(blockWidth) + "x"
+ + std::to_string(blockHeight) + " blocks";
+ }
+ return "PerformancePoint(" + info + ")";
+}
+
+void VideoCapabilities::PerformancePoint::init(int32_t width, int32_t height,
+ int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+ mBlockSize = VideoSize(divUp(blockSize.getWidth(), (int32_t)16),
+ divUp(blockSize.getHeight(), (int32_t)16));
+ // Use IsPowerOfTwoStrict as we do not want width and height to be 0;
+ if (!IsPowerOfTwoStrict(blockSize.getWidth()) || !IsPowerOfTwoStrict(blockSize.getHeight())) {
+ ALOGE("The width and height of a PerformancePoint must be the power of two and not zero."
+ " width: %d, height: %d", blockSize.getWidth(), blockSize.getHeight());
+ }
+
+ // these are guaranteed not to overflow as we decimate by 16
+ mWidth = (int32_t)(divUp(std::max(width, 1),
+ std::max(blockSize.getWidth(), 16))
+ * mBlockSize.getWidth());
+ mHeight = (int32_t)(divUp(std::max(height, 1),
+ std::max(blockSize.getHeight(), 16))
+ * mBlockSize.getHeight());
+ mMaxFrameRate = std::max(std::max(frameRate, maxFrameRate), 1);
+ mMaxMacroBlockRate = std::max(frameRate, 1) * (int64_t)getMaxMacroBlocks();
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(int32_t width, int32_t height,
+ int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+ init(width, height, frameRate, maxFrameRate, blockSize);
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(VideoSize blockSize, int32_t width,
+ int32_t height, int32_t maxFrameRate, int64_t maxMacroBlockRate) :
+ mBlockSize(blockSize), mWidth(width), mHeight(height), mMaxFrameRate(maxFrameRate),
+ mMaxMacroBlockRate(maxMacroBlockRate) {}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+ const PerformancePoint &pp, VideoSize newBlockSize) {
+ init(16 * pp.mWidth, 16 * pp.mHeight,
+ // guaranteed not to overflow as these were multiplied at construction
+ (int32_t)divUp(pp.mMaxMacroBlockRate, (int64_t)pp.getMaxMacroBlocks()),
+ pp.mMaxFrameRate,
+ VideoSize(std::max(newBlockSize.getWidth(), 16 * pp.mBlockSize.getWidth()),
+ std::max(newBlockSize.getHeight(), 16 * pp.mBlockSize.getHeight())));
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+ int32_t width, int32_t height, int32_t frameRate) {
+ init(width, height, frameRate, frameRate /* maxFrameRate */, VideoSize(16, 16));
+}
+
+int32_t VideoCapabilities::PerformancePoint::saturateInt64ToInt32(int64_t value) const {
+ if (value < INT32_MIN) {
+ return INT32_MIN;
+ } else if (value > INT32_MAX) {
+ return INT32_MAX;
+ } else {
+ return (int32_t)value;
+ }
+}
+
+/* This method may overflow */
+int32_t VideoCapabilities::PerformancePoint::align(
+ int32_t value, int32_t alignment) const {
+ return divUp(value, alignment) * alignment;
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+ const sp<AMessage> &format) const {
+ int32_t width, height;
+ format->findInt32(KEY_WIDTH, &width);
+ format->findInt32(KEY_HEIGHT, &height);
+ double frameRate;
+ format->findDouble(KEY_FRAME_RATE, &frameRate);
+ PerformancePoint other = PerformancePoint(
+ width, height,
+ // safely convert ceil(double) to int through float cast and std::round
+ std::round((float)(std::ceil(frameRate)))
+ );
+ return covers(other);
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+ const PerformancePoint &other) const {
+ // convert performance points to common block size
+ VideoSize commonSize = getCommonBlockSize(other);
+ PerformancePoint aligned = PerformancePoint(*this, commonSize);
+ PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+ return (aligned.getMaxMacroBlocks() >= otherAligned.getMaxMacroBlocks()
+ && aligned.mMaxFrameRate >= otherAligned.mMaxFrameRate
+ && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getCommonBlockSize(
+ const PerformancePoint &other) const {
+ return VideoSize(
+ 16 * std::max(mBlockSize.getWidth(), other.mBlockSize.getWidth()),
+ 16 * std::max(mBlockSize.getHeight(), other.mBlockSize.getHeight()));
+}
+
+bool VideoCapabilities::PerformancePoint::equals(
+ const PerformancePoint &other) const {
+ // convert performance points to common block size
+ VideoSize commonSize = getCommonBlockSize(other);
+ PerformancePoint aligned = PerformancePoint(*this, commonSize);
+ PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+ return (aligned.getMaxMacroBlocks() == otherAligned.getMaxMacroBlocks()
+ && aligned.mMaxFrameRate == otherAligned.mMaxFrameRate
+ && aligned.mMaxMacroBlockRate == otherAligned.mMaxMacroBlockRate);
+}
+
+// VideoCapabilities
+
+const std::vector<VideoCapabilities::PerformancePoint>&
+ VideoCapabilities::getSupportedPerformancePoints() const {
+ return mPerformancePoints;
+}
+
+bool VideoCapabilities::areSizeAndRateSupported(
+ int32_t width, int32_t height, double frameRate) const {
+ return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::make_optional<double>(frameRate));
+}
+
+bool VideoCapabilities::isSizeSupported(int32_t width, int32_t height) const {
+ return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */);
+}
+
+bool VideoCapabilities::supports(std::optional<int32_t> width, std::optional<int32_t> height,
+ std::optional<double> rate) const {
+ bool ok = true;
+
+ if (width) {
+ ok &= mWidthRange.contains(width.value())
+ && (width.value() % mWidthAlignment == 0);
+ }
+ if (height) {
+ ok &= mHeightRange.contains(height.value())
+ && (height.value() % mHeightAlignment == 0);
+ }
+ if (rate) {
+ ok &= mFrameRateRange.contains(Range<int32_t>::RangeFor(rate.value()));
+ }
+ if (height && width) {
+ ok &= std::min(height.value(), width.value()) <= mSmallerDimensionUpperLimit;
+
+ const int32_t widthInBlocks = divUp(width.value(), mBlockWidth);
+ const int32_t heightInBlocks = divUp(height.value(), mBlockHeight);
+ const int32_t blockCount = widthInBlocks * heightInBlocks;
+ ok &= mBlockCountRange.contains(blockCount)
+ && mBlockAspectRatioRange.contains(
+ Rational(widthInBlocks, heightInBlocks))
+ && mAspectRatioRange.contains(Rational(width.value(), height.value()));
+ if (rate) {
+ double blocksPerSec = blockCount * rate.value();
+ ok &= mBlocksPerSecondRange.contains(
+ Range<int64_t>::RangeFor(blocksPerSec));
+ }
+ }
+ return ok;
+}
+
+bool VideoCapabilities::supportsFormat(const sp<AMessage> &format) const {
+ int32_t widthVal, heightVal;
+ std::optional<int32_t> width = format->findInt32(KEY_WIDTH, &widthVal)
+ ? std::make_optional<int32_t>(widthVal) : std::nullopt;
+ std::optional<int32_t> height = format->findInt32(KEY_HEIGHT, &heightVal)
+ ? std::make_optional<int32_t>(heightVal) : std::nullopt;
+ double rateVal;
+ std::optional<double> rate = format->findDouble(KEY_FRAME_RATE, &rateVal)
+ ? std::make_optional<double>(rateVal) : std::nullopt;
+
+ if (!supports(width, height, rate)) {
+ return false;
+ }
+
+ if (!CodecCapabilities::SupportsBitrate(mBitrateRange, format)) {
+ return false;
+ }
+
+ // we ignore color-format for now as it is not reliably reported by codec
+ return true;
+}
+
+// static
+std::shared_ptr<VideoCapabilities> VideoCapabilities::Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+ std::shared_ptr<VideoCapabilities> caps(new VideoCapabilities());
+ caps->init(mediaType, profLevs, format);
+ return caps;
+}
+
+void VideoCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format) {
+ mMediaType = mediaType;
+ mProfileLevels = profLevs;
+ mError = 0;
+
+ initWithPlatformLimits();
+ applyLevelLimits();
+ parseFromInfo(format);
+ updateLimits();
+}
+
+VideoSize VideoCapabilities::getBlockSize() const {
+ return VideoSize(mBlockWidth, mBlockHeight);
+}
+
+const Range<int32_t>& VideoCapabilities::getBlockCountRange() const {
+ return mBlockCountRange;
+}
+
+const Range<int64_t>& VideoCapabilities::getBlocksPerSecondRange() const {
+ return mBlocksPerSecondRange;
+}
+
+Range<Rational> VideoCapabilities::getAspectRatioRange(bool blocks) const {
+ return blocks ? mBlockAspectRatioRange : mAspectRatioRange;
+}
+
+void VideoCapabilities::initWithPlatformLimits() {
+ mBitrateRange = BITRATE_RANGE;
+
+ mWidthRange = VideoSize::GetAllowedDimensionRange();
+ mHeightRange = VideoSize::GetAllowedDimensionRange();
+ mFrameRateRange = FRAME_RATE_RANGE;
+
+ mHorizontalBlockRange = VideoSize::GetAllowedDimensionRange();
+ mVerticalBlockRange = VideoSize::GetAllowedDimensionRange();
+
+ // full positive ranges are supported as these get calculated
+ mBlockCountRange = POSITIVE_INT32;
+ mBlocksPerSecondRange = POSITIVE_INT64;
+
+ mBlockAspectRatioRange = POSITIVE_RATIONALS;
+ mAspectRatioRange = POSITIVE_RATIONALS;
+
+ // YUV 4:2:0 requires 2:2 alignment
+ mWidthAlignment = 2;
+ mHeightAlignment = 2;
+ mBlockWidth = 2;
+ mBlockHeight = 2;
+ mSmallerDimensionUpperLimit = VideoSize::GetAllowedDimensionRange().upper();
+}
+
+std::vector<VideoCapabilities::PerformancePoint>
+ VideoCapabilities::getPerformancePoints(
+ const sp<AMessage> &format) const {
+ std::vector<PerformancePoint> ret;
+ AMessage::Type type;
+ for (int i = 0; i < format->countEntries(); i++) {
+ const char *name = format->getEntryNameAt(i, &type);
+ AString rangeStr;
+ if (!format->findString(name, &rangeStr)) {
+ continue;
+ }
+
+ const std::string key = std::string(name);
+ // looking for: performance-point-WIDTHxHEIGHT-range
+
+ // check none performance point
+ if (key == "performance-point-none" && ret.size() == 0) {
+ // This means that component knowingly did not publish performance points.
+ // This is different from when the component forgot to publish performance
+ // points.
+ return ret;
+ }
+
+ // parse size from key
+ std::regex sizeRegex("performance-point-(.+)-range");
+ std::smatch sizeMatch;
+ if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+ continue;
+ }
+ std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+ if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+ continue;
+ }
+
+ // parse range from value
+ std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+ if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+ continue;
+ }
+
+ PerformancePoint given = PerformancePoint(
+ size.value().getWidth(), size.value().getHeight(), (int32_t)range.value().lower(),
+ (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+ PerformancePoint rotated = PerformancePoint(
+ size.value().getHeight(), size.value().getWidth(), (int32_t)range.value().lower(),
+ (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+ ret.push_back(given);
+ if (!given.covers(rotated)) {
+ ret.push_back(rotated);
+ }
+ }
+
+ // check if the component specified no performance point indication
+ if (ret.size() == 0) {
+ return ret;
+ }
+
+ // sort reversed by area first, then by frame rate
+ std::sort(ret.begin(), ret.end(), [](const PerformancePoint &a, const PerformancePoint &b) {
+ return -((a.getMaxMacroBlocks() != b.getMaxMacroBlocks()) ?
+ (a.getMaxMacroBlocks() < b.getMaxMacroBlocks() ? -1 : 1) :
+ (a.getMaxMacroBlockRate() != b.getMaxMacroBlockRate()) ?
+ (a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
+ (a.getMaxFrameRate() != b.getMaxFrameRate()) ?
+ (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0);
+ });
+
+ return ret;
+}
+
+std::map<VideoSize, Range<int64_t>, VideoSizeCompare> VideoCapabilities
+ ::getMeasuredFrameRates(const sp<AMessage> &format) const {
+ std::map<VideoSize, Range<int64_t>, VideoSizeCompare> ret;
+ AMessage::Type type;
+ for (int i = 0; i < format->countEntries(); i++) {
+ const char *name = format->getEntryNameAt(i, &type);
+ AString rangeStr;
+ if (!format->findString(name, &rangeStr)) {
+ continue;
+ }
+
+ const std::string key = std::string(name);
+ // looking for: measured-frame-rate-WIDTHxHEIGHT-range
+
+ std::regex sizeRegex("measured-frame-rate-(.+)-range");
+ std::smatch sizeMatch;
+ if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+ continue;
+ }
+
+ std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+ if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+ continue;
+ }
+
+ std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+ if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+ continue;
+ }
+
+ ret.emplace(size.value(), range.value());
+ }
+ return ret;
+}
+
+// static
+std::optional<std::pair<Range<int32_t>, Range<int32_t>>> VideoCapabilities
+ ::ParseWidthHeightRanges(const std::string &str) {
+ std::optional<std::pair<VideoSize, VideoSize>> range = VideoSize::ParseSizeRange(str);
+ if (!range) {
+ ALOGW("could not parse size range: %s", str.c_str());
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::pair(
+ Range(range.value().first.getWidth(), range.value().second.getWidth()),
+ Range(range.value().first.getHeight(), range.value().second.getHeight())));
+}
+
+// static
+int32_t VideoCapabilities::EquivalentVP9Level(const sp<AMessage> &format) {
+ int32_t blockSizeWidth = 8;
+ int32_t blockSizeHeight = 8;
+ // VideoSize *blockSizePtr = &VideoSize(8, 8);
+ AString blockSizeStr;
+ if (format->findString("block-size", &blockSizeStr)) {
+ std::optional<VideoSize> parsedBlockSize
+ = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+ if (parsedBlockSize) {
+ // blockSize = parsedBlockSize.value();
+ blockSizeWidth = parsedBlockSize.value().getWidth();
+ blockSizeHeight = parsedBlockSize.value().getHeight();
+ }
+ }
+ int32_t BS = blockSizeWidth * blockSizeHeight;
+
+ int32_t FS = 0;
+ AString blockCountRangeStr;
+ if (format->findString("block-count-range", &blockCountRangeStr)) {
+ std::optional<Range<int>> counts = Range<int32_t>::Parse(
+ std::string(blockCountRangeStr.c_str()));
+ if (counts) {
+ FS = BS * counts.value().upper();
+ }
+ }
+
+ int64_t SR = 0;
+ AString blockRatesStr;
+ if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+ std::optional<Range<int64_t>> blockRates
+ = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+ if (blockRates) {
+ // ToDo: Catch the potential overflow issue.
+ SR = BS * blockRates.value().upper();
+ }
+ }
+
+ int32_t D = 0;
+ AString dimensionRangesStr;
+ if (format->findString("size-range", &dimensionRangesStr)) {
+ std::optional<std::pair<Range<int>, Range<int>>> dimensionRanges =
+ ParseWidthHeightRanges(std::string(dimensionRangesStr.c_str()));
+ if (dimensionRanges) {
+ D = std::max(dimensionRanges.value().first.upper(),
+ dimensionRanges.value().second.upper());
+ }
+ }
+
+ int32_t BR = 0;
+ AString bitrateRangeStr;
+ if (format->findString("bitrate-range", &bitrateRangeStr)) {
+ std::optional<Range<int>> bitRates = Range<int32_t>::Parse(
+ std::string(bitrateRangeStr.c_str()));
+ if (bitRates) {
+ BR = divUp(bitRates.value().upper(), 1000);
+ }
+ }
+
+ if (SR <= 829440 && FS <= 36864 && BR <= 200 && D <= 512)
+ return VP9Level1;
+ if (SR <= 2764800 && FS <= 73728 && BR <= 800 && D <= 768)
+ return VP9Level11;
+ if (SR <= 4608000 && FS <= 122880 && BR <= 1800 && D <= 960)
+ return VP9Level2;
+ if (SR <= 9216000 && FS <= 245760 && BR <= 3600 && D <= 1344)
+ return VP9Level21;
+ if (SR <= 20736000 && FS <= 552960 && BR <= 7200 && D <= 2048)
+ return VP9Level3;
+ if (SR <= 36864000 && FS <= 983040 && BR <= 12000 && D <= 2752)
+ return VP9Level31;
+ if (SR <= 83558400 && FS <= 2228224 && BR <= 18000 && D <= 4160)
+ return VP9Level4;
+ if (SR <= 160432128 && FS <= 2228224 && BR <= 30000 && D <= 4160)
+ return VP9Level41;
+ if (SR <= 311951360 && FS <= 8912896 && BR <= 60000 && D <= 8384)
+ return VP9Level5;
+ if (SR <= 588251136 && FS <= 8912896 && BR <= 120000 && D <= 8384)
+ return VP9Level51;
+ if (SR <= 1176502272 && FS <= 8912896 && BR <= 180000 && D <= 8384)
+ return VP9Level52;
+ if (SR <= 1176502272 && FS <= 35651584 && BR <= 180000 && D <= 16832)
+ return VP9Level6;
+ if (SR <= 2353004544L && FS <= 35651584 && BR <= 240000 && D <= 16832)
+ return VP9Level61;
+ if (SR <= 4706009088L && FS <= 35651584 && BR <= 480000 && D <= 16832)
+ return VP9Level62;
+ // returning largest level
+ return VP9Level62;
+}
+
+void VideoCapabilities::parseFromInfo(const sp<AMessage> &format) {
+ VideoSize blockSize = VideoSize(mBlockWidth, mBlockHeight);
+ VideoSize alignment = VideoSize(mWidthAlignment, mHeightAlignment);
+ std::optional<Range<int32_t>> counts, widths, heights;
+ std::optional<Range<int32_t>> frameRates, bitRates;
+ std::optional<Range<int64_t>> blockRates;
+ std::optional<Range<Rational>> ratios, blockRatios;
+
+ AString blockSizeStr;
+ if (format->findString("block-size", &blockSizeStr)) {
+ std::optional<VideoSize> parsedBlockSize
+ = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+ blockSize = parsedBlockSize.value_or(blockSize);
+ }
+ AString alignmentStr;
+ if (format->findString("alignment", &alignmentStr)) {
+ std::optional<VideoSize> parsedAlignment
+ = VideoSize::ParseSize(std::string(alignmentStr.c_str()));
+ alignment = parsedAlignment.value_or(alignment);
+ }
+ AString blockCountRangeStr;
+ if (format->findString("block-count-range", &blockCountRangeStr)) {
+ std::optional<Range<int>> parsedBlockCountRange =
+ Range<int32_t>::Parse(std::string(blockCountRangeStr.c_str()));
+ if (parsedBlockCountRange) {
+ counts = parsedBlockCountRange.value();
+ }
+ }
+ AString blockRatesStr;
+ if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+ blockRates = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+ }
+ mMeasuredFrameRates = getMeasuredFrameRates(format);
+ mPerformancePoints = getPerformancePoints(format);
+ AString sizeRangesStr;
+ if (format->findString("size-range", &sizeRangesStr)) {
+ std::optional<std::pair<Range<int>, Range<int>>> sizeRanges =
+ ParseWidthHeightRanges(std::string(sizeRangesStr.c_str()));
+ if (sizeRanges) {
+ widths = sizeRanges.value().first;
+ heights = sizeRanges.value().second;
+ }
+ }
+ // for now this just means using the smaller max size as 2nd
+ // upper limit.
+ // for now we are keeping the profile specific "width/height
+ // in macroblocks" limits.
+ if (format->contains("feature-can-swap-width-height")) {
+ if (widths && heights) {
+ mSmallerDimensionUpperLimit =
+ std::min(widths.value().upper(), heights.value().upper());
+ widths = heights = widths.value().extend(heights.value());
+ } else {
+ ALOGW("feature can-swap-width-height is best used with size-range");
+ mSmallerDimensionUpperLimit =
+ std::min(mWidthRange.upper(), mHeightRange.upper());
+ mWidthRange = mHeightRange = mWidthRange.extend(mHeightRange);
+ }
+ }
+
+ AString ratioStr;
+ if (format->findString("block-aspect-ratio-range", &ratioStr)) {
+ ratios = Rational::ParseRange(std::string(ratioStr.c_str()));
+ }
+ AString blockRatiosStr;
+ if (format->findString("pixel-aspect-ratio-range", &blockRatiosStr)) {
+ blockRatios = Rational::ParseRange(std::string(blockRatiosStr.c_str()));
+ }
+ AString frameRatesStr;
+ if (format->findString("frame-rate-range", &frameRatesStr)) {
+ frameRates = Range<int32_t>::Parse(std::string(frameRatesStr.c_str()));
+ if (frameRates) {
+ frameRates = frameRates.value().intersect(FRAME_RATE_RANGE);
+ if (frameRates.value().empty()) {
+ ALOGW("frame rate range is out of limits");
+ frameRates = std::nullopt;
+ }
+ }
+ }
+ AString bitRatesStr;
+ if (format->findString("bitrate-range", &bitRatesStr)) {
+ bitRates = Range<int32_t>::Parse(std::string(bitRatesStr.c_str()));
+ if (bitRates) {
+ bitRates = bitRates.value().intersect(BITRATE_RANGE);
+ if (bitRates.value().empty()) {
+ ALOGW("bitrate range is out of limits");
+ bitRates = std::nullopt;
+ }
+ }
+ }
+
+ if (!IsPowerOfTwo(blockSize.getWidth()) || !IsPowerOfTwo(blockSize.getHeight())
+ || !IsPowerOfTwo(alignment.getWidth()) || !IsPowerOfTwo(alignment.getHeight())) {
+ ALOGE("The widths and heights of blockSizes and alignments must be the power of two."
+ " blockSize width: %d; blockSize height: %d;"
+ " alignment width: %d; alignment height: %d.",
+ blockSize.getWidth(), blockSize.getHeight(),
+ alignment.getWidth(), alignment.getHeight());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ // update block-size and alignment
+ applyMacroBlockLimits(
+ INT32_MAX, INT32_MAX, INT32_MAX, INT64_MAX,
+ blockSize.getWidth(), blockSize.getHeight(),
+ alignment.getWidth(), alignment.getHeight());
+
+ if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0 || mAllowMbOverride) {
+ // codec supports profiles that we don't know.
+ // Use supplied values clipped to platform limits
+ if (widths) {
+ mWidthRange = VideoSize::GetAllowedDimensionRange().intersect(widths.value());
+ }
+ if (heights) {
+ mHeightRange = VideoSize::GetAllowedDimensionRange().intersect(heights.value());
+ }
+ if (counts) {
+ mBlockCountRange = POSITIVE_INT32.intersect(
+ counts.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRates) {
+ mBlocksPerSecondRange = POSITIVE_INT64.intersect(
+ blockRates.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRatios) {
+ mBlockAspectRatioRange = POSITIVE_RATIONALS.intersect(
+ Rational::ScaleRange(blockRatios.value(),
+ mBlockHeight / blockSize.getHeight(),
+ mBlockWidth / blockSize.getWidth()));
+ }
+ if (ratios) {
+ mAspectRatioRange = POSITIVE_RATIONALS.intersect(ratios.value());
+ }
+ if (frameRates) {
+ mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates.value());
+ }
+ if (bitRates) {
+ // only allow bitrate override if unsupported profiles were encountered
+ if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
+ mBitrateRange = BITRATE_RANGE.intersect(bitRates.value());
+ } else {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+ }
+ } else {
+ // no unsupported profile/levels, so restrict values to known limits
+ if (widths) {
+ mWidthRange = mWidthRange.intersect(widths.value());
+ }
+ if (heights) {
+ mHeightRange = mHeightRange.intersect(heights.value());
+ }
+ if (counts) {
+ mBlockCountRange = mBlockCountRange.intersect(
+ counts.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRates) {
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+ blockRates.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRatios) {
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+ Rational::ScaleRange(blockRatios.value(),
+ mBlockHeight / blockSize.getHeight(),
+ mBlockWidth / blockSize.getWidth()));
+ }
+ if (ratios) {
+ mAspectRatioRange = mAspectRatioRange.intersect(ratios.value());
+ }
+ if (frameRates) {
+ mFrameRateRange = mFrameRateRange.intersect(frameRates.value());
+ }
+ if (bitRates) {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+ }
+ updateLimits();
+}
+
+void VideoCapabilities::applyBlockLimits(
+ int32_t blockWidth, int32_t blockHeight,
+ Range<int32_t> counts, Range<int64_t> rates, Range<Rational> ratios) {
+
+ if (!IsPowerOfTwo(blockWidth) || !IsPowerOfTwo(blockHeight)) {
+ ALOGE("blockWidth and blockHeight must be the power of two."
+ " blockWidth: %d; blockHeight: %d", blockWidth, blockHeight);
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ const int32_t newBlockWidth = std::max(blockWidth, mBlockWidth);
+ const int32_t newBlockHeight = std::max(blockHeight, mBlockHeight);
+
+ // factor will always be a power-of-2
+ int32_t factor =
+ newBlockWidth * newBlockHeight / mBlockWidth / mBlockHeight;
+ if (factor != 1) {
+ mBlockCountRange = mBlockCountRange.factor(factor);
+ mBlocksPerSecondRange = mBlocksPerSecondRange.factor(factor);
+ mBlockAspectRatioRange = Rational::ScaleRange(
+ mBlockAspectRatioRange,
+ newBlockHeight / mBlockHeight,
+ newBlockWidth / mBlockWidth);
+ mHorizontalBlockRange = mHorizontalBlockRange.factor(newBlockWidth / mBlockWidth);
+ mVerticalBlockRange = mVerticalBlockRange.factor(newBlockHeight / mBlockHeight);
+ }
+ factor = newBlockWidth * newBlockHeight / blockWidth / blockHeight;
+ if (factor != 1) {
+ counts = counts.factor(factor);
+ rates = rates.factor((int64_t)factor);
+ ratios = Rational::ScaleRange(
+ ratios, newBlockHeight / blockHeight,
+ newBlockWidth / blockWidth);
+ }
+ mBlockCountRange = mBlockCountRange.intersect(counts);
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(rates);
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(ratios);
+ mBlockWidth = newBlockWidth;
+ mBlockHeight = newBlockHeight;
+}
+
+void VideoCapabilities::applyAlignment(
+ int32_t widthAlignment, int32_t heightAlignment) {
+ if (!IsPowerOfTwo(widthAlignment) || !IsPowerOfTwo(heightAlignment)) {
+ ALOGE("width and height alignments must be the power of two."
+ " widthAlignment: %d; heightAlignment: %d", widthAlignment, heightAlignment);
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ if (widthAlignment > mBlockWidth || heightAlignment > mBlockHeight) {
+ // maintain assumption that 0 < alignment <= block-size
+ applyBlockLimits(
+ std::max(widthAlignment, mBlockWidth),
+ std::max(heightAlignment, mBlockHeight),
+ POSITIVE_INT32, POSITIVE_INT64, POSITIVE_RATIONALS);
+ }
+
+ mWidthAlignment = std::max(widthAlignment, mWidthAlignment);
+ mHeightAlignment = std::max(heightAlignment, mHeightAlignment);
+
+ mWidthRange = mWidthRange.align(mWidthAlignment);
+ mHeightRange = mHeightRange.align(mHeightAlignment);
+}
+
+void VideoCapabilities::updateLimits() {
+ // pixels -> blocks <- counts
+ mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+ mWidthRange.factor(mBlockWidth));
+ mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+ Range( mBlockCountRange.lower() / mVerticalBlockRange.upper(),
+ mBlockCountRange.upper() / mVerticalBlockRange.lower()));
+ mVerticalBlockRange = mVerticalBlockRange.intersect(
+ mHeightRange.factor(mBlockHeight));
+ mVerticalBlockRange = mVerticalBlockRange.intersect(
+ Range( mBlockCountRange.lower() / mHorizontalBlockRange.upper(),
+ mBlockCountRange.upper() / mHorizontalBlockRange.lower()));
+ mBlockCountRange = mBlockCountRange.intersect(
+ Range( mHorizontalBlockRange.lower()
+ * mVerticalBlockRange.lower(),
+ mHorizontalBlockRange.upper()
+ * mVerticalBlockRange.upper()));
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+ Rational(mHorizontalBlockRange.lower(), mVerticalBlockRange.upper()),
+ Rational(mHorizontalBlockRange.upper(), mVerticalBlockRange.lower()));
+
+ // blocks -> pixels
+ mWidthRange = mWidthRange.intersect(
+ (mHorizontalBlockRange.lower() - 1) * mBlockWidth + mWidthAlignment,
+ mHorizontalBlockRange.upper() * mBlockWidth);
+ mHeightRange = mHeightRange.intersect(
+ (mVerticalBlockRange.lower() - 1) * mBlockHeight + mHeightAlignment,
+ mVerticalBlockRange.upper() * mBlockHeight);
+ mAspectRatioRange = mAspectRatioRange.intersect(
+ Rational(mWidthRange.lower(), mHeightRange.upper()),
+ Rational(mWidthRange.upper(), mHeightRange.lower()));
+
+ mSmallerDimensionUpperLimit = std::min(
+ mSmallerDimensionUpperLimit,
+ std::min(mWidthRange.upper(), mHeightRange.upper()));
+
+ // blocks -> rate
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+ mBlockCountRange.lower() * (int64_t)mFrameRateRange.lower(),
+ mBlockCountRange.upper() * (int64_t)mFrameRateRange.upper());
+ mFrameRateRange = mFrameRateRange.intersect(
+ (int32_t)(mBlocksPerSecondRange.lower()
+ / mBlockCountRange.upper()),
+ (int32_t)(mBlocksPerSecondRange.upper()
+ / (double)mBlockCountRange.lower()));
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment) {
+ applyMacroBlockLimits(
+ 1 /* minHorizontalBlocks */, 1 /* minVerticalBlocks */,
+ maxHorizontalBlocks, maxVerticalBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockWidth, blockHeight, widthAlignment, heightAlignment);
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+ int32_t minHorizontalBlocks, int32_t minVerticalBlocks,
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment) {
+ applyAlignment(widthAlignment, heightAlignment);
+ applyBlockLimits(
+ blockWidth, blockHeight, Range((int32_t)1, maxBlocks),
+ Range((int64_t)1, maxBlocksPerSecond),
+ Range(Rational(1, maxVerticalBlocks), Rational(maxHorizontalBlocks, 1)));
+ mHorizontalBlockRange =
+ mHorizontalBlockRange.intersect(
+ divUp(minHorizontalBlocks, (mBlockWidth / blockWidth)),
+ maxHorizontalBlocks / (mBlockWidth / blockWidth));
+ mVerticalBlockRange =
+ mVerticalBlockRange.intersect(
+ divUp(minVerticalBlocks, (mBlockHeight / blockHeight)),
+ maxVerticalBlocks / (mBlockHeight / blockHeight));
+}
+
+void VideoCapabilities::applyLevelLimits() {
+ int64_t maxBlocksPerSecond = 0;
+ int32_t maxBlocks = 0;
+ int32_t maxBps = 0;
+ int32_t maxDPBBlocks = 0;
+
+ int errors = ERROR_CAPABILITIES_NONE_SUPPORTED;
+ const char *mediaType = mMediaType.c_str();
+ if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AVC)) {
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ maxDPBBlocks = 396;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, DPB = 0;
+ bool supported = true;
+ switch (profileLevel.mLevel) {
+ case AVCLevel1:
+ MBPS = 1485; FS = 99; BR = 64; DPB = 396; break;
+ case AVCLevel1b:
+ MBPS = 1485; FS = 99; BR = 128; DPB = 396; break;
+ case AVCLevel11:
+ MBPS = 3000; FS = 396; BR = 192; DPB = 900; break;
+ case AVCLevel12:
+ MBPS = 6000; FS = 396; BR = 384; DPB = 2376; break;
+ case AVCLevel13:
+ MBPS = 11880; FS = 396; BR = 768; DPB = 2376; break;
+ case AVCLevel2:
+ MBPS = 11880; FS = 396; BR = 2000; DPB = 2376; break;
+ case AVCLevel21:
+ MBPS = 19800; FS = 792; BR = 4000; DPB = 4752; break;
+ case AVCLevel22:
+ MBPS = 20250; FS = 1620; BR = 4000; DPB = 8100; break;
+ case AVCLevel3:
+ MBPS = 40500; FS = 1620; BR = 10000; DPB = 8100; break;
+ case AVCLevel31:
+ MBPS = 108000; FS = 3600; BR = 14000; DPB = 18000; break;
+ case AVCLevel32:
+ MBPS = 216000; FS = 5120; BR = 20000; DPB = 20480; break;
+ case AVCLevel4:
+ MBPS = 245760; FS = 8192; BR = 20000; DPB = 32768; break;
+ case AVCLevel41:
+ MBPS = 245760; FS = 8192; BR = 50000; DPB = 32768; break;
+ case AVCLevel42:
+ MBPS = 522240; FS = 8704; BR = 50000; DPB = 34816; break;
+ case AVCLevel5:
+ MBPS = 589824; FS = 22080; BR = 135000; DPB = 110400; break;
+ case AVCLevel51:
+ MBPS = 983040; FS = 36864; BR = 240000; DPB = 184320; break;
+ case AVCLevel52:
+ MBPS = 2073600; FS = 36864; BR = 240000; DPB = 184320; break;
+ case AVCLevel6:
+ MBPS = 4177920; FS = 139264; BR = 240000; DPB = 696320; break;
+ case AVCLevel61:
+ MBPS = 8355840; FS = 139264; BR = 480000; DPB = 696320; break;
+ case AVCLevel62:
+ MBPS = 16711680; FS = 139264; BR = 800000; DPB = 696320; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case AVCProfileConstrainedHigh:
+ case AVCProfileHigh:
+ BR *= 1250; break;
+ case AVCProfileHigh10:
+ BR *= 3000; break;
+ case AVCProfileExtended:
+ case AVCProfileHigh422:
+ case AVCProfileHigh444:
+ ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ supported = false;
+ FALLTHROUGH_INTENDED;
+ // fall through - treat as base profile
+ case AVCProfileConstrainedBaseline:
+ FALLTHROUGH_INTENDED;
+ case AVCProfileBaseline:
+ FALLTHROUGH_INTENDED;
+ case AVCProfileMain:
+ BR *= 1000; break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ BR *= 1000;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR, maxBps);
+ maxDPBBlocks = std::max(maxDPBBlocks, DPB);
+ }
+
+ int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG2)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ bool supported = true;
+ switch (profileLevel.mProfile) {
+ case MPEG2ProfileSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG2ProfileMain:
+ switch (profileLevel.mLevel) {
+ case MPEG2LevelLL:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
+ case MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
+ case MPEG2LevelH14:
+ FR = 60; W = 90; H = 68; MBPS = 183600; FS = 6120; BR = 60000; break;
+ case MPEG2LevelHL:
+ FR = 60; W = 120; H = 68; MBPS = 244800; FS = 8160; BR = 80000; break;
+ case MPEG2LevelHP:
+ FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d / %d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG2Profile422:
+ case MPEG2ProfileSNR:
+ case MPEG2ProfileSpatial:
+ case MPEG2ProfileHigh:
+ ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG4)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ bool strict = false; // true: W, H and FR are individual max limits
+ bool supported = true;
+ switch (profileLevel.mProfile) {
+ case MPEG4ProfileSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG4Level0:
+ strict = true;
+ FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
+ case MPEG4Level1:
+ FR = 30; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
+ case MPEG4Level0b:
+ strict = true;
+ FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 128; break;
+ case MPEG4Level2:
+ FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 128; break;
+ case MPEG4Level3:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
+ case MPEG4Level4a:
+ FR = 30; W = 40; H = 30; MBPS = 36000; FS = 1200; BR = 4000; break;
+ case MPEG4Level5:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 8000; break;
+ case MPEG4Level6:
+ FR = 30; W = 80; H = 45; MBPS = 108000; FS = 3600; BR = 12000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG4ProfileAdvancedSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG4Level0:
+ case MPEG4Level1:
+ FR = 30; W = 11; H = 9; MBPS = 2970; FS = 99; BR = 128; break;
+ case MPEG4Level2:
+ FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 384; break;
+ case MPEG4Level3:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 768; break;
+ case MPEG4Level3b:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 1500; break;
+ case MPEG4Level4:
+ FR = 30; W = 44; H = 36; MBPS = 23760; FS = 792; BR = 3000; break;
+ case MPEG4Level5:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG4ProfileMain: // 2-4
+ case MPEG4ProfileNbit: // 2
+ case MPEG4ProfileAdvancedRealTime: // 1-4
+ case MPEG4ProfileCoreScalable: // 1-3
+ case MPEG4ProfileAdvancedCoding: // 1-4
+ case MPEG4ProfileCore: // 1-2
+ case MPEG4ProfileAdvancedCore: // 1-4
+ case MPEG4ProfileSimpleScalable: // 0-2
+ case MPEG4ProfileHybrid: // 1-2
+
+ // Studio profiles are not supported by our codecs.
+
+ // Only profiles that can decode simple object types are considered.
+ // The following profiles are not able to.
+ case MPEG4ProfileBasicAnimated: // 1-2
+ case MPEG4ProfileScalableTexture: // 1
+ case MPEG4ProfileSimpleFace: // 1-2
+ case MPEG4ProfileAdvancedScalable: // 1-3
+ case MPEG4ProfileSimpleFBA: // 1-2
+ ALOGV("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ if (strict) {
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ } else {
+ // assuming max 60 fps frame rate and 1:2 aspect ratio
+ int32_t maxDim = (int32_t)std::sqrt(2 * FS);
+ maxWidth = std::max(maxDim, maxWidth);
+ maxHeight = std::max(maxDim, maxHeight);
+ maxRate = std::max(std::max(FR, 60), maxRate);
+ }
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_H263)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ int32_t minWidth = maxWidth, minHeight = maxHeight;
+ int32_t minAlignment = 16;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, BR = 0, FR = 0, W = 0, H = 0, minW = minWidth, minH = minHeight;
+ bool strict = false; // true: support only sQCIF, QCIF (maybe CIF)
+ switch (profileLevel.mLevel) {
+ case H263Level10:
+ strict = true; // only supports sQCIF & QCIF
+ FR = 15; W = 11; H = 9; BR = 1; MBPS = W * H * FR; break;
+ case H263Level20:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 2; MBPS = W * H * 15; break;
+ case H263Level30:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 6; MBPS = W * H * FR; break;
+ case H263Level40:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 32; MBPS = W * H * FR; break;
+ case H263Level45:
+ // only implies level 10 support
+ strict = profileLevel.mProfile == H263ProfileBaseline
+ || profileLevel.mProfile ==
+ H263ProfileBackwardCompatible;
+ if (!strict) {
+ minW = 1; minH = 1; minAlignment = 4;
+ }
+ FR = 15; W = 11; H = 9; BR = 2; MBPS = W * H * FR; break;
+ case H263Level50:
+ // only supports 50fps for H > 15
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 22; H = 18; BR = 64; MBPS = W * H * 50; break;
+ case H263Level60:
+ // only supports 50fps for H > 15
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 45; H = 18; BR = 128; MBPS = W * H * 50; break;
+ case H263Level70:
+ // only supports 50fps for H > 30
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 45; H = 36; BR = 256; MBPS = W * H * 50; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case H263ProfileBackwardCompatible:
+ case H263ProfileBaseline:
+ case H263ProfileH320Coding:
+ case H263ProfileHighCompression:
+ case H263ProfileHighLatency:
+ case H263ProfileInterlace:
+ case H263ProfileInternet:
+ case H263ProfileISWV2:
+ case H263ProfileISWV3:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (strict) {
+ // Strict levels define sub-QCIF min size and enumerated sizes. We cannot
+ // express support for "only sQCIF & QCIF (& CIF)" using VideoCapabilities
+ // but we can express "only QCIF (& CIF)", so set minimume size at QCIF.
+ // minW = 8; minH = 6;
+ minW = 11; minH = 9;
+ } else {
+ // any support for non-strict levels (including unrecognized profiles or
+ // levels) allow custom frame size support beyond supported limits
+ // (other than bitrate)
+ mAllowMbOverride = true;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(W * H, maxBlocks);
+ maxBps = std::max(BR * 64000, maxBps);
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ minWidth = std::min(minW, minWidth);
+ minHeight = std::min(minH, minHeight);
+ }
+ // unless we encountered custom frame size support, limit size to QCIF and CIF
+ // using aspect ratio.
+ if (!mAllowMbOverride) {
+ mBlockAspectRatioRange =
+ Range(Rational(11, 9), Rational(11, 9));
+ }
+ applyMacroBlockLimits(
+ minWidth, minHeight,
+ maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ minAlignment /* widthAlignment */, minAlignment /* heightAlignment */);
+ mFrameRateRange = Range(1, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP8)) {
+ maxBlocks = INT_MAX;
+ maxBlocksPerSecond = INT_MAX;
+
+ // TODO: set to 100Mbps for now, need a number for VP8
+ maxBps = 100000000;
+
+ // profile levels are not indicative for VPx, but verify
+ // them nonetheless
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mLevel) {
+ case VP8Level_Version0:
+ case VP8Level_Version1:
+ case VP8Level_Version2:
+ case VP8Level_Version3:
+ break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case VP8ProfileMain:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+
+ const int32_t blockSize = 16;
+ applyMacroBlockLimits(SHRT_MAX, SHRT_MAX,
+ maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP9)) {
+ maxBlocksPerSecond = 829440;
+ maxBlocks = 36864;
+ maxBps = 200000;
+ int32_t maxDim = 512;
+
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int64_t SR = 0; // luma sample rate
+ int32_t FS = 0; // luma picture size
+ int32_t BR = 0; // bit rate kbps
+ int32_t D = 0; // luma dimension
+ switch (profileLevel.mLevel) {
+ case VP9Level1:
+ SR = 829440; FS = 36864; BR = 200; D = 512; break;
+ case VP9Level11:
+ SR = 2764800; FS = 73728; BR = 800; D = 768; break;
+ case VP9Level2:
+ SR = 4608000; FS = 122880; BR = 1800; D = 960; break;
+ case VP9Level21:
+ SR = 9216000; FS = 245760; BR = 3600; D = 1344; break;
+ case VP9Level3:
+ SR = 20736000; FS = 552960; BR = 7200; D = 2048; break;
+ case VP9Level31:
+ SR = 36864000; FS = 983040; BR = 12000; D = 2752; break;
+ case VP9Level4:
+ SR = 83558400; FS = 2228224; BR = 18000; D = 4160; break;
+ case VP9Level41:
+ SR = 160432128; FS = 2228224; BR = 30000; D = 4160; break;
+ case VP9Level5:
+ SR = 311951360; FS = 8912896; BR = 60000; D = 8384; break;
+ case VP9Level51:
+ SR = 588251136; FS = 8912896; BR = 120000; D = 8384; break;
+ case VP9Level52:
+ SR = 1176502272; FS = 8912896; BR = 180000; D = 8384; break;
+ case VP9Level6:
+ SR = 1176502272; FS = 35651584; BR = 180000; D = 16832; break;
+ case VP9Level61:
+ SR = 2353004544L; FS = 35651584; BR = 240000; D = 16832; break;
+ case VP9Level62:
+ SR = 4706009088L; FS = 35651584; BR = 480000; D = 16832; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case VP9Profile0:
+ case VP9Profile1:
+ case VP9Profile2:
+ case VP9Profile3:
+ case VP9Profile2HDR:
+ case VP9Profile3HDR:
+ case VP9Profile2HDR10Plus:
+ case VP9Profile3HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxDim = std::max(D, maxDim);
+ }
+
+ const int32_t blockSize = 8;
+ int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+ maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+ maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_HEVC)) {
+ // CTBs are at least 8x8 so use 8x8 block size
+ maxBlocks = 36864 >> 6; // 192x192 pixels == 576 8x8 blocks
+ maxBlocksPerSecond = maxBlocks * 15;
+ maxBps = 128000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ double FR = 0;
+ int32_t FS = 0, BR = 0;
+ switch (profileLevel.mLevel) {
+ /* The HEVC spec talks only in a very convoluted manner about the
+ existence of levels 1-3.1 for High tier, which could also be
+ understood as 'decoders and encoders should treat these levels
+ as if they were Main tier', so we do that. */
+ case HEVCMainTierLevel1:
+ case HEVCHighTierLevel1:
+ FR = 15; FS = 36864; BR = 128; break;
+ case HEVCMainTierLevel2:
+ case HEVCHighTierLevel2:
+ FR = 30; FS = 122880; BR = 1500; break;
+ case HEVCMainTierLevel21:
+ case HEVCHighTierLevel21:
+ FR = 30; FS = 245760; BR = 3000; break;
+ case HEVCMainTierLevel3:
+ case HEVCHighTierLevel3:
+ FR = 30; FS = 552960; BR = 6000; break;
+ case HEVCMainTierLevel31:
+ case HEVCHighTierLevel31:
+ FR = 33.75; FS = 983040; BR = 10000; break;
+ case HEVCMainTierLevel4:
+ FR = 30; FS = 2228224; BR = 12000; break;
+ case HEVCHighTierLevel4:
+ FR = 30; FS = 2228224; BR = 30000; break;
+ case HEVCMainTierLevel41:
+ FR = 60; FS = 2228224; BR = 20000; break;
+ case HEVCHighTierLevel41:
+ FR = 60; FS = 2228224; BR = 50000; break;
+ case HEVCMainTierLevel5:
+ FR = 30; FS = 8912896; BR = 25000; break;
+ case HEVCHighTierLevel5:
+ FR = 30; FS = 8912896; BR = 100000; break;
+ case HEVCMainTierLevel51:
+ FR = 60; FS = 8912896; BR = 40000; break;
+ case HEVCHighTierLevel51:
+ FR = 60; FS = 8912896; BR = 160000; break;
+ case HEVCMainTierLevel52:
+ FR = 120; FS = 8912896; BR = 60000; break;
+ case HEVCHighTierLevel52:
+ FR = 120; FS = 8912896; BR = 240000; break;
+ case HEVCMainTierLevel6:
+ FR = 30; FS = 35651584; BR = 60000; break;
+ case HEVCHighTierLevel6:
+ FR = 30; FS = 35651584; BR = 240000; break;
+ case HEVCMainTierLevel61:
+ FR = 60; FS = 35651584; BR = 120000; break;
+ case HEVCHighTierLevel61:
+ FR = 60; FS = 35651584; BR = 480000; break;
+ case HEVCMainTierLevel62:
+ FR = 120; FS = 35651584; BR = 240000; break;
+ case HEVCHighTierLevel62:
+ FR = 120; FS = 35651584; BR = 800000; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case HEVCProfileMain:
+ case HEVCProfileMain10:
+ case HEVCProfileMainStill:
+ case HEVCProfileMain10HDR10:
+ case HEVCProfileMain10HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+
+ /* DPB logic:
+ if (width * height <= FS / 4) DPB = 16;
+ else if (width * height <= FS / 2) DPB = 12;
+ else if (width * height <= FS * 0.75) DPB = 8;
+ else DPB = 6;
+ */
+
+ FS >>= 6; // convert pixels to blocks
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max((int64_t)(FR * FS), maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(1000 * BR, maxBps);
+ }
+
+ int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ 8 /* blockWidth */, 8 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AV1)) {
+ maxBlocksPerSecond = 829440;
+ maxBlocks = 36864;
+ maxBps = 200000;
+ int32_t maxDim = 512;
+
+ // Sample rate, Picture Size, Bit rate and luma dimension for AV1 Codec,
+ // corresponding to the definitions in
+ // "AV1 Bitstream & Decoding Process Specification", Annex A
+ // found at https://aomedia.org/av1-bitstream-and-decoding-process-specification/
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int64_t SR = 0; // luma sample rate
+ int32_t FS = 0; // luma picture size
+ int32_t BR = 0; // bit rate kbps
+ int32_t D = 0; // luma D
+ switch (profileLevel.mLevel) {
+ case AV1Level2:
+ SR = 5529600; FS = 147456; BR = 1500; D = 2048; break;
+ case AV1Level21:
+ case AV1Level22:
+ case AV1Level23:
+ SR = 10454400; FS = 278784; BR = 3000; D = 2816; break;
+
+ case AV1Level3:
+ SR = 24969600; FS = 665856; BR = 6000; D = 4352; break;
+ case AV1Level31:
+ case AV1Level32:
+ case AV1Level33:
+ SR = 39938400; FS = 1065024; BR = 10000; D = 5504; break;
+
+ case AV1Level4:
+ SR = 77856768; FS = 2359296; BR = 12000; D = 6144; break;
+ case AV1Level41:
+ case AV1Level42:
+ case AV1Level43:
+ SR = 155713536; FS = 2359296; BR = 20000; D = 6144; break;
+
+ case AV1Level5:
+ SR = 273715200; FS = 8912896; BR = 30000; D = 8192; break;
+ case AV1Level51:
+ SR = 547430400; FS = 8912896; BR = 40000; D = 8192; break;
+ case AV1Level52:
+ SR = 1094860800; FS = 8912896; BR = 60000; D = 8192; break;
+ case AV1Level53:
+ SR = 1176502272; FS = 8912896; BR = 60000; D = 8192; break;
+
+ case AV1Level6:
+ SR = 1176502272; FS = 35651584; BR = 60000; D = 16384; break;
+ case AV1Level61:
+ SR = 2189721600L; FS = 35651584; BR = 100000; D = 16384; break;
+ case AV1Level62:
+ SR = 4379443200L; FS = 35651584; BR = 160000; D = 16384; break;
+ case AV1Level63:
+ SR = 4706009088L; FS = 35651584; BR = 160000; D = 16384; break;
+
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case AV1ProfileMain8:
+ case AV1ProfileMain10:
+ case AV1ProfileMain10HDR10:
+ case AV1ProfileMain10HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxDim = std::max(D, maxDim);
+ }
+
+ const int32_t blockSize = 8;
+ int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+ maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+ maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else {
+ ALOGW("Unsupported mime %s", mediaType);
+ // using minimal bitrate here. should be overridden by
+ // info from media_codecs.xml
+ maxBps = 64000;
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ }
+ mBitrateRange = Range(1, maxBps);
+ mError |= errors;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/include/media/AudioCapabilities.h b/media/libmedia/include/media/AudioCapabilities.h
index d2bd9d7..3e1c9df 100644
--- a/media/libmedia/include/media/AudioCapabilities.h
+++ b/media/libmedia/include/media/AudioCapabilities.h
@@ -44,14 +44,14 @@
* supports only discrete values. Otherwise, it returns an empty array.
* The array is sorted in ascending order.
*/
- const std::vector<int>& getSupportedSampleRates() const;
+ const std::vector<int32_t>& getSupportedSampleRates() const;
/**
* Returns the array of supported sample rate ranges. The
* array is sorted in ascending order, and the ranges are
* distinct.
*/
- const std::vector<Range<int>>& getSupportedSampleRateRanges() const;
+ const std::vector<Range<int32_t>>& getSupportedSampleRateRanges() const;
/**
* Returns the maximum number of input channels supported.
@@ -68,7 +68,7 @@
* The {@link #getMaxInputChannelCount} method will return the highest value
* in the ranges returned by {@link #getInputChannelCountRanges}
*/
- int getMaxInputChannelCount() const;
+ int32_t getMaxInputChannelCount() const;
/**
* Returns the minimum number of input channels supported.
@@ -77,7 +77,7 @@
* This returns the lowest channel count in the ranges returned by
* {@link #getInputChannelCountRanges}.
*/
- int getMinInputChannelCount() const;
+ int32_t getMinInputChannelCount() const;
/**
* Returns an array of ranges representing the number of input channels supported.
@@ -89,12 +89,12 @@
*
* The returned array cannot be empty.
*/
- const std::vector<Range<int>>& getInputChannelCountRanges() const;
+ const std::vector<Range<int32_t>>& getInputChannelCountRanges() const;
/**
* Query whether the sample rate is supported by the codec.
*/
- bool isSampleRateSupported(int sampleRate);
+ bool isSampleRateSupported(int32_t sampleRate);
/* For internal use only. Not exposed as a public API */
void getDefaultFormat(sp<AMessage> &format);
@@ -103,7 +103,7 @@
bool supportsFormat(const sp<AMessage> &format);
private:
- static constexpr int MAX_INPUT_CHANNEL_COUNT = 30;
+ static constexpr int32_t MAX_INPUT_CHANNEL_COUNT = 30;
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_LIMIT;
int mError;
@@ -112,21 +112,21 @@
Range<int32_t> mBitrateRange;
- std::vector<int> mSampleRates;
- std::vector<Range<int>> mSampleRateRanges;
- std::vector<Range<int>> mInputChannelRanges;
+ std::vector<int32_t> mSampleRates;
+ std::vector<Range<int32_t>> mSampleRateRanges;
+ std::vector<Range<int32_t>> mInputChannelRanges;
/* no public constructor */
AudioCapabilities() {}
void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
const sp<AMessage> &format);
void initWithPlatformLimits();
- bool supports(std::optional<int> sampleRate, std::optional<int> inputChannels);
- void limitSampleRates(std::vector<int> rates);
+ bool supports(std::optional<int32_t> sampleRate, std::optional<int32_t> inputChannels);
+ void limitSampleRates(std::vector<int32_t> rates);
void createDiscreteSampleRates();
- void limitSampleRates(std::vector<Range<int>> rateRanges);
+ void limitSampleRates(std::vector<Range<int32_t>> rateRanges);
void applyLevelLimits();
- void applyLimits(const std::vector<Range<int>> &inputChannels,
+ void applyLimits(const std::vector<Range<int32_t>> &inputChannels,
const std::optional<Range<int32_t>> &bitRates);
void parseFromInfo(const sp<AMessage> &format);
diff --git a/media/libmedia/include/media/CodecCapabilities.h b/media/libmedia/include/media/CodecCapabilities.h
index 570c8b5..700373a 100644
--- a/media/libmedia/include/media/CodecCapabilities.h
+++ b/media/libmedia/include/media/CodecCapabilities.h
@@ -19,6 +19,7 @@
#define CODEC_CAPABILITIES_H_
#include <media/AudioCapabilities.h>
+#include <media/VideoCapabilities.h>
#include <media/CodecCapabilitiesUtils.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -52,6 +53,7 @@
std::vector<ProfileLevel> mProfileLevels;
std::shared_ptr<AudioCapabilities> mAudioCaps;
+ std::shared_ptr<VideoCapabilities> mVideoCaps;
};
} // namespace android
diff --git a/media/libmedia/include/media/CodecCapabilitiesUtils.h b/media/libmedia/include/media/CodecCapabilitiesUtils.h
index 89a452c..7ca536a 100644
--- a/media/libmedia/include/media/CodecCapabilitiesUtils.h
+++ b/media/libmedia/include/media/CodecCapabilitiesUtils.h
@@ -19,8 +19,12 @@
#define CODEC_CAPABILITIES__UTILS_H_
#include <algorithm>
+#include <cerrno>
#include <cmath>
+#include <cstdlib>
+#include <numeric>
#include <optional>
+#include <regex>
#include <string>
#include <vector>
@@ -113,6 +117,74 @@
return Range(std::max(lower_, lower), std::min(upper_, upper));
}
+ /**
+ * Returns the smallest range that includes this range and
+ * another range.
+ *
+ * E.g. if a < b < c < d, the
+ * extension of [a, c] and [b, d] ranges is [a, d].
+ * As the endpoints are object references, there is no guarantee
+ * which specific endpoint reference is used from the input ranges:
+ *
+ * E.g. if a == a' < b < c, the
+ * extension of [a, b] and [a', c] ranges could be either
+ * [a, c] or ['a, c], where ['a, c] could be either the exact
+ * input range, or a newly created range with the same endpoints.
+ *
+ * @param range a non-null Range<T> reference
+ * @return the extension of this range and the other range.
+ */
+ Range<T> extend(Range<T> range) {
+ return Range<T>(std::min(lower_, range.lower_), std::max(upper_, range.upper_));
+ }
+
+ Range<T> align(T align) {
+ return this->intersect(
+ divUp(lower_, align) * align, (upper_ / align) * align);
+ }
+
+ Range<T> factor(T factor) {
+ if (factor == 1) {
+ return *this;
+ }
+ return Range(divUp(this->lower(), factor), this->upper() / factor);
+ }
+
+ // parse a string into a range
+ static std::optional<Range<T>> Parse(const std::string &str) {
+ if (str.empty()) {
+ ALOGW("could not parse empty integer range");
+ return std::nullopt;
+ }
+ long long lower, upper;
+ std::regex regex("^([0-9]+)-([0-9]+)$");
+ std::smatch match;
+ errno = 0;
+ if (std::regex_match(str, match, regex)) {
+ lower = std::strtoll(match[1].str().c_str(), NULL, 10);
+ upper = std::strtoll(match[2].str().c_str(), NULL, 10);
+ } else {
+ char *end;
+ lower = upper = std::strtoll(str.c_str(), &end, 10);
+ if (*end != '\0') {
+ ALOGW("could not parse integer range: %s", str.c_str());
+ return std::nullopt;
+ }
+ }
+
+ if (errno == ERANGE || lower < std::numeric_limits<T>::min()
+ || std::numeric_limits<T>::max() < upper || upper < lower) {
+ ALOGW("could not parse integer range: %s", str.c_str());
+ return std::nullopt;
+ }
+
+ return std::make_optional<Range<T>>((T)lower, (T)upper);
+ }
+
+ static Range<T> RangeFor(double v) {
+ return Range((T)v, (T)ceil(v));
+ }
+
private:
T lower_;
T upper_;
@@ -125,7 +197,7 @@
// found profile/level for which we don't have capability estimates
constexpr int ERROR_CAPABILITIES_UNSUPPORTED = (1 << 1);
// have not found any profile/level for which we don't have capability estimate
-// constexpr int ERROR_NONE_SUPPORTED = (1 << 2);
+constexpr int ERROR_CAPABILITIES_NONE_SUPPORTED = (1 << 2);
/**
* Sorts distinct (non-intersecting) range array in ascending order.
@@ -176,8 +248,464 @@
return result;
}
-// parse string into int range
-std::optional<Range<int>> ParseIntRange(const std::string &str);
+/**
+ * Immutable class for describing width and height dimensions in pixels.
+ */
+struct VideoSize {
+ /**
+ * Create a new immutable VideoSize instance.
+ *
+ * @param width The width of the size, in pixels
+ * @param height The height of the size, in pixels
+ */
+ VideoSize(int32_t width, int32_t height);
+
+ // default constructor
+ VideoSize();
+
+ /**
+ * Get the width of the size (in pixels).
+ * @return width
+ */
+ int32_t getWidth() const;
+
+ /**
+ * Get the height of the size (in pixels).
+ * @return height
+ */
+ int32_t getHeight() const;
+
+ /**
+ * Check if this size is equal to another size.
+ *
+ * Two sizes are equal if and only if both their widths and heights are
+ * equal.
+ *
+ * A size object is never equal to any other type of object.
+ *
+ * @return true if the objects were equal, false otherwise
+ */
+ bool equals(VideoSize other) const;
+
+ bool empty() const;
+
+ std::string toString() const;
+
+ /**
+ * Parses the specified string as a size value.
+ *
+ * The ASCII characters {@code \}{@code u002a} ('*') and
+ * {@code \}{@code u0078} ('x') are recognized as separators between
+ * the width and height.
+ *
+ * For any {@code VideoSize s}: {@code VideoSize::ParseSize(s.toString()).equals(s)}.
+ * However, the method also handles sizes expressed in the
+ * following forms:
+ *
+ * "<i>width</i>{@code x}<i>height</i>" or
+ * "<i>width</i>{@code *}<i>height</i>" {@code => new VideoSize(width, height)},
+ * where <i>width</i> and <i>height</i> are string integers potentially
+ * containing a sign, such as "-10", "+7" or "5".
+ *
+ * <pre>{@code
+ * VideoSize::ParseSize("3*+6").equals(new VideoSize(3, 6)) == true
+ * VideoSize::ParseSize("-3x-6").equals(new VideoSize(-3, -6)) == true
+ * VideoSize::ParseSize("4 by 3") => throws NumberFormatException
+ * }</pre>
+ *
+ * @param string the string representation of a size value.
+ * @return the size value represented by {@code string}.
+ */
+ static std::optional<VideoSize> ParseSize(std::string str);
+
+ static std::optional<std::pair<VideoSize, VideoSize>> ParseSizeRange(const std::string str);
+
+ static Range<int32_t> GetAllowedDimensionRange();
+
+private:
+ int32_t mWidth;
+ int32_t mHeight;
+};
+
+// This is used for the std::map<VideoSize> in VideoCapabilities
+struct VideoSizeCompare {
+ bool operator() (const VideoSize& lhs, const VideoSize& rhs) const {
+ if (lhs.getWidth() == rhs.getWidth()) {
+ return lhs.getHeight() < rhs.getHeight();
+ } else {
+ return lhs.getWidth() < rhs.getWidth();
+ }
+ }
+};
+
+/**
+ * An immutable data type representation a rational number.
+ *
+ * Contains a pair of ints representing the numerator and denominator of a
+ * Rational number.
+ */
+struct Rational {
+ /**
+ * <p>Create a {@code Rational} with a given numerator and denominator.</p>
+ *
+ * <p>The signs of the numerator and the denominator may be flipped such that the denominator
+ * is always positive. Both the numerator and denominator will be converted to their reduced
+ * forms (see {@link #equals} for more details).</p>
+ *
+ * <p>For example,
+ * <ul>
+ * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
+ * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
+ * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
+ * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
+ * </ul>
+ * </p>
+ *
+ * @param numerator the numerator of the rational
+ * @param denominator the denominator of the rational
+ *
+ * @see #equals
+ */
+ Rational(int32_t numerator, int32_t denominator) {
+ if (denominator < 0) {
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+
+ // Convert to reduced form
+ if (denominator == 0 && numerator > 0) {
+ mNumerator = 1; // +Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator < 0) {
+ mNumerator = -1; // -Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator == 0) {
+ mNumerator = 0; // NaN
+ mDenominator = 0;
+ } else if (numerator == 0) {
+ mNumerator = 0;
+ mDenominator = 1;
+ } else {
+ int gcd = std::gcd(numerator, denominator);
+
+ mNumerator = numerator / gcd;
+ mDenominator = denominator / gcd;
+ }
+ }
+
+ // default constructor;
+ Rational() {
+ Rational(0, 0);
+ }
+
+ /**
+ * Gets the numerator of the rational.
+ *
+ * <p>The numerator will always return {@code 1} if this rational represents
+ * infinity (that is, the denominator is {@code 0}).</p>
+ */
+ int32_t getNumerator() const {
+ return mNumerator;
+ }
+
+ /**
+ * Gets the denominator of the rational
+ *
+ * <p>The denominator may return {@code 0}, in which case the rational may represent
+ * positive infinity (if the numerator was positive), negative infinity (if the numerator
+ * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
+ *
+ * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
+ */
+ int32_t getDenominator() const {
+ return mDenominator;
+ }
+
+ /**
+ * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
+ *
+ * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
+ * {@code false} if this is a (potentially infinite) number value
+ */
+ bool isNaN() const {
+ return mDenominator == 0 && mNumerator == 0;
+ }
+
+ /**
+ * Indicates whether this rational represents an infinite value.
+ *
+ * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ bool isInfinite() const {
+ return mNumerator != 0 && mDenominator == 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a finite value.
+ *
+ * <p>A finite value occurs when the denominator is not {@code 0}; in other words
+ * the rational is neither infinity or {@code NaN}.</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ bool isFinite() const {
+ return mDenominator != 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a zero value.
+ *
+ * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is finite zero value;
+ * {@code false} otherwise
+ */
+ bool isZero() const {
+ return isFinite() && mNumerator == 0;
+ }
+
+ /**
+ * Return a string representation of this rational, e.g. {@code "1/2"}.
+ *
+ * <p>The following rules of conversion apply:
+ * <ul>
+ * <li>{@code NaN} values will return {@code "NaN"}
+ * <li>Positive infinity values will return {@code "Infinity"}
+ * <li>Negative infinity values will return {@code "-Infinity"}
+ * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
+ * and {@code denominator} are substituted with the appropriate numerator and denominator
+ * values.
+ * </ul></p>
+ */
+ std::string toString() const {
+ if (isNaN()) {
+ return "NaN";
+ } else if (isPosInf()) {
+ return "Infinity";
+ } else if (isNegInf()) {
+ return "-Infinity";
+ } else {
+ return std::to_string(mNumerator) + "/" + std::to_string(mDenominator);
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code double}.
+ *
+ * <p>The {@code double} is calculated by converting both the numerator and denominator
+ * to a {@code double}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code double}.
+ */
+ double asDouble() const {
+ double num = mNumerator;
+ double den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code float}.
+ *
+ * <p>The {@code float} is calculated by converting both the numerator and denominator
+ * to a {@code float}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code float}.
+ */
+ float asfloat() const {
+ float num = mNumerator;
+ float den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code int}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to an {@code int}, in particular:
+ *
+ * @return the divided value of the numerator and denominator as a {@code int}.
+ */
+ int32_t asInt32() const {
+ // Mimic float to int conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return INT32_MAX;
+ } else if (isNegInf()) {
+ return INT32_MIN;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code long}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to a {@code long}, in particular:
+ *
+ * @return the divided value of the numerator and denominator as a {@code long}.
+ */
+ int64_t asInt64() const {
+ // Mimic float to long conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return INT64_MAX;
+ } else if (isNegInf()) {
+ return INT64_MIN;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code short}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
+ * identically to {@link #intValue}; the {@code int} result is then truncated to a
+ * {@code short} before returning the value.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code short}.
+ */
+ int16_t asInt16() const {
+ return (int16_t) asInt32();
+ }
+
+ /**
+ * Compare this rational to the specified rational to determine their natural order.
+ *
+ * Nan is considered to be equal to itself and greater than all other
+ * Rational values. Otherwise, if the objects are not equal, then
+ * the following rules apply:
+ *
+ * Positive infinity is greater than any other finite number (or negative infinity)
+ * Negative infinity is less than any other finite number (or positive infinity)
+ * The finite number represented by this rational is checked numerically
+ * against the other finite number by converting both rationals to a common denominator multiple
+ * and comparing their numerators.
+ *
+ * @param another the rational to be compared
+ *
+ * @return a negative integer, zero, or a positive integer as this object is less than,
+ * equal to, or greater than the specified rational.
+ */
+ // bool operator> (const Rational& another) {
+ int compareTo(Rational another) const {
+ if (equals(another)) {
+ return 0;
+ } else if (isNaN()) { // NaN is greater than the other non-NaN value
+ return 1;
+ } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
+ return -1;
+ } else if (isPosInf() || another.isNegInf()) {
+ return 1; // positive infinity is greater than any non-NaN/non-posInf value
+ } else if (isNegInf() || another.isPosInf()) {
+ return -1; // negative infinity is less than any non-NaN/non-negInf value
+ }
+
+ // else both this and another are finite numbers
+
+ // make the denominators the same, then compare numerators. int64_t to avoid overflow
+ int64_t thisNumerator = ((int64_t)mNumerator) * another.mDenominator;
+ int64_t otherNumerator = ((int64_t)another.mNumerator) * mDenominator;
+
+ // avoid underflow from subtraction by doing comparisons
+ if (thisNumerator < otherNumerator) {
+ return -1;
+ } else if (thisNumerator > otherNumerator) {
+ return 1;
+ } else {
+ // This should be covered by #equals, but have this code path just in case
+ return 0;
+ }
+ }
+
+ bool operator > (const Rational& another) const {
+ return compareTo(another) > 0;
+ }
+
+ bool operator >= (const Rational& another) const {
+ return compareTo(another) >= 0;
+ }
+
+ bool operator < (const Rational& another) const {
+ return compareTo(another) < 0;
+ }
+
+ bool operator <= (const Rational& another) const {
+ return compareTo(another) <= 0;
+ }
+
+ bool operator == (const Rational& another) const {
+ return equals(another);
+ }
+
+ static std::optional<Range<Rational>> ParseRange(const std::string str);
+
+ static Range<Rational> ScaleRange(Range<Rational> range, int32_t num, int32_t den);
+
+private:
+ int32_t mNumerator;
+ int32_t mDenominator;
+
+ bool isPosInf() const {
+ return mDenominator == 0 && mNumerator > 0;
+ }
+
+ bool isNegInf() const {
+ return mDenominator == 0 && mNumerator < 0;
+ }
+
+ bool equals(Rational other) const {
+ return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
+ }
+
+ Rational scale(int32_t num, int32_t den);
+
+ /**
+ * Parses the specified string as a rational value.
+ * The ASCII characters {@code \}{@code u003a} (':') and
+ * {@code \}{@code u002f} ('/') are recognized as separators between
+ * the numerator and denominator.
+ *
+ * For any {@code Rational r}: {@code Rational::parseRational(r.toString()).equals(r)}.
+ * However, the method also handles rational numbers expressed in the
+ * following forms:
+ *
+ * "<i>num</i>{@code /}<i>den</i>" or
+ * "<i>num</i>{@code :}<i>den</i>" {@code => new Rational(num, den);},
+ * where <i>num</i> and <i>den</i> are string integers potentially
+ * containing a sign, such as "-10", "+7" or "5".
+ *
+ * Rational::Parse("3:+6").equals(new Rational(1, 2)) == true
+ * Rational::Parse("-3/-6").equals(new Rational(1, 2)) == true
+ * Rational::Parse("4.56") => return std::nullopt
+ *
+ * @param str the string representation of a rational value.
+ * @return the rational value wrapped by std::optional represented by str.
+ */
+ static std::optional<Rational> Parse(std::string str);
+};
+
+static const Rational NaN = Rational(0, 0);
+static const Rational POSITIVE_INFINITY = Rational(1, 0);
+static const Rational NEGATIVE_INFINITY = Rational(-1, 0);
+static const Rational ZERO = Rational(0, 1);
} // namespace android
diff --git a/media/libmedia/include/media/VideoCapabilities.h b/media/libmedia/include/media/VideoCapabilities.h
new file mode 100644
index 0000000..5671375
--- /dev/null
+++ b/media/libmedia/include/media/VideoCapabilities.h
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2024, 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 VIDEO_CAPABILITIES_H_
+
+#define VIDEO_CAPABILITIES_H_
+
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct VideoCapabilities {
+ struct PerformancePoint {
+ /**
+ * Maximum number of macroblocks in the frame.
+ *
+ * Video frames are conceptually divided into 16-by-16 pixel blocks called macroblocks.
+ * Most coding standards operate on these 16-by-16 pixel blocks; thus, codec performance
+ * is characterized using such blocks.
+ *
+ * Test API
+ */
+ int32_t getMaxMacroBlocks() const;
+
+ /**
+ * Return the width.
+ *
+ * For internal use only.
+ */
+ int32_t getWidth() const;
+
+ /**
+ * Return the height.
+ *
+ * For internal use only.
+ */
+ int32_t getHeight() const;
+
+ /**
+ * Maximum frame rate in frames per second.
+ *
+ * Test API
+ */
+ int32_t getMaxFrameRate() const;
+
+ /**
+ * Maximum number of macroblocks processed per second.
+ *
+ * Test API
+ */
+ int64_t getMaxMacroBlockRate() const;
+
+ /**
+ * Return the block size.
+ *
+ * For internal use only.
+ */
+ VideoSize getBlockSize() const;
+
+ /**
+ * convert to a debug string
+ *
+ * Be careful about the serializable compatibility across API revisions.
+ */
+ std::string toString() const;
+
+ /**
+ * Create a detailed performance point with custom max frame rate and macroblock size.
+ *
+ * @param width frame width in pixels
+ * @param height frame height in pixels
+ * @param frameRate frames per second for frame width and height
+ * @param maxFrameRate maximum frames per second for any frame size
+ * @param blockSize block size for codec implementation. Must be powers of two in both
+ * width and height.
+ *
+ * Test API
+ */
+ PerformancePoint(int32_t width, int32_t height, int32_t frameRate, int32_t maxFrameRate,
+ VideoSize blockSize);
+
+ /**
+ * Create a detailed performance point with custom max frame rate and macroblock size.
+ *
+ * @param width frame width in pixels
+ * @param height frame height in pixels
+ * @param maxFrameRate maximum frames per second for any frame size
+ * @param maxMacroBlockRate maximum number of macroblocks processed per second.
+ * @param blockSize block size for codec implementation. Must be powers of two in both
+ * width and height.
+ *
+ * Test API
+ */
+ PerformancePoint(VideoSize blockSize, int32_t width, int32_t height, int maxFrameRate,
+ int64_t maxMacroBlockRate);
+
+ /**
+ * Convert a performance point to a larger blocksize.
+ *
+ * @param pp performance point. NonNull
+ * @param blockSize block size for codec implementation. NonNull.
+ *
+ * Test API
+ */
+ PerformancePoint(const PerformancePoint &pp, VideoSize newBlockSize);
+
+ /**
+ * Create a performance point for a given frame size and frame rate.
+ *
+ * @param width width of the frame in pixels
+ * @param height height of the frame in pixels
+ * @param frameRate frame rate in frames per second
+ */
+ PerformancePoint(int32_t width, int32_t height, int32_t frameRate);
+
+ /**
+ * Checks whether the performance point covers a media format.
+ *
+ * @param format Stream format considered
+ *
+ * @return {@code true} if the performance point covers the format.
+ */
+ bool covers(const sp<AMessage> &format) const;
+
+ /**
+ * Checks whether the performance point covers another performance point. Use this
+ * method to determine if a performance point advertised by a codec covers the
+ * performance point required. This method can also be used for loose ordering as this
+ * method is transitive.
+ *
+ * @param other other performance point considered
+ *
+ * @return {@code true} if the performance point covers the other.
+ */
+ bool covers(const PerformancePoint &other) const;
+
+ /**
+ * Check if two PerformancePoint instances are equal.
+ *
+ * @param other other PerformancePoint instance for comparison.
+ *
+ * @return true if two PerformancePoint are equal.
+ */
+ bool equals(const PerformancePoint &other) const;
+
+ private:
+ VideoSize mBlockSize; // codec block size in macroblocks
+ int32_t mWidth; // width in macroblocks
+ int32_t mHeight; // height in macroblocks
+ int32_t mMaxFrameRate; // max frames per second
+ int64_t mMaxMacroBlockRate; // max macro block rate
+
+ void init(int32_t width, int32_t height, int32_t frameRate, int32_t maxFrameRate,
+ VideoSize blockSize);
+
+ /** Saturates a 64 bit integer value to a 32 bit integer */
+ int32_t saturateInt64ToInt32(int64_t value) const;
+
+ /** This method may overflow */
+ int32_t align(int32_t value, int32_t alignment) const;
+
+ /** @return NonNull */
+ VideoSize getCommonBlockSize(const PerformancePoint &other) const;
+ };
+
+ /**
+ * Find the equivalent VP9 profile level.
+ *
+ * Not a public API to developers.
+ */
+ static int32_t EquivalentVP9Level(const sp<AMessage> &format);
+
+ /**
+ * Returns the range of supported bitrates in bits/second.
+ */
+ const Range<int32_t>& getBitrateRange() const;
+
+ /**
+ * Returns the range of supported video widths.
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space.
+ */
+ const Range<int32_t>& getSupportedWidths() const;
+
+ /**
+ * Returns the range of supported video heights.
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space.
+ */
+ const Range<int32_t>& getSupportedHeights() const;
+
+ /**
+ * Returns the alignment requirement for video width (in pixels).
+ *
+ * This is a power-of-2 value that video width must be a
+ * multiple of.
+ */
+ int32_t getWidthAlignment() const;
+
+ /**
+ * Returns the alignment requirement for video height (in pixels).
+ *
+ * This is a power-of-2 value that video height must be a
+ * multiple of.
+ */
+ int32_t getHeightAlignment() const;
+
+ /**
+ * Return the upper limit on the smaller dimension of width or height.
+ *
+ * Some codecs have a limit on the smaller dimension, whether it be
+ * the width or the height. E.g. a codec may only be able to handle
+ * up to 1920x1080 both in landscape and portrait mode (1080x1920).
+ * In this case the maximum width and height are both 1920, but the
+ * smaller dimension limit will be 1080. For other codecs, this is
+ * {@code Math.min(getSupportedWidths().getUpper(),
+ * getSupportedHeights().getUpper())}.
+ */
+ int32_t getSmallerDimensionUpperLimit() const;
+
+ /**
+ * Returns the range of supported frame rates.
+ *
+ * This is not a performance indicator. Rather, it expresses the
+ * limits specified in the coding standard, based on the complexities
+ * of encoding material for later playback at a certain frame rate,
+ * or the decoding of such material in non-realtime.
+ */
+ const Range<int32_t>& getSupportedFrameRates() const;
+
+ /**
+ * Returns the range of supported video widths for a video height.
+ * @param height the height of the video
+ */
+ std::optional<Range<int32_t>> getSupportedWidthsFor(int32_t height) const;
+
+ /**
+ * Returns the range of supported video heights for a video width
+ * @param width the width of the video
+ */
+ std::optional<Range<int32_t>> getSupportedHeightsFor(int32_t width) const;
+
+ /**
+ * Returns the range of supported video frame rates for a video size.
+ *
+ * This is not a performance indicator. Rather, it expresses the limits specified in
+ * the coding standard, based on the complexities of encoding material of a given
+ * size for later playback at a certain frame rate, or the decoding of such material
+ * in non-realtime.
+
+ * @param width the width of the video
+ * @param height the height of the video
+ */
+ std::optional<Range<double>> getSupportedFrameRatesFor(int32_t width, int32_t height) const;
+
+ /**
+ * Returns the range of achievable video frame rates for a video size.
+ * May return {@code null}, if the codec did not publish any measurement
+ * data.
+ * <p>
+ * This is a performance estimate provided by the device manufacturer based on statistical
+ * sampling of full-speed decoding and encoding measurements in various configurations
+ * of common video sizes supported by the codec. As such it should only be used to
+ * compare individual codecs on the device. The value is not suitable for comparing
+ * different devices or even different android releases for the same device.
+ * <p>
+ * <em>On {@link android.os.Build.VERSION_CODES#M} release</em> the returned range
+ * corresponds to the fastest frame rates achieved in the tested configurations. As
+ * such, it should not be used to gauge guaranteed or even average codec performance
+ * on the device.
+ * <p>
+ * <em>On {@link android.os.Build.VERSION_CODES#N} release</em> the returned range
+ * corresponds closer to sustained performance <em>in tested configurations</em>.
+ * One can expect to achieve sustained performance higher than the lower limit more than
+ * 50% of the time, and higher than half of the lower limit at least 90% of the time
+ * <em>in tested configurations</em>.
+ * Conversely, one can expect performance lower than twice the upper limit at least
+ * 90% of the time.
+ * <p class=note>
+ * Tested configurations use a single active codec. For use cases where multiple
+ * codecs are active, applications can expect lower and in most cases significantly lower
+ * performance.
+ * <p class=note>
+ * The returned range value is interpolated from the nearest frame size(s) tested.
+ * Codec performance is severely impacted by other activity on the device as well
+ * as environmental factors (such as battery level, temperature or power source), and can
+ * vary significantly even in a steady environment.
+ * <p class=note>
+ * Use this method in cases where only codec performance matters, e.g. to evaluate if
+ * a codec has any chance of meeting a performance target. Codecs are listed
+ * in {@link MediaCodecList} in the preferred order as defined by the device
+ * manufacturer. As such, applications should use the first suitable codec in the
+ * list to achieve the best balance between power use and performance.
+ *
+ * @param width the width of the video
+ * @param height the height of the video
+ */
+ std::optional<Range<double>> getAchievableFrameRatesFor(int32_t width, int32_t height) const;
+
+ /**
+ * Returns the supported performance points. May return {@code null} if the codec did not
+ * publish any performance point information (e.g. the vendor codecs have not been updated
+ * to the latest android release). May return an empty list if the codec published that
+ * if does not guarantee any performance points.
+ * <p>
+ * This is a performance guarantee provided by the device manufacturer for hardware codecs
+ * based on hardware capabilities of the device.
+ * <p>
+ * The returned list is sorted first by decreasing number of pixels, then by decreasing
+ * width, and finally by decreasing frame rate.
+ * Performance points assume a single active codec. For use cases where multiple
+ * codecs are active, should use that highest pixel count, and add the frame rates of
+ * each individual codec.
+ * <p class=note>
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space, but performance points will be presented as is.
+ * In other words, even though a component publishes a performance point for
+ * a resolution higher than 4096x4096, it does not mean that the resolution is supported
+ * for 32-bit processes.
+ */
+ const std::vector<PerformancePoint>& getSupportedPerformancePoints() const;
+
+ /**
+ * Returns whether a given video size ({@code width} and
+ * {@code height}) and {@code frameRate} combination is supported.
+ */
+ bool areSizeAndRateSupported(int32_t width, int32_t height, double frameRate) const;
+
+ /**
+ * Returns whether a given video size ({@code width} and
+ * {@code height}) is supported.
+ */
+ bool isSizeSupported(int32_t width, int32_t height) const;
+
+ /**
+ * Returns if a media format is supported.
+ *
+ * Not exposed to public
+ */
+ bool supportsFormat(const sp<AMessage> &format) const;
+
+ /**
+ * Create VideoCapabilities.
+ */
+ static std::shared_ptr<VideoCapabilities> Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format);
+
+ /**
+ * Get the block size.
+ *
+ * Not a public API to developers
+ */
+ VideoSize getBlockSize() const;
+
+ /**
+ * Get the block count range.
+ *
+ * Not a public API to developers
+ */
+ const Range<int32_t>& getBlockCountRange() const;
+
+ /**
+ * Get the blocks per second range.
+ *
+ * Not a public API to developers
+ */
+ const Range<int64_t>& getBlocksPerSecondRange() const;
+
+ /**
+ * Get the aspect ratio range.
+ *
+ * Not a public API to developers
+ */
+ Range<Rational> getAspectRatioRange(bool blocks) const;
+
+private:
+ std::string mMediaType;
+ std::vector<ProfileLevel> mProfileLevels;
+ int mError;
+
+ Range<int32_t> mBitrateRange;
+ Range<int32_t> mHeightRange;
+ Range<int32_t> mWidthRange;
+ Range<int32_t> mBlockCountRange;
+ Range<int32_t> mHorizontalBlockRange;
+ Range<int32_t> mVerticalBlockRange;
+ Range<Rational> mAspectRatioRange;
+ Range<Rational> mBlockAspectRatioRange;
+ Range<int64_t> mBlocksPerSecondRange;
+ std::map<VideoSize, Range<int64_t>, VideoSizeCompare> mMeasuredFrameRates;
+ std::vector<PerformancePoint> mPerformancePoints;
+ Range<int32_t> mFrameRateRange;
+
+ int32_t mBlockWidth;
+ int32_t mBlockHeight;
+ int32_t mWidthAlignment;
+ int32_t mHeightAlignment;
+ int mSmallerDimensionUpperLimit;
+
+ bool mAllowMbOverride; // allow XML to override calculated limits
+
+ int32_t getBlockCount(int32_t width, int32_t height) const;
+ std::optional<VideoSize> findClosestSize(int32_t width, int32_t height) const;
+ std::optional<Range<double>> estimateFrameRatesFor(int32_t width, int32_t height) const;
+ bool supports(std::optional<int32_t> width, std::optional<int32_t> height,
+ std::optional<double> rate) const;
+ /* no public constructor */
+ VideoCapabilities() {}
+ void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format);
+ void initWithPlatformLimits();
+ std::vector<PerformancePoint> getPerformancePoints(const sp<AMessage> &format) const;
+ std::map<VideoSize, Range<int64_t>, VideoSizeCompare>
+ getMeasuredFrameRates(const sp<AMessage> &format) const;
+
+ static std::optional<std::pair<Range<int32_t>, Range<int32_t>>> ParseWidthHeightRanges(
+ const std::string &str);
+ void parseFromInfo(const sp<AMessage> &format);
+ void applyBlockLimits(int32_t blockWidth, int32_t blockHeight,
+ Range<int32_t> counts, Range<int64_t> rates, Range<Rational> ratios);
+ void applyAlignment(int32_t widthAlignment, int32_t heightAlignment);
+ void updateLimits();
+ void applyMacroBlockLimits(
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment);
+ void applyMacroBlockLimits(
+ int32_t minHorizontalBlocks, int32_t minVerticalBlocks,
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment);
+ void applyLevelLimits();
+
+ friend struct CodecCapabilities;
+};
+
+} // namespace android
+
+#endif // VIDEO_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
index 02e43a4..c1bd65a 100644
--- a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
+++ b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
@@ -72,15 +72,15 @@
}
TEST_F(AudioCapsAacTest, AudioCaps_Aac_InputChannelCount) {
- int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ int32_t maxInputChannelCount = audioCaps->getMaxInputChannelCount();
EXPECT_EQ(maxInputChannelCount, 8);
- int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ int32_t minInputChannelCount = audioCaps->getMinInputChannelCount();
EXPECT_EQ(minInputChannelCount, 1);
}
TEST_F(AudioCapsAacTest, AudioCaps_Aac_SupportedSampleRates) {
- const std::vector<int>& sampleRates = audioCaps->getSupportedSampleRates();
- EXPECT_EQ(sampleRates, std::vector<int>({7350, 8000, 11025, 12000, 16000, 22050,
+ const std::vector<int32_t>& sampleRates = audioCaps->getSupportedSampleRates();
+ EXPECT_EQ(sampleRates, std::vector<int32_t>({7350, 8000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000}));
EXPECT_FALSE(audioCaps->isSampleRateSupported(6000))
@@ -120,16 +120,16 @@
}
TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCount) {
- int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ int32_t maxInputChannelCount = audioCaps->getMaxInputChannelCount();
EXPECT_EQ(maxInputChannelCount, 12);
- int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ int32_t minInputChannelCount = audioCaps->getMinInputChannelCount();
EXPECT_EQ(minInputChannelCount, 1);
}
TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCountRanges) {
- const std::vector<Range<int>>& inputChannelCountRanges
+ const std::vector<Range<int32_t>>& inputChannelCountRanges
= audioCaps->getInputChannelCountRanges();
- std::vector<Range<int>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
+ std::vector<Range<int32_t>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
{6,6}, {7,7}, {8,8}, {9,9}, {10,10}, {11,11}, {12,12}});
ASSERT_EQ(inputChannelCountRanges.size(), expectedOutput.size());
for (int i = 0; i < inputChannelCountRanges.size(); i++) {
@@ -139,7 +139,7 @@
}
TEST_F(AudioCapsRawTest, AudioCaps_Raw_SupportedSampleRates) {
- const std::vector<Range<int>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
+ const std::vector<Range<int32_t>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
EXPECT_EQ(sampleRateRanges.size(), 1);
EXPECT_EQ(sampleRateRanges.at(0).lower(), 8000);
EXPECT_EQ(sampleRateRanges.at(0).upper(), 192000);
@@ -148,3 +148,82 @@
EXPECT_EQ(audioCaps->isSampleRateSupported(10000), true);
EXPECT_EQ(audioCaps->isSampleRateSupported(193000), false);
}
+
+class VideoCapsHevcTest : public testing::Test {
+protected:
+ VideoCapsHevcTest() {
+ std::string mediaType = MIMETYPE_VIDEO_HEVC;
+
+ sp<AMessage> details = new AMessage;
+ details->setString("alignment", "2x2");
+ details->setString("bitrate-range", "1-120000000");
+ details->setString("block-count-range", "1-32640");
+ details->setString("block-size", "16x16");
+ details->setString("blocks-per-second-range", "1-3916800");
+ details->setInt32("feature-adaptive-playback", 0);
+ details->setInt32("feature-can-swap-width-height", 1);
+ details->setString("max-concurrent-instances", "16");
+ details->setString("measured-frame-rate-1280x720-range", "547-553");
+ details->setString("measured-frame-rate-1920x1080-range", "569-572");
+ details->setString("measured-frame-rate-352x288-range", "1150-1250");
+ details->setString("measured-frame-rate-3840x2160-range", "159-159");
+ details->setString("measured-frame-rate-640x360-range", "528-529");
+ details->setString("measured-frame-rate-720x480-range", "546-548");
+ details->setString("performance-point-1280x720-range", "240");
+ details->setString("performance-point-3840x2160-range", "120");
+ details->setString("size-range", "64x64-3840x2176");
+
+ std::vector<ProfileLevel> profileLevel{
+ ProfileLevel(1, 8388608),
+ ProfileLevel(2, 8388608),
+ ProfileLevel(4096, 8388608),
+ ProfileLevel(8192, 8388608),
+ };
+
+ videoCaps = VideoCapabilities::Create(mediaType, profileLevel, details);
+ }
+
+ std::shared_ptr<VideoCapabilities> videoCaps;
+};
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_Alignment) {
+ int32_t widthAlignment = videoCaps->getWidthAlignment();
+ EXPECT_EQ(widthAlignment, 2);
+ int32_t heightAlignment = videoCaps->getHeightAlignment();
+ EXPECT_EQ(heightAlignment, 2);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_BitrateRange) {
+ const Range<int32_t>& bitrateRange = videoCaps->getBitrateRange();
+ EXPECT_EQ(bitrateRange.lower(), 1);
+ EXPECT_EQ(bitrateRange.upper(), 120000000);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_SupportedWidthsAndHeights) {
+ const Range<int32_t>& supportedWidths = videoCaps->getSupportedWidths();
+ EXPECT_EQ(supportedWidths.upper(), 3840);
+ const Range<int32_t>& supportedHeights = videoCaps->getSupportedHeights();
+ EXPECT_EQ(supportedHeights.upper(), 3840);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_SupportedFrameRates) {
+ const Range<int32_t>& supportedFrameRates = videoCaps->getSupportedFrameRates();
+ EXPECT_EQ(supportedFrameRates.lower(), 0);
+ EXPECT_EQ(supportedFrameRates.upper(), 960);
+
+ std::optional<Range<double>> supportedFR720p = videoCaps->getSupportedFrameRatesFor(1280, 720);
+ EXPECT_EQ(supportedFR720p.value().upper(), 960.0);
+ std::optional<Range<double>> supportedFR1080p
+ = videoCaps->getSupportedFrameRatesFor(1920, 1080);
+ EXPECT_EQ(supportedFR1080p.value().upper(), 480.0);
+ std::optional<Range<double>> supportedFR4k = videoCaps->getSupportedFrameRatesFor(3840, 2160);
+ EXPECT_EQ(std::round(supportedFR4k.value().upper()), 121);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_AchievableFrameRates) {
+ std::optional<Range<double>> achievableFR1080p
+ = videoCaps->getAchievableFrameRatesFor(1920, 1080);
+ ASSERT_NE(achievableFR1080p, std::nullopt) << "resolution not supported";
+ EXPECT_EQ(achievableFR1080p.value().lower(), 569);
+ EXPECT_EQ(achievableFR1080p.value().upper(), 572);
+}
diff --git a/media/libstagefright/FrameCaptureLayer.cpp b/media/libstagefright/FrameCaptureLayer.cpp
index 4e71943..53e4d7d 100644
--- a/media/libstagefright/FrameCaptureLayer.cpp
+++ b/media/libstagefright/FrameCaptureLayer.cpp
@@ -242,8 +242,7 @@
ALOGV("releaseBuffer");
Mutex::Autolock _lock(mLock);
- return mConsumer->releaseBuffer(bi.mSlot, bi.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, bi.mFence);
+ return mConsumer->releaseBuffer(bi.mSlot, bi.mFrameNumber, bi.mFence);
}
} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index efbd682..eb7edaf 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -755,8 +755,7 @@
// consume buffer
sp<IGraphicBufferConsumer> consumer = mConsumer.promote();
if (consumer != nullptr && consumer->acquireBuffer(&buffer, 0) == NO_ERROR) {
- consumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, buffer.mFence);
+ consumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, buffer.mFence);
}
}
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index a3f55da..b640040 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -752,8 +752,7 @@
status_t status = mInput->attachBuffer(&consumerSlot, oldBuffer);
ALOGE_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status);
if (status == NO_ERROR) {
- status = mInput->releaseBuffer(consumerSlot, 0 /* frameNumber */,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence);
+ status = mInput->releaseBuffer(consumerSlot, 0 /* frameNumber */, fence);
ALOGE_IF(status != NO_ERROR, "releasing buffer to input failed (%d)", status);
}
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index 4601831..20c97dc 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -261,6 +261,7 @@
<Limit name="bitrate" range="1-240000000"/>
<Limit name="block-size" value="16x16" />
<Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+ <Feature name="adaptive-playback" />
<Attribute name="software-codec"/>
</MediaCodec>
</Decoders>
diff --git a/media/module/bqhelper/GraphicBufferSource.cpp b/media/module/bqhelper/GraphicBufferSource.cpp
index 82ddbc0..c9082f2 100644
--- a/media/module/bqhelper/GraphicBufferSource.cpp
+++ b/media/module/bqhelper/GraphicBufferSource.cpp
@@ -996,9 +996,8 @@
// somehow need to propagate frame number to that queue
if (buffer->isCached()) {
--mNumOutstandingAcquires;
- mConsumer->releaseBuffer(
- buffer->getSlot(), frameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
- buffer->getReleaseFence());
+ mConsumer->releaseBuffer(buffer->getSlot(), frameNum,
+ buffer->getReleaseFence());
}
},
bi.mFence);
diff --git a/media/module/foundation/include/media/stagefright/foundation/AUtils.h b/media/module/foundation/include/media/stagefright/foundation/AUtils.h
index 3b646dc..eb605a7 100644
--- a/media/module/foundation/include/media/stagefright/foundation/AUtils.h
+++ b/media/module/foundation/include/media/stagefright/foundation/AUtils.h
@@ -92,4 +92,13 @@
return (err < (period / 2)) ? err : (period - err);
}
+inline static bool IsPowerOfTwo(int32_t value) {
+ return (value & (value - 1)) == 0;
+}
+
+/** Checks if the value is a power of two and not zero. */
+inline static bool IsPowerOfTwoStrict(int32_t value) {
+ return value != 0 && (value & (value - 1)) == 0;
+}
+
#endif // A_UTILS_H_
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index ddb93fe..39a172f 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -77,15 +77,24 @@
return packages[0];
}
+// NOTE/TODO(b/379754682):
+// AUDIO_SOURCE_VOICE_DOWNLINK and AUDIO_SOURCE_VOICE_CALL are handled specially:
+// DOWNLINK is an output source, but we still require RecordOp in addition to
+// OP_RECORD_INCOMING_PHONE_AUDIO
+// CALL includes both uplink and downlink, but we attribute RECORD_OP (only), since
+// there is not support for noting multiple ops.
int32_t getOpForSource(audio_source_t source) {
switch (source) {
+ // BEGIN output sources
case AUDIO_SOURCE_FM_TUNER:
return AppOpsManager::OP_NONE;
case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough
case AUDIO_SOURCE_REMOTE_SUBMIX:
+ // TODO -- valid in all cases?
return AppOpsManager::OP_RECORD_AUDIO_OUTPUT;
case AUDIO_SOURCE_VOICE_DOWNLINK:
return AppOpsManager::OP_RECORD_INCOMING_PHONE_AUDIO;
+ // END output sources
case AUDIO_SOURCE_HOTWORD:
return AppOpsManager::OP_RECORD_AUDIO_HOTWORD;
case AUDIO_SOURCE_DEFAULT:
@@ -99,6 +108,7 @@
case AUDIO_SOURCE_FM_TUNER:
case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough
case AUDIO_SOURCE_REMOTE_SUBMIX:
+ // case AUDIO_SOURCE_VOICE_DOWNLINK:
return false;
default:
return true;
@@ -291,6 +301,21 @@
return ok;
}
+bool bypassConcurrentPolicyAllowed(const AttributionSourceState& attributionSource) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
+ uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
+ if (isAudioServerOrRootUid(uid)) return true;
+ static const String16 sBypassConcurrentPolicy(
+ "android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION ");
+ // Use PermissionChecker, which includes some logic for allowing the isolated
+ // HotwordDetectionService to hold certain permissions.
+ bool ok = PermissionCache::checkPermission(sBypassConcurrentPolicy, pid, uid);
+ if (!ok) {
+ ALOGV("Request requires android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION");
+ }
+ return ok;
+}
+
bool accessUltrasoundAllowed(const AttributionSourceState& attributionSource) {
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index 9abdbf1..573cc14 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -102,6 +102,7 @@
bool captureMediaOutputAllowed(const AttributionSourceState& attributionSource);
bool captureTunerAudioInputAllowed(const AttributionSourceState& attributionSource);
bool captureVoiceCommunicationOutputAllowed(const AttributionSourceState& attributionSource);
+bool bypassConcurrentPolicyAllowed(const AttributionSourceState& attributionSource) ;
bool accessUltrasoundAllowed(const AttributionSourceState& attributionSource);
bool captureHotwordAllowed(const AttributionSourceState& attributionSource);
bool settingsAllowed();
diff --git a/services/audioparameterparser/Android.bp b/services/audioparameterparser/Android.bp
index 1c1c1e1..0b2c1ba 100644
--- a/services/audioparameterparser/Android.bp
+++ b/services/audioparameterparser/Android.bp
@@ -35,10 +35,10 @@
name: "android.hardware.audio.parameter_parser.example_defaults",
defaults: [
"latest_android_hardware_audio_core_ndk_shared",
- "latest_av_audio_types_aidl_ndk_shared",
],
shared_libs: [
+ "av-audio-types-aidl-ndk",
"libbase",
"libbinder_ndk",
],
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 04ebb57..a52ec64 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2350,7 +2350,7 @@
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputForClient(portId);
if (outputDesc == 0) {
ALOGW("startOutput() no output for client %d", portId);
- return BAD_VALUE;
+ return DEAD_OBJECT;
}
sp<TrackClientDescriptor> client = outputDesc->getClient(portId);
@@ -2727,7 +2727,7 @@
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputForClient(portId);
if (outputDesc == 0) {
ALOGW("stopOutput() no output for client %d", portId);
- return BAD_VALUE;
+ return DEAD_OBJECT;
}
sp<TrackClientDescriptor> client = outputDesc->getClient(portId);
@@ -3430,7 +3430,7 @@
sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
if (inputDesc == 0) {
ALOGW("%s no input for client %d", __FUNCTION__, portId);
- return BAD_VALUE;
+ return DEAD_OBJECT;
}
audio_io_handle_t input = inputDesc->mIoHandle;
sp<RecordClientDescriptor> client = inputDesc->getClient(portId);
@@ -3573,6 +3573,7 @@
ALOGI("%s: deviceType 0x%X, enabled %d, streamToDriveAbs %d", __func__, deviceType, enabled,
streamToDriveAbs);
+ bool changed = false;
audio_attributes_t attributesToDriveAbs = mEngine->getAttributesForStreamType(streamToDriveAbs);
if (enabled) {
if (attributesToDriveAbs == AUDIO_ATTRIBUTES_INITIALIZER) {
@@ -3581,16 +3582,25 @@
return BAD_VALUE;
}
- mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
+ if (mAbsoluteVolumeDrivingStreams[deviceType] != attributesToDriveAbs) {
+ mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
+ changed = true;
+ }
} else {
- mAbsoluteVolumeDrivingStreams.erase(deviceType);
+ if (mAbsoluteVolumeDrivingStreams.find(deviceType) != mAbsoluteVolumeDrivingStreams.end()) {
+ mAbsoluteVolumeDrivingStreams.erase(deviceType);
+ changed = true;
+ }
}
- // apply the stream volumes regarding the new absolute mode to all the outputs
- for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
- ALOGV("%s: apply stream volumes for portId %d", __func__, desc->getId());
- applyStreamVolumes(desc, {deviceType}, static_cast<int>(desc->latency()) * 2);
+ // if something changed, apply the stream volumes regarding the new absolute mode to all the
+ // outputs without any delay
+ if (changed) {
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ ALOGV("%s: apply stream volumes for portId %d", __func__, desc->getId());
+ applyStreamVolumes(desc, {deviceType});
+ }
}
return NO_ERROR;
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index e157808..619c924 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -16,6 +16,7 @@
],
shared_libs: [
+ "android.media.audio-aconfig-cc",
"android.media.audiopolicy-aconfig-cc",
"audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 3589de1..20bc788 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -23,6 +23,8 @@
#include <android/content/AttributionSourceState.h>
#include <android_media_audiopolicy.h>
+#include <android_media_audio.h>
+#include <binder/Enums.h>
#include <com_android_media_audio.h>
#include <cutils/properties.h>
#include <error/expected_utils.h>
@@ -50,14 +52,18 @@
#define CHECK_PERM(expr1, expr2) \
VALUE_OR_RETURN_STATUS(getPermissionProvider().checkPermission((expr1), (expr2)))
+#define PROPAGATE_FALSEY(val) do { if (!val.has_value() || !val.value()) return val; } while (0)
+
#define MAX_ITEMS_PER_LIST 1024
namespace android {
namespace audiopolicy_flags = android::media::audiopolicy;
using binder::Status;
using aidl_utils::binderStatusFromStatusT;
+using android::media::audio::concurrent_audio_record_bypass_permission;
using com::android::media::audio::audioserver_permissions;
using com::android::media::permission::NativePermissionController;
+using com::android::media::permission::PermissionEnum;
using com::android::media::permission::PermissionEnum::ACCESS_ULTRASOUND;
using com::android::media::permission::PermissionEnum::CALL_AUDIO_INTERCEPTION;
using com::android::media::permission::PermissionEnum::CAPTURE_AUDIO_HOTWORD;
@@ -71,6 +77,7 @@
using com::android::media::permission::PermissionEnum::MODIFY_PHONE_STATE;
using com::android::media::permission::PermissionEnum::RECORD_AUDIO;
using com::android::media::permission::PermissionEnum::WRITE_SECURE_SETTINGS;
+using com::android::media::permission::PermissionEnum::BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION;
using content::AttributionSourceState;
using media::audio::common::AudioConfig;
using media::audio::common::AudioConfigBase;
@@ -658,6 +665,146 @@
mAudioPolicyManager->releaseOutput(portId);
}
+// These are sources for which CAPTURE_AUDIO_OUTPUT granted access
+// for legacy reasons, before more specific permissions were deployed.
+// TODO: remove this access
+static bool isLegacyOutputSource(AudioSource source) {
+ switch (source) {
+ case AudioSource::VOICE_CALL:
+ case AudioSource::VOICE_DOWNLINK:
+ case AudioSource::VOICE_UPLINK:
+ case AudioSource::FM_TUNER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource(
+ const AttributionSourceState& attrSource, AudioSource source, bool isHotword) {
+ error::BinderResult<bool> permRes = true;
+ const auto check_perm = [&](PermissionEnum perm, uid_t uid) {
+ return getPermissionProvider().checkPermission(perm, uid);
+ };
+ switch (source) {
+ case AudioSource::VOICE_UPLINK:
+ case AudioSource::VOICE_DOWNLINK:
+ case AudioSource::VOICE_CALL:
+ permRes = audioserver_permissions()
+ ? check_perm(CALL_AUDIO_INTERCEPTION, attrSource.uid)
+ : callAudioInterceptionAllowed(attrSource);
+ break;
+ case AudioSource::ECHO_REFERENCE:
+ permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid)
+ : captureAudioOutputAllowed(attrSource);
+ break;
+ case AudioSource::FM_TUNER:
+ permRes = audioserver_permissions()
+ ? check_perm(CAPTURE_TUNER_AUDIO_INPUT, attrSource.uid)
+ : captureTunerAudioInputAllowed(attrSource);
+ break;
+ case AudioSource::HOTWORD:
+ permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_HOTWORD, attrSource.uid)
+ : captureHotwordAllowed(attrSource);
+ break;
+ case AudioSource::ULTRASOUND:
+ permRes = audioserver_permissions() ? check_perm(ACCESS_ULTRASOUND, attrSource.uid)
+ : accessUltrasoundAllowed(attrSource);
+ break;
+ case AudioSource::SYS_RESERVED_INVALID:
+ case AudioSource::DEFAULT:
+ case AudioSource::MIC:
+ case AudioSource::CAMCORDER:
+ case AudioSource::VOICE_RECOGNITION:
+ case AudioSource::VOICE_COMMUNICATION:
+ case AudioSource::UNPROCESSED:
+ case AudioSource::VOICE_PERFORMANCE:
+ // No additional check intended
+ case AudioSource::REMOTE_SUBMIX:
+ // special-case checked based on device (evaluatePermsForDevice)
+ break;
+ }
+
+ bool isAllowed = VALUE_OR_RETURN(permRes);
+
+ if (!isAllowed) {
+ if (isLegacyOutputSource(source)) {
+ permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid)
+ : captureAudioOutputAllowed(attrSource);
+ PROPAGATE_FALSEY(permRes);
+ } else {
+ return false;
+ }
+ }
+
+ if (isHotword) {
+ permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_HOTWORD, attrSource.uid)
+ : captureHotwordAllowed(attrSource);
+ PROPAGATE_FALSEY(permRes);
+ }
+
+ // All sources which aren't output capture require RECORD as well,
+ // as well as vdi policy mix
+ const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value();
+ if (isRecordOpRequired(legacySource)) {
+ permRes = audioserver_permissions() ? check_perm(RECORD_AUDIO, attrSource.uid)
+ : recordingAllowed(attrSource, legacySource);
+ PROPAGATE_FALSEY(permRes);
+ }
+ return true;
+}
+
+error::BinderResult<bool> AudioPolicyService::evaluatePermsForDevice(
+ const AttributionSourceState& attrSource, AudioSource source,
+ AudioPolicyInterface::input_type_t inputType, uint32_t vdi, bool isCallRedir) {
+ // enforce permission (if any) required for each type of input
+ error::BinderResult<bool> permRes = true;
+ const auto check_perm = [&](PermissionEnum perm, uid_t uid) {
+ return getPermissionProvider().checkPermission(perm, uid);
+ };
+ bool isAllowedDueToCallPerm = false;
+ if (isCallRedir) {
+ const auto checkCall = audioserver_permissions()
+ ? check_perm(CALL_AUDIO_INTERCEPTION, attrSource.uid)
+ : callAudioInterceptionAllowed(attrSource);
+ isAllowedDueToCallPerm = VALUE_OR_RETURN(checkCall);
+ }
+ switch (inputType) {
+ case AudioPolicyInterface::API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK:
+ // this use case has been validated in audio service with a MediaProjection token,
+ // and doesn't rely on regular permissions
+ case AudioPolicyInterface::API_INPUT_LEGACY:
+ break;
+ case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
+ if (isAllowedDueToCallPerm) break;
+ // FIXME: use the same permission as for remote submix for now.
+ FALLTHROUGH_INTENDED;
+ case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
+ permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid)
+ : captureAudioOutputAllowed(attrSource);
+ break;
+ case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: {
+ // TODO intended?
+ if (isAllowedDueToCallPerm) break;
+ permRes = audioserver_permissions() ? check_perm(MODIFY_AUDIO_ROUTING, attrSource.uid)
+ : modifyAudioRoutingAllowed(attrSource);
+ break;
+ }
+ case AudioPolicyInterface::API_INPUT_INVALID:
+ default:
+ LOG_ALWAYS_FATAL("%s encountered an invalid input type %d", __func__, (int)inputType);
+ }
+
+ PROPAGATE_FALSEY(permRes);
+
+ if (audiopolicy_flags::record_audio_device_aware_permission()) {
+ // enforce device-aware RECORD_AUDIO permission
+ const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value();
+ return vdi == kDefaultVirtualDeviceId || recordingAllowed(attrSource, vdi, legacySource);
+ }
+ return true;
+}
+
Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttributes& attrAidl,
int32_t inputAidl,
int32_t riidAidl,
@@ -667,6 +814,7 @@
int32_t flagsAidl,
int32_t selectedDeviceIdAidl,
media::GetInputForAttrResponse* _aidl_return) {
+ auto inputSource = attrAidl.source;
audio_attributes_t attr = VALUE_OR_RETURN_BINDER_STATUS(
aidl2legacy_AudioAttributes_audio_attributes_t(attrAidl));
audio_io_handle_t input = VALUE_OR_RETURN_BINDER_STATUS(
@@ -691,97 +839,50 @@
RETURN_IF_BINDER_ERROR(
binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
- audio_source_t inputSource = attr.source;
- if (inputSource == AUDIO_SOURCE_DEFAULT) {
- inputSource = AUDIO_SOURCE_MIC;
- }
-
- // already checked by client, but double-check in case the client wrapper is bypassed
- if ((inputSource < AUDIO_SOURCE_DEFAULT)
- || (inputSource >= AUDIO_SOURCE_CNT
- && inputSource != AUDIO_SOURCE_HOTWORD
- && inputSource != AUDIO_SOURCE_FM_TUNER
- && inputSource != AUDIO_SOURCE_ECHO_REFERENCE
- && inputSource != AUDIO_SOURCE_ULTRASOUND)) {
+ if (inputSource == AudioSource::SYS_RESERVED_INVALID ||
+ std::find(enum_range<AudioSource>().begin(), enum_range<AudioSource>().end(),
+ inputSource) == enum_range<AudioSource>().end()) {
return binderStatusFromStatusT(BAD_VALUE);
}
- RETURN_IF_BINDER_ERROR(validateUsage(attr, attributionSource));
+ if (inputSource == AudioSource::DEFAULT) {
+ inputSource = AudioSource::MIC;
+ }
+
+ const bool isHotword = (flags & (AUDIO_INPUT_FLAG_HW_HOTWORD | AUDIO_INPUT_FLAG_HOTWORD_TAP |
+ AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0;
+
+ const bool isCallRedir = (attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0;
+
+ const bool canCaptureOutput = audioserver_permissions()
+ ? CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid)
+ : captureAudioOutputAllowed(attributionSource);
+
+ //TODO(b/374751406): remove forcing canBypassConcurrentPolicy to canCaptureOutput
+ // once all system apps using CAPTURE_AUDIO_OUTPUT to capture during calls
+ // are updated to use the new CONCURRENT_AUDIO_RECORD_BYPASS permission.
+ bool canBypassConcurrentPolicy = canCaptureOutput;
+ if (concurrent_audio_record_bypass_permission()) {
+ canBypassConcurrentPolicy = audioserver_permissions() ?
+ CHECK_PERM(BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION,
+ attributionSource.uid)
+ : bypassConcurrentPolicyAllowed(attributionSource);
+ }
+
+ const bool hasPerm = VALUE_OR_RETURN_STATUS(evaluatePermsForSource(
+ attributionSource,
+ inputSource,
+ isHotword));
+
+ if (!hasPerm) {
+ return Status::fromExceptionCode(
+ EX_SECURITY, String8::format("%s: %s missing perms for source %s", __func__,
+ attributionSource.toString().c_str(),
+ toString(inputSource).c_str()));
+ }
uint32_t virtualDeviceId = kDefaultVirtualDeviceId;
- // check calling permissions.
- // Capturing from the following sources does not require permission RECORD_AUDIO
- // as the captured audio does not come from a microphone:
- // - FM_TUNER source is controlled by captureTunerAudioInputAllowed() or
- // captureAudioOutputAllowed() (deprecated).
- // - REMOTE_SUBMIX source is controlled by captureAudioOutputAllowed() if the input
- // type is API_INPUT_MIX_EXT_POLICY_REROUTE and by AudioService if a media projection
- // is used and input type is API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK
- // - ECHO_REFERENCE source is controlled by captureAudioOutputAllowed()
- const auto isRecordingAllowed = audioserver_permissions() ?
- CHECK_PERM(RECORD_AUDIO, attributionSource.uid) :
- recordingAllowed(attributionSource, inputSource);
- if (!(isRecordingAllowed
- || inputSource == AUDIO_SOURCE_FM_TUNER
- || inputSource == AUDIO_SOURCE_REMOTE_SUBMIX
- || inputSource == AUDIO_SOURCE_ECHO_REFERENCE)) {
- ALOGE("%s permission denied: recording not allowed for %s",
- __func__, attributionSource.toString().c_str());
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- bool canCaptureOutput = audioserver_permissions() ?
- CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid)
- : captureAudioOutputAllowed(attributionSource);
- bool canInterceptCallAudio = audioserver_permissions() ?
- CHECK_PERM(CALL_AUDIO_INTERCEPTION, attributionSource.uid)
- : callAudioInterceptionAllowed(attributionSource);
- bool isCallAudioSource = inputSource == AUDIO_SOURCE_VOICE_UPLINK
- || inputSource == AUDIO_SOURCE_VOICE_DOWNLINK
- || inputSource == AUDIO_SOURCE_VOICE_CALL;
-
- if (isCallAudioSource && !canInterceptCallAudio && !canCaptureOutput) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
- if (inputSource == AUDIO_SOURCE_ECHO_REFERENCE
- && !canCaptureOutput) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
- if (inputSource == AUDIO_SOURCE_FM_TUNER
- && !canCaptureOutput
- && !(audioserver_permissions() ?
- CHECK_PERM(CAPTURE_TUNER_AUDIO_INPUT, attributionSource.uid)
- : captureTunerAudioInputAllowed(attributionSource))) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- bool canCaptureHotword = audioserver_permissions() ?
- CHECK_PERM(CAPTURE_AUDIO_HOTWORD, attributionSource.uid)
- : captureHotwordAllowed(attributionSource);
- if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- if (((flags & (AUDIO_INPUT_FLAG_HW_HOTWORD |
- AUDIO_INPUT_FLAG_HOTWORD_TAP |
- AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0)
- && !canCaptureHotword) {
- ALOGE("%s: permission denied: hotword mode not allowed"
- " for uid %d pid %d", __func__, attributionSource.uid, attributionSource.pid);
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- if (attr.source == AUDIO_SOURCE_ULTRASOUND) {
- if (!(audioserver_permissions() ?
- CHECK_PERM(ACCESS_ULTRASOUND, attributionSource.uid)
- : accessUltrasoundAllowed(attributionSource))) {
- ALOGE("%s: permission denied: ultrasound not allowed for uid %d pid %d",
- __func__, attributionSource.uid, attributionSource.pid);
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
- }
-
sp<AudioPolicyEffects>audioPolicyEffects;
{
status_t status;
@@ -801,72 +902,29 @@
audioPolicyEffects = mAudioPolicyEffects;
if (status == NO_ERROR) {
- // enforce permission (if any) required for each type of input
- switch (inputType) {
- case AudioPolicyInterface::API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK:
- // this use case has been validated in audio service with a MediaProjection token,
- // and doesn't rely on regular permissions
- case AudioPolicyInterface::API_INPUT_LEGACY:
- break;
- case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
- if ((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0
- && canInterceptCallAudio) {
- break;
- }
- // FIXME: use the same permission as for remote submix for now.
- FALLTHROUGH_INTENDED;
- case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
- if (!canCaptureOutput) {
- ALOGE("%s permission denied: capture not allowed", __func__);
- status = PERMISSION_DENIED;
- }
- break;
- case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: {
- bool modAudioRoutingAllowed;
- if (audioserver_permissions()) {
- auto result = getPermissionProvider().checkPermission(
- MODIFY_AUDIO_ROUTING, attributionSource.uid);
- if (!result.ok()) {
- ALOGE("%s permission provider error: %s", __func__,
- result.error().toString8().c_str());
- status = aidl_utils::statusTFromBinderStatus(result.error());
- break;
- }
- modAudioRoutingAllowed = result.value();
- } else {
- modAudioRoutingAllowed = modifyAudioRoutingAllowed(attributionSource);
- }
- if (!(modAudioRoutingAllowed
- || ((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0
- && canInterceptCallAudio))) {
- ALOGE("%s permission denied for remote submix capture", __func__);
- status = PERMISSION_DENIED;
- }
- break;
- }
- case AudioPolicyInterface::API_INPUT_INVALID:
- default:
- LOG_ALWAYS_FATAL("%s encountered an invalid input type %d",
- __func__, (int)inputType);
- }
+ const auto permResult = evaluatePermsForDevice(attributionSource,
+ inputSource, inputType, virtualDeviceId,
+ isCallRedir);
- if (audiopolicy_flags::record_audio_device_aware_permission()) {
- // enforce device-aware RECORD_AUDIO permission
- if (virtualDeviceId != kDefaultVirtualDeviceId &&
- !recordingAllowed(attributionSource, virtualDeviceId, inputSource)) {
- status = PERMISSION_DENIED;
- }
+ if (!permResult.has_value()) {
+ AutoCallerClear acc;
+ mAudioPolicyManager->releaseInput(portId);
+ return permResult.error();
+ } else if (!permResult.value()) {
+ AutoCallerClear acc;
+ mAudioPolicyManager->releaseInput(portId);
+ return Status::fromExceptionCode(
+ EX_SECURITY,
+ String8::format(
+ "%s: %s missing perms for input type %d, inputSource %d, vdi %d",
+ __func__, attributionSource.toString().c_str(), inputType,
+ inputSource, virtualDeviceId));
}
}
if (status != NO_ERROR) {
- if (status == PERMISSION_DENIED) {
- AutoCallerClear acc;
- mAudioPolicyManager->releaseInput(portId);
- } else {
- _aidl_return->config = VALUE_OR_RETURN_BINDER_STATUS(
- legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/));
- }
+ _aidl_return->config = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/));
return binderStatusFromStatusT(status);
}
@@ -874,14 +932,15 @@
sp<AudioRecordClient> client = new AudioRecordClient(attr, input, session, portId,
selectedDeviceIds, attributionSource,
virtualDeviceId,
- canCaptureOutput, canCaptureHotword,
+ canBypassConcurrentPolicy,
mOutputCommandThread);
mAudioRecordClients.add(portId, client);
}
if (audioPolicyEffects != 0) {
// create audio pre processors according to input source
- status_t status = audioPolicyEffects->addInputEffects(input, inputSource, session);
+ status_t status = audioPolicyEffects->addInputEffects(input,
+ aidl2legacy_AudioSource_audio_source_t(inputSource).value(), session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("Failed to add effects on input %d", input);
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 80ee34e..4c506e8 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -853,13 +853,13 @@
// AND an accessibility service is TOP
// AND source is either VOICE_RECOGNITION OR HOTWORD
// OR there is no active privacy sensitive capture or call
-// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+// OR client can capture calls
// AND source is VOICE_RECOGNITION OR HOTWORD
// The client is an assistant AND active assistant is not being used
// AND an accessibility service is on TOP or a RTT call is active
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR there is no active privacy sensitive capture or call
-// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+// OR client can capture calls
// AND is TOP most recent assistant and uses VOICE_RECOGNITION or HOTWORD
// OR there is no top recent assistant and source is HOTWORD
// OR The client is an accessibility service
@@ -867,7 +867,7 @@
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR The assistant is not on TOP
// AND there is no active privacy sensitive capture or call
-// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+// OR client can capture calls
// AND is on TOP
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR the client source is virtual (remote submix, call audio TX or RX...)
@@ -875,7 +875,7 @@
// AND is on TOP
// OR all active clients are using HOTWORD source
// AND no call is active
-// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+// OR client can capture calls
// OR the client is the current InputMethodService
// AND a RTT call is active AND the source is VOICE_RECOGNITION
// OR The client is an active communication owner
@@ -884,7 +884,11 @@
// AND The assistant is not on TOP
// AND is on TOP or latest started
// AND there is no active privacy sensitive capture or call
-// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+// OR client can capture calls
+// NOTE: a client can capture calls if it either:
+// has CAPTURE_AUDIO_OUTPUT privileged permission (temporarily until
+// all system apps are updated)
+// or has CONCURRENT_AUDIO_RECORD_BYPASS privileged permission
sp<AudioRecordClient> topActive;
@@ -1024,7 +1028,7 @@
// else
// favor the privacy sensitive case
if (topActive != nullptr && topSensitiveActive != nullptr
- && !topActive->canCaptureOutput) {
+ && !topActive->canBypassConcurrentPolicy) {
topActive = nullptr;
}
@@ -1055,8 +1059,8 @@
mMutex) {
uid_t recordUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
recordClient->attributionSource.uid));
- bool canCaptureCall = recordClient->canCaptureOutput;
- bool canCaptureCommunication = recordClient->canCaptureOutput
+ bool canCaptureCall = recordClient->canBypassConcurrentPolicy;
+ bool canCaptureCommunication = recordClient->canBypassConcurrentPolicy
|| !isPhoneStateOwnerActive
|| recordUid == mPhoneStateOwnerUid;
return !(isInCall && !canCaptureCall)
@@ -1072,7 +1076,7 @@
// AND is ongoing communication owner
// AND is on TOP or latest started
const bool allowSensitiveCapture =
- !isSensitiveActive || isTopOrLatestSensitive || current->canCaptureOutput;
+ !isSensitiveActive || isTopOrLatestSensitive || current->canBypassConcurrentPolicy;
bool allowCapture = false;
if (!isAssistantOnTop || isActiveAssistant) {
allowCapture = (isTopOrLatestActive || isTopOrLatestSensitive) &&
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index eeac9a6..5fe200c 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -504,6 +504,14 @@
const audio_output_flags_t flags);
status_t unregisterOutput(audio_io_handle_t output);
+ error::BinderResult<bool> evaluatePermsForSource(const AttributionSourceState& attrSource,
+ AudioSource source, bool isHotword);
+
+ error::BinderResult<bool> evaluatePermsForDevice(const AttributionSourceState& attrSource,
+ AudioSource source,
+ AudioPolicyInterface::input_type_t inputType,
+ uint32_t vdi, bool isCallRedir);
+
// If recording we need to make sure the UID is allowed to do that. If the UID is idle
// then it cannot record and gets buffers with zeros - silence. As soon as the UID
// transitions to an active state we will start reporting buffers with data. This approach
diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h
index 76bc17a..f45b4de 100644
--- a/services/audiopolicy/service/AudioRecordClient.h
+++ b/services/audiopolicy/service/AudioRecordClient.h
@@ -90,14 +90,13 @@
const DeviceIdVector deviceIds,
const AttributionSourceState& attributionSource,
const uint32_t virtualDeviceId,
- bool canCaptureOutput, bool canCaptureHotword,
+ bool canBypassConcurrentPolicy,
wp<AudioPolicyService::AudioCommandThread> commandThread) :
AudioClient(attributes, io, attributionSource,
session, portId, deviceIds), attributionSource(attributionSource),
virtualDeviceId(virtualDeviceId),
- startTimeNs(0), canCaptureOutput(canCaptureOutput),
- canCaptureHotword(canCaptureHotword), silenced(false),
- mOpRecordAudioMonitor(
+ startTimeNs(0), canBypassConcurrentPolicy(canBypassConcurrentPolicy),
+ silenced(false), mOpRecordAudioMonitor(
OpRecordAudioMonitor::createIfNeeded(attributionSource,
virtualDeviceId,
attributes, commandThread)) {
@@ -112,8 +111,7 @@
const AttributionSourceState attributionSource; // attribution source of client
const uint32_t virtualDeviceId; // id of the virtual device associated with the audio device
nsecs_t startTimeNs;
- const bool canCaptureOutput;
- const bool canCaptureHotword;
+ const bool canBypassConcurrentPolicy;
bool silenced;
private:
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index c6434a5..9e89a19 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3796,16 +3796,19 @@
return submitRequestSuccess;
}
-status_t Camera3Device::removeFwkOnlyRegionKeys(CameraMetadata *request) {
- static const std::array<uint32_t, 4> kFwkOnlyRegionKeys = {ANDROID_CONTROL_AF_REGIONS_SET,
- ANDROID_CONTROL_AE_REGIONS_SET, ANDROID_CONTROL_AWB_REGIONS_SET,
- ANDROID_SCALER_CROP_REGION_SET};
+status_t Camera3Device::removeFwkOnlyKeys(CameraMetadata *request) {
+ static const std::array<uint32_t, 5> kFwkOnlyKeys = {
+ ANDROID_CONTROL_AF_REGIONS_SET,
+ ANDROID_CONTROL_AE_REGIONS_SET,
+ ANDROID_CONTROL_AWB_REGIONS_SET,
+ ANDROID_SCALER_CROP_REGION_SET,
+ ANDROID_CONTROL_ZOOM_METHOD};
if (request == nullptr) {
ALOGE("%s request metadata nullptr", __FUNCTION__);
return BAD_VALUE;
}
status_t res = OK;
- for (const auto &key : kFwkOnlyRegionKeys) {
+ for (const auto &key : kFwkOnlyKeys) {
if (request->exists(key)) {
res = request->erase(key);
if (res != OK) {
@@ -3884,7 +3887,7 @@
it != captureRequest->mSettingsList.end(); it++) {
if (parent->mUHRCropAndMeteringRegionMappers.find(it->cameraId) ==
parent->mUHRCropAndMeteringRegionMappers.end()) {
- if (removeFwkOnlyRegionKeys(&(it->metadata)) != OK) {
+ if (removeFwkOnlyKeys(&(it->metadata)) != OK) {
SET_ERR("RequestThread: Unable to remove fwk-only keys from request"
"%d: %s (%d)", halRequest->frame_number, strerror(-res),
res);
@@ -3904,7 +3907,7 @@
return INVALID_OPERATION;
}
captureRequest->mUHRCropAndMeteringRegionsUpdated = true;
- if (removeFwkOnlyRegionKeys(&(it->metadata)) != OK) {
+ if (removeFwkOnlyKeys(&(it->metadata)) != OK) {
SET_ERR("RequestThread: Unable to remove fwk-only keys from request"
"%d: %s (%d)", halRequest->frame_number, strerror(-res),
res);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index fad3f53..5d3c010 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -380,7 +380,7 @@
protected:
status_t disconnectImpl();
- static status_t removeFwkOnlyRegionKeys(CameraMetadata *request);
+ static status_t removeFwkOnlyKeys(CameraMetadata *request);
float getMaxPreviewFps(sp<camera3::Camera3OutputStreamInterface> stream);
diff --git a/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp
index 00bbde3..41be9a4 100644
--- a/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp
@@ -635,8 +635,7 @@
if (detach) {
res = consumer->detachBuffer(consumerSlot);
} else {
- res = consumer->releaseBuffer(consumerSlot, frameNumber, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
+ res = consumer->releaseBuffer(consumerSlot, frameNumber, tracker_ptr->getMergedFence());
}
} else {
SP_LOGE("%s: consumer has become null!", __FUNCTION__);
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index c968e44..ec8da1a 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -248,9 +248,7 @@
// item.mGraphicBuffer was populated with the proper graphic-buffer
// at acquire even if it was previously acquired
- err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer,
- EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer);
if (err != OK) {
BI_LOGE("Failed to release buffer: %s (%d)",
strerror(-err), err);