Merge "C2SoftAacDec: Use default DRC parameters from properties" into main
diff --git a/camera/Android.mk b/camera/Android.mk
deleted file mode 100644
index d9068c0..0000000
--- a/camera/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-include $(call all-subdir-makefiles)
diff --git a/camera/include/camera/camera2/SessionConfiguration.h b/camera/include/camera/camera2/SessionConfiguration.h
index 29913f6..73fafb4 100644
--- a/camera/include/camera/camera2/SessionConfiguration.h
+++ b/camera/include/camera/camera2/SessionConfiguration.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HARDWARE_CAMERA2_SESSIONCONFIGURATION_H
#define ANDROID_HARDWARE_CAMERA2_SESSIONCONFIGURATION_H
+#include "OutputConfiguration.h"
+
#include <binder/Parcelable.h>
namespace android {
@@ -25,8 +27,6 @@
namespace camera2 {
namespace params {
-class OutputConfiguration;
-
class SessionConfiguration : public android::Parcelable {
public:
diff --git a/camera/tests/Android.bp b/camera/tests/Android.bp
new file mode 100644
index 0000000..65b8b41
--- /dev/null
+++ b/camera/tests/Android.bp
@@ -0,0 +1,52 @@
+// Copyright 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_av_camera_license",
+ ],
+}
+
+cc_test {
+ name: "camera_client_test",
+ srcs: [
+ "VendorTagDescriptorTests.cpp",
+ "CameraBinderTests.cpp",
+ "CameraZSLTests.cpp",
+ "CameraCharacteristicsPermission.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libcutils",
+ "libcamera_metadata",
+ "libcamera_client",
+ "libgui",
+ "libsync",
+ "libui",
+ "libdl",
+ "libbinder",
+ ],
+ include_dirs: [
+ "system/media/private/camera/include",
+ "system/media/camera/tests",
+ "frameworks/av/services/camera/libcameraservice",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
deleted file mode 100644
index 7f8078e..0000000
--- a/camera/tests/Android.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SRC_FILES:= \
- VendorTagDescriptorTests.cpp \
- CameraBinderTests.cpp \
- CameraZSLTests.cpp \
- CameraCharacteristicsPermission.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libutils \
- libcutils \
- libcamera_metadata \
- libcamera_client \
- libgui \
- libsync \
- libui \
- libdl \
- libbinder
-
-LOCAL_C_INCLUDES += \
- system/media/private/camera/include \
- system/media/camera/tests \
- frameworks/av/services/camera/libcameraservice \
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-LOCAL_MODULE:= camera_client_test
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../NOTICE
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/media/aconfig/Android.bp b/media/aconfig/Android.bp
index 96bf4f5..ee25c03 100644
--- a/media/aconfig/Android.bp
+++ b/media/aconfig/Android.bp
@@ -1,22 +1,49 @@
+// deprecated
aconfig_declarations {
name: "aconfig_mediacodec_flags",
package: "com.android.media.codec.flags",
srcs: ["mediacodec_flags.aconfig"],
}
+// deprecated
java_aconfig_library {
name: "aconfig_mediacodec_flags_java_lib",
aconfig_declarations: "aconfig_mediacodec_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// deprecated
cc_aconfig_library {
name: "aconfig_mediacodec_flags_c_lib",
min_sdk_version: "30",
vendor_available: true,
+ double_loadable: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+ aconfig_declarations: "aconfig_mediacodec_flags",
+}
+
+aconfig_declarations {
+ name: "aconfig_codec_fwk_flags",
+ package: "android.media.codec",
+ srcs: ["codec_fwk.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.media.codec-aconfig-java",
+ aconfig_declarations: "aconfig_codec_fwk_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+cc_aconfig_library {
+ name: "android.media.codec-aconfig-cc",
+ min_sdk_version: "30",
+ vendor_available: true,
apex_available: [
"//apex_available:platform",
"com.android.media.swcodec",
],
- aconfig_declarations: "aconfig_mediacodec_flags",
+ aconfig_declarations: "aconfig_codec_fwk_flags",
}
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
new file mode 100644
index 0000000..06aceeb
--- /dev/null
+++ b/media/aconfig/codec_fwk.aconfig
@@ -0,0 +1,33 @@
+# Codec framework feature flags.
+#
+# !!! Please add flags in alphabetical order. !!!
+
+package: "android.media.codec"
+
+flag {
+ name: "aidl_hal_input_surface"
+ namespace: "codec_fwk"
+ description: "Feature flags for enabling AIDL HAL InputSurface handling"
+ bug: "201479783"
+}
+
+flag {
+ name: "dynamic_color_aspects"
+ namespace: "codec_fwk"
+ description: "Feature flag for dynamic color aspect support"
+ bug: "297914560"
+}
+
+flag {
+ name: "hlg_editing"
+ namespace: "codec_fwk"
+ description: "Feature flag for HLG editing support"
+ bug: "316397061"
+}
+
+flag {
+ name: "null_output_surface"
+ namespace: "codec_fwk"
+ description: "Feature flag for null output Surface support"
+ bug: "297920102"
+}
diff --git a/media/aconfig/mediacodec_flags.aconfig b/media/aconfig/mediacodec_flags.aconfig
index c82ad4d..be0fc5c 100644
--- a/media/aconfig/mediacodec_flags.aconfig
+++ b/media/aconfig/mediacodec_flags.aconfig
@@ -1,5 +1,10 @@
package: "com.android.media.codec.flags"
+# ******************************************************************
+# !!! DO NOT ADD FURTHER FLAGS TO THIS FILE !!!
+# !!! USE codec_fwk.aconfig INSTEAD !!!
+# ******************************************************************
+
flag {
name: "large_audio_frame"
namespace: "codec_fwk"
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index ab48198..bcb31f3 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -28,7 +28,6 @@
#include "media/AidlConversionCppNdk.h"
-#include <media/ShmemCompat.h>
#include <media/stagefright/foundation/MediaDefs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/media/audioaidlconversion/Android.bp b/media/audioaidlconversion/Android.bp
index d3a5755..07c59c7 100644
--- a/media/audioaidlconversion/Android.bp
+++ b/media/audioaidlconversion/Android.bp
@@ -56,6 +56,19 @@
}
cc_defaults {
+ name: "audio_aidl_conversion_common_default_cpp",
+ shared_libs: [
+ "libbinder",
+ "libshmemcompat",
+ "shared-file-region-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ ],
+ export_shared_lib_headers: [
+ "shared-file-region-aidl-cpp",
+ ],
+}
+
+cc_defaults {
name: "audio_aidl_conversion_common_default",
export_include_dirs: ["include"],
host_supported: true,
@@ -67,17 +80,12 @@
],
shared_libs: [
"libbase",
- "libbinder",
"liblog",
- "libshmemcompat",
"libstagefright_foundation",
"libutils",
- "shared-file-region-aidl-cpp",
- "framework-permission-aidl-cpp",
],
export_shared_lib_headers: [
"libbase",
- "shared-file-region-aidl-cpp",
],
cflags: [
"-Wall",
@@ -113,6 +121,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"latest_android_media_audio_common_types_cpp_export_shared",
],
min_sdk_version: "29",
@@ -223,6 +232,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"audio_aidl_conversion_common_util_default",
"latest_android_media_audio_common_types_cpp_shared",
"latest_android_media_audio_common_types_ndk_shared",
diff --git a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
index 656d76a..7cba011 100644
--- a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
+++ b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
@@ -25,12 +25,12 @@
#define AUDIO_AIDL_CONVERSION_AIDL_CONVERSION_UTIL_CPP
#endif // BACKEND_NDK_IMPL
+#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
#include <android-base/expected.h>
-#include <binder/Status.h>
#if defined(BACKEND_NDK_IMPL)
#include <android/binder_auto_utils.h>
@@ -40,6 +40,7 @@
namespace aidl {
#else
#include <binder/Enums.h>
+#include <binder/Status.h>
#endif // BACKEND_NDK_IMPL
namespace android {
@@ -374,6 +375,30 @@
* Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code
* can be found from transactionError() or serviceSpecificErrorCode().
*/
+#if defined(BACKEND_NDK_IMPL)
+static inline ::android::status_t statusTFromExceptionCode(binder_exception_t exception) {
+ switch (exception) {
+ case EX_NONE:
+ return ::android::OK;
+ case EX_SECURITY: // Java SecurityException, rethrows locally in Java
+ return ::android::PERMISSION_DENIED;
+ case EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java
+ case EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java
+ case EX_NULL_POINTER: // Java NullPointerException, rethrows in Java
+ return ::android::BAD_VALUE;
+ case EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java
+ case EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows
+ return ::android::INVALID_OPERATION;
+ case EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows
+ case EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows
+ case EX_TRANSACTION_FAILED: // Native - see error code
+ case EX_SERVICE_SPECIFIC: // Java ServiceSpecificException,
+ // rethrows in Java with integer error code
+ return ::android::UNKNOWN_ERROR;
+ }
+ return ::android::UNKNOWN_ERROR;
+}
+#else
static inline ::android::status_t statusTFromExceptionCode(int32_t exceptionCode) {
using namespace ::android::binder;
switch (exceptionCode) {
@@ -398,6 +423,7 @@
}
return ::android::UNKNOWN_ERROR;
}
+#endif // BACKEND_NDK_IMPL
/**
* Return the equivalent Android ::android::status_t from a binder status.
@@ -410,6 +436,7 @@
*
* return_type method(type0 param0, ...)
*/
+#if !defined(BACKEND_NDK_IMPL)
static inline ::android::status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
return status.isOk() ? ::android::OK // check ::android::OK,
: status.serviceSpecificErrorCode() // service-side error, not standard Java exception
@@ -418,6 +445,7 @@
?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a
// standard Java exception (fromExceptionCode)
}
+#endif
#if defined(BACKEND_NDK_IMPL)
static inline ::android::status_t statusTFromBinderStatus(const ::ndk::ScopedAStatus &status) {
@@ -443,6 +471,7 @@
* This is used for methods not returning an explicit status_t,
* where Java callers expect an exception, not an integer return value.
*/
+#if !defined(BACKEND_NDK_IMPL)
static inline ::android::binder::Status binderStatusFromStatusT(
::android::status_t status, const char *optionalMessage = nullptr) {
const char * const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
@@ -470,6 +499,7 @@
// throw a ServiceSpecificException.
return Status::fromServiceSpecificError(status, emptyIfNull);
}
+#endif
} // namespace aidl_utils
diff --git a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
index 60727b4..f78243e 100644
--- a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
+++ b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+#define LOG_TAG "AidlConversionNdkTests"
#include <iostream>
#include <type_traits>
diff --git a/media/codec2/Android.mk b/media/codec2/Android.mk
deleted file mode 100644
index 82d739f..0000000
--- a/media/codec2/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# =============================================================================
-# DOCUMENTATION GENERATION
-# =============================================================================
-C2_ROOT := $(call my-dir)
-
-C2_DOCS_ROOT := $(OUT_DIR)/target/common/docs/codec2
-
-C2_OUT_TEMP := $(PRODUCT_OUT)/gen/ETC/Codec2-docs_intermediates
-
-C2_DOXY := $(or $(shell command -v doxygen),\
- $(shell command -v /Applications/Doxygen.app/Contents/Resources/doxygen))
-
-.PHONY: check-doxygen
-check-doxygen:
-ifndef C2_DOXY
- $(error 'doxygen is not available')
-endif
-
-$(C2_OUT_TEMP)/doxy-api.config: $(C2_ROOT)/docs/doxygen.config
- # only document include directory, no internal sections
- sed 's/\(^INPUT *=.*\)/\1include\//; \
- s/\(^INTERNAL_DOCS *= *\).*/\1NO/; \
- s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/; \
- s:\(^OUTPUT_DIRECTORY *= \)out:\1'$(OUT_DIR)':;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-$(C2_OUT_TEMP)/doxy-internal.config: $(C2_ROOT)/docs/doxygen.config
- sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$(OUT_DIR)'\2internal:;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-.PHONY: docs-api
-docs-api: $(C2_OUT_TEMP)/doxy-api.config check-doxygen
- echo API docs are building in $(C2_DOCS_ROOT)/api
- rm -rf $(C2_DOCS_ROOT)/api
- mkdir -p $(C2_DOCS_ROOT)/api
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-api.config
-
-.PHONY: docs-internal
-docs-internal: $(C2_OUT_TEMP)/doxy-internal.config check-doxygen
- echo Internal docs are building in $(C2_DOCS_ROOT)/internal
- rm -rf $(C2_DOCS_ROOT)/internal
- mkdir -p $(C2_DOCS_ROOT)/internal
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-internal.config
-
-.PHONY: docs-all
-docs-all: docs-api docs-internal
-
-include $(call all-makefiles-under,$(call my-dir))
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 8a894f3..b911e11 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -25,5 +25,8 @@
}
]
}
+ ],
+ "postsubmit": [
+ { "name": "c2aidl_gtracker_test"}
]
}
diff --git a/media/codec2/components/OWNERS b/media/codec2/components/OWNERS
new file mode 100644
index 0000000..453999a
--- /dev/null
+++ b/media/codec2/components/OWNERS
@@ -0,0 +1 @@
+kyslov@google.com
\ No newline at end of file
diff --git a/media/codec2/components/aom/C2SoftAomEnc.cpp b/media/codec2/components/aom/C2SoftAomEnc.cpp
index 71909e5..7c9d3e8 100644
--- a/media/codec2/components/aom/C2SoftAomEnc.cpp
+++ b/media/codec2/components/aom/C2SoftAomEnc.cpp
@@ -338,6 +338,19 @@
}
c2_status_t C2SoftAomEnc::onStop() {
+ IntfImpl::Lock lock = mIntf->lock();
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
+ lock.unlock();
+ if (requestSync != mRequestSync) {
+ // we can handle IDR immediately
+ if (requestSync->value) {
+ // unset request
+ C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
+ }
+ mRequestSync = requestSync;
+ }
onRelease();
return C2_OK;
}
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.cpp b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
index 3f96cb3..76680a3 100644
--- a/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
@@ -42,6 +42,8 @@
constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr uint32_t kOutputDelay = 4;
+
class C2SoftDav1dDec::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
@@ -239,6 +241,13 @@
.withFields({C2F(mPixelFormat, value).oneOf(pixelFormats)})
.withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
.build());
+
+ addParameter(
+ DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+ .withDefault(new C2PortActualDelayTuning::output(kOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kOutputDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
+ .build());
}
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output>& oldMe,
@@ -450,13 +459,6 @@
if (mDav1dCtx) {
Dav1dPicture p;
- while (mDecodedPictures.size() > 0) {
- p = mDecodedPictures.front();
- mDecodedPictures.pop_front();
-
- dav1d_picture_unref(&p);
- }
-
int res = 0;
while (true) {
memset(&p, 0, sizeof(p));
@@ -527,6 +529,8 @@
android::base::GetIntProperty(NUM_THREADS_DAV1D_PROPERTY, NUM_THREADS_DAV1D_DEFAULT);
if (numThreads > 0) lib_settings.n_threads = numThreads;
+ lib_settings.max_frame_delay = kOutputDelay;
+
int res = 0;
if ((res = dav1d_open(&mDav1dCtx, &lib_settings))) {
ALOGE("dav1d_open failed. status: %d.", res);
@@ -540,15 +544,6 @@
void C2SoftDav1dDec::destroyDecoder() {
if (mDav1dCtx) {
- Dav1dPicture p;
- while (mDecodedPictures.size() > 0) {
- memset(&p, 0, sizeof(p));
- p = mDecodedPictures.front();
- mDecodedPictures.pop_front();
-
- dav1d_picture_unref(&p);
- }
-
dav1d_close(&mDav1dCtx);
mDav1dCtx = nullptr;
mOutputBufferIndex = 0;
@@ -572,19 +567,24 @@
}
void C2SoftDav1dDec::finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
- const std::shared_ptr<C2GraphicBlock>& block) {
+ const std::shared_ptr<C2GraphicBlock>& block,
+ const Dav1dPicture &img) {
std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block, C2Rect(mWidth, mHeight));
{
IntfImpl::Lock lock = mIntf->lock();
buffer->setInfo(mIntf->getColorAspects_l());
}
- auto fillWork = [buffer, index](const std::unique_ptr<C2Work>& work) {
+
+ auto fillWork = [buffer, index, img, this](const std::unique_ptr<C2Work>& work) {
uint32_t flags = 0;
if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
(c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
flags |= C2FrameData::FLAG_END_OF_STREAM;
ALOGV("signalling end_of_stream.");
}
+ getHDRStaticParams(&img, work);
+ getHDR10PlusInfoData(&img, work);
+
work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
@@ -598,10 +598,6 @@
}
}
-static void freeCallback(const uint8_t */*data*/, void */*cookie*/) {
- return;
-}
-
void C2SoftDav1dDec::process(const std::unique_ptr<C2Work>& work,
const std::shared_ptr<C2BlockPool>& pool) {
work->result = C2_OK;
@@ -652,19 +648,60 @@
if (res == 0) {
ALOGV("dav1d found a sequenceHeader (%dx%d) for in_frameIndex=%ld.", seq.max_width,
seq.max_height, (long)in_frameIndex);
+ if (seq.max_width != mWidth || seq.max_height != mHeight) {
+ drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+ mWidth = seq.max_width;
+ mHeight = seq.max_height;
+
+ C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(size));
+ } else {
+ ALOGE("Config update size failed");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ work->workletsProcessed = 1u;
+ return;
+ }
+ }
}
+ // insert OBU TD if it is not present.
+ // TODO: b/286852962
+ uint8_t obu_type = (bitstream[0] >> 3) & 0xf;
Dav1dData data;
- res = dav1d_data_wrap(&data, bitstream, inSize, freeCallback, nullptr);
- if (res != 0) {
- ALOGE("Decoder wrap error %s!", strerror(DAV1D_ERR(res)));
+ uint8_t* ptr = (obu_type == DAV1D_OBU_TD) ? dav1d_data_create(&data, inSize)
+ : dav1d_data_create(&data, inSize + 2);
+ if (ptr == nullptr) {
+ ALOGE("dav1d_data_create failed!");
i_ret = -1;
+
} else {
data.m.timestamp = in_frameIndex;
- // ALOGV("inSize=%ld, in_frameIndex=%ld, timestamp=%ld",
- // inSize, frameIndex, data.m.timestamp);
+ int new_Size;
+ if (obu_type != DAV1D_OBU_TD) {
+ new_Size = (int)(inSize + 2);
+
+ // OBU TD
+ ptr[0] = 0x12;
+ ptr[1] = 0;
+
+ memcpy(ptr + 2, bitstream, inSize);
+ } else {
+ new_Size = (int)(inSize);
+ // TODO: b/277797541 - investigate how to wrap this pointer in Dav1dData to
+ // avoid memcopy operations.
+ memcpy(ptr, bitstream, new_Size);
+ }
+
+ // ALOGV("memcpy(ptr,bitstream,inSize=%ld,new_Size=%d,in_frameIndex=%ld,timestamp=%ld,"
+ // "ptr[0,1,2,3,4]=%x,%x,%x,%x,%x)",
+ // inSize, new_Size, frameIndex, data.m.timestamp, ptr[0], ptr[1], ptr[2],
+ // ptr[3], ptr[4]);
// Dump the bitstream data (inputBuffer) if dumping is enabled.
#ifdef FILE_DUMP_ENABLE
@@ -672,6 +709,7 @@
#endif
bool b_draining = false;
+ int res;
do {
res = dav1d_send_data(mDav1dCtx, &data);
@@ -685,39 +723,9 @@
break;
}
- bool b_output_error = false;
+ outputBuffer(pool, work);
- do {
- Dav1dPicture img;
- memset(&img, 0, sizeof(img));
-
- res = dav1d_get_picture(mDav1dCtx, &img);
- if (res == 0) {
- mDecodedPictures.push_back(img);
-
- if (!end_of_stream) break;
- } else if (res == DAV1D_ERR(EAGAIN)) {
- /* the decoder needs more data to be able to output something.
- * if there is more data pending, continue the loop below or
- * otherwise break */
- if (data.sz != 0) res = 0;
- break;
- } else {
- ALOGE("warning! Decoder error %d!", res);
- b_output_error = true;
- break;
- }
- } while (res == 0);
-
- if (b_output_error) break;
-
- /* on drain, we must ignore the 1st EAGAIN */
- if (!b_draining && (res == DAV1D_ERR(EAGAIN) || res == 0) &&
- (end_of_stream)) {
- b_draining = true;
- res = 0;
- }
- } while (res == 0 && ((data.sz != 0) || b_draining));
+ } while (res == DAV1D_ERR(EAGAIN));
if (data.sz > 0) {
ALOGE("unexpected data.sz=%zu after dav1d_send_data", data.sz);
@@ -739,8 +747,6 @@
}
}
- (void)outputBuffer(pool, work);
-
if (end_of_stream) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
mSignalledOutputEos = true;
@@ -749,7 +755,7 @@
}
}
-void C2SoftDav1dDec::getHDRStaticParams(Dav1dPicture* picture,
+void C2SoftDav1dDec::getHDRStaticParams(const Dav1dPicture* picture,
const std::unique_ptr<C2Work>& work) {
C2StreamHdrStaticMetadataInfo::output hdrStaticMetadataInfo{};
bool infoPresent = false;
@@ -813,7 +819,7 @@
}
}
-void C2SoftDav1dDec::getHDR10PlusInfoData(Dav1dPicture* picture,
+void C2SoftDav1dDec::getHDR10PlusInfoData(const Dav1dPicture* picture,
const std::unique_ptr<C2Work>& work) {
if (picture != nullptr) {
if (picture->itut_t35 != nullptr) {
@@ -853,7 +859,7 @@
}
}
-void C2SoftDav1dDec::getVuiParams(Dav1dPicture* picture) {
+void C2SoftDav1dDec::getVuiParams(const Dav1dPicture* picture) {
VuiColorAspects vuiColorAspects;
if (picture) {
@@ -924,53 +930,16 @@
memset(&img, 0, sizeof(img));
int res = 0;
- if (mDecodedPictures.size() > 0) {
- img = mDecodedPictures.front();
- mDecodedPictures.pop_front();
- // ALOGD("Got a picture(out_frameIndex=%ld,timestamp=%ld) from the deque for
- // outputBuffer.",img.m.timestamp,img.m.timestamp);
- } else {
- res = dav1d_get_picture(mDav1dCtx, &img);
- if (res == 0) {
- // ALOGD("Got a picture(out_frameIndex=%ld,timestamp=%ld) from dav1d for
- // outputBuffer.",img.m.timestamp,img.m.timestamp);
- } else {
- ALOGE("failed to get a picture from dav1d for outputBuffer.");
- }
- }
-
+ res = dav1d_get_picture(mDav1dCtx, &img);
if (res == DAV1D_ERR(EAGAIN)) {
- ALOGD("Not enough data to output a picture.");
+ ALOGV("Not enough data to output a picture.");
return false;
- }
- if (res != 0) {
+ } else if (res != 0) {
ALOGE("The AV1 decoder failed to get a picture (res=%s).", strerror(DAV1D_ERR(res)));
return false;
}
- const int width = img.p.w;
- const int height = img.p.h;
- if (width != mWidth || height != mHeight) {
- mWidth = width;
- mHeight = height;
-
- C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
- if (err == C2_OK) {
- work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(size));
- } else {
- ALOGE("Config update size failed");
- mSignalledError = true;
- work->result = C2_CORRUPTED;
- work->workletsProcessed = 1u;
- return false;
- }
- }
-
getVuiParams(&img);
- getHDRStaticParams(&img, work);
- getHDR10PlusInfoData(&img, work);
// out_frameIndex that the decoded picture returns from dav1d.
int64_t out_frameIndex = img.m.timestamp;
@@ -1156,9 +1125,8 @@
convFormat);
}
+ finishWork(out_frameIndex, work, std::move(block), img);
dav1d_picture_unref(&img);
-
- finishWork(out_frameIndex, work, std::move(block));
block = nullptr;
return true;
}
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.h b/media/codec2/components/dav1d/C2SoftDav1dDec.h
index e3d2a93..5d2a725 100644
--- a/media/codec2/components/dav1d/C2SoftDav1dDec.h
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.h
@@ -58,7 +58,6 @@
int mOutputBufferIndex = 0;
Dav1dContext* mDav1dCtx = nullptr;
- std::deque<Dav1dPicture> mDecodedPictures;
// configurations used by component in process
// (TODO: keep this in intf but make them internal only)
@@ -101,12 +100,13 @@
nsecs_t mTimeEnd = 0; // Time at the end of decode()
bool initDecoder();
- void getHDRStaticParams(Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
- void getHDR10PlusInfoData(Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
- void getVuiParams(Dav1dPicture* picture);
+ void getHDRStaticParams(const Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
+ void getHDR10PlusInfoData(const Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
+ void getVuiParams(const Dav1dPicture* picture);
void destroyDecoder();
void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
- const std::shared_ptr<C2GraphicBlock>& block);
+ const std::shared_ptr<C2GraphicBlock>& block,
+ const Dav1dPicture &img);
// Sets |work->result| and mSignalledError. Returns false.
void setError(const std::unique_ptr<C2Work>& work, c2_status_t error);
bool allocTmpFrameBuffer(size_t size);
diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp
index a03d4e2..ea13071 100644
--- a/media/codec2/components/raw/C2SoftRawDec.cpp
+++ b/media/codec2/components/raw/C2SoftRawDec.cpp
@@ -65,7 +65,7 @@
addParameter(
DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
.withDefault(new C2StreamChannelCountInfo::output(0u, 2))
- .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+ .withFields({C2F(mChannelCount, value).inRange(1, 12)})
.withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
.build());
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 9a3399d..785cdf2 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -160,6 +160,10 @@
kParamIndexSecureMode,
kParamIndexEncryptedBuffer, // info-buffer, used with SM_READ_PROTECTED_WITH_ENCRYPTED
+ /* multiple access unit support */
+ kParamIndexLargeFrame,
+ kParamIndexAccessUnitInfos, // struct
+
// deprecated
kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG,
@@ -1114,6 +1118,36 @@
constexpr char C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE[] = "input.buffers.max-size";
constexpr char C2_PARAMKEY_OUTPUT_MAX_BUFFER_SIZE[] = "output.buffers.max-size";
+/**
+ * Large frame struct
+ *
+ * This structure describes the size limits for large frames (frames with multiple
+ * access units.)
+ */
+struct C2LargeFrameStruct {
+ uint32_t maxSize; ///< maximum size of the buffer in bytes
+ uint32_t thresholdSize; ///< size threshold for the buffer in bytes. The buffer is considered
+ ///< full as soon as its size reaches or surpasses this limit.
+ C2LargeFrameStruct()
+ : maxSize(0),
+ thresholdSize(0) {}
+
+ C2LargeFrameStruct(uint32_t maxSize_, uint32_t thresholdSize_)
+ : maxSize(maxSize_), thresholdSize(thresholdSize_) {}
+
+ DEFINE_AND_DESCRIBE_C2STRUCT(LargeFrame)
+ C2FIELD(maxSize, "max-size")
+ C2FIELD(thresholdSize, "threshold-size")
+};
+
+/**
+ * This tuning controls the size limits for large output frames for the component.
+ * The default value for this tuning is platform specific.
+ */
+typedef C2StreamParam<C2Tuning, C2LargeFrameStruct, kParamIndexLargeFrame>
+ C2LargeFrame;
+constexpr char C2_PARAMKEY_OUTPUT_LARGE_FRAME[] = "output.large-frame";
+
/* ---------------------------------------- misc. state ---------------------------------------- */
/**
@@ -2146,6 +2180,49 @@
C2StreamAudioFrameSizeInfo;
constexpr char C2_PARAMKEY_AUDIO_FRAME_SIZE[] = "raw.audio-frame-size";
+/**
+ * Information for an access unit in a large frame (containing multiple access units)
+ */
+struct C2AccessUnitInfosStruct {
+
+ inline C2AccessUnitInfosStruct() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ inline C2AccessUnitInfosStruct(
+ uint32_t flags_,
+ uint32_t size_,
+ int64_t timestamp_)
+ : flags(flags_),
+ size(size_),
+ timestamp(timestamp_) { }
+
+ uint32_t flags; ///<flags for the access-unit
+ uint32_t size; ///<size of access-unit
+ int64_t timestamp; ///<timestamp in us for the access-unit
+
+ DEFINE_AND_DESCRIBE_C2STRUCT(AccessUnitInfos)
+ C2FIELD(flags, "flags")
+ C2FIELD(size, "size")
+ C2FIELD(timestamp, "timestamp")
+};
+
+/**
+ * Multiple access unit support (e.g large audio frames)
+ *
+ * If supported by a component, multiple access units may be contained
+ * in a single work item. For now this is only defined for linear buffers.
+ * The metadata indicates the access-unit boundaries in a single buffer.
+ * The boundary of each access-units are marked by its size, immediately
+ * followed by the next access-unit.
+ */
+typedef C2StreamParam<C2Info, C2SimpleArrayStruct<C2AccessUnitInfosStruct>,
+ kParamIndexAccessUnitInfos>
+ C2AccessUnitInfos;
+
+constexpr char C2_PARAMKEY_INPUT_ACCESS_UNIT_INFOS[] = "input.access-unit-infos";
+constexpr char C2_PARAMKEY_OUTPUT_ACCESS_UNIT_INFOS[] = "output.access-unit-infos";
+
/* --------------------------------------- AAC components --------------------------------------- */
/**
diff --git a/media/codec2/core/include/C2ParamDef.h b/media/codec2/core/include/C2ParamDef.h
index d578820..86dfe65 100644
--- a/media/codec2/core/include/C2ParamDef.h
+++ b/media/codec2/core/include/C2ParamDef.h
@@ -404,6 +404,7 @@
/// Specialization for an input port parameter.
struct input : public T, public S,
public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+ using T::operator!=;
_C2_CORE_INDEX_OVERRIDE(ParamIndex)
/// Wrapper around base structure's constructor.
template<typename ...Args>
@@ -416,6 +417,7 @@
/// Specialization for an output port parameter.
struct output : public T, public S,
public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+ using T::operator!=;
_C2_CORE_INDEX_OVERRIDE(ParamIndex)
/// Wrapper around base structure's constructor.
template<typename ...Args>
@@ -470,6 +472,7 @@
/// Specialization for an input port parameter.
struct input : public T,
public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+ using T::operator!=;
private:
/// Wrapper around base structure's constructor while also specifying port/direction.
template<typename ...Args>
@@ -486,6 +489,7 @@
/// Specialization for an output port parameter.
struct output : public T,
public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+ using T::operator!=;
private:
/// Wrapper around base structure's constructor while also specifying port/direction.
template<typename ...Args>
@@ -549,6 +553,7 @@
struct input : public T, public S,
public _C2StructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
+ using T::operator!=;
_C2_CORE_INDEX_OVERRIDE(ParamIndex)
/// Default constructor. Stream-ID is undefined.
@@ -567,6 +572,7 @@
struct output : public T, public S,
public _C2StructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
+ using T::operator!=;
_C2_CORE_INDEX_OVERRIDE(ParamIndex)
/// Default constructor. Stream-ID is undefined.
@@ -634,6 +640,7 @@
struct input : public T,
public _C2FlexStructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
+ using T::operator!=;
private:
/// Default constructor. Stream-ID is undefined.
inline input(size_t flexCount) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE) { }
@@ -656,6 +663,7 @@
struct output : public T,
public _C2FlexStructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
+ using T::operator!=;
private:
/// Default constructor. Stream-ID is undefined.
inline output(size_t flexCount) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE) { }
diff --git a/media/codec2/docs/doxygen.config b/media/codec2/docs/doxygen.config
index 5c3bea3..ab8b53b 100644
--- a/media/codec2/docs/doxygen.config
+++ b/media/codec2/docs/doxygen.config
@@ -162,7 +162,7 @@
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH = frameworks/av/media/libstagefright/codec2
+STRIP_FROM_PATH = frameworks/av/media/codec2
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -781,7 +781,7 @@
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = frameworks/av/media/libstagefright/codec2/
+INPUT = frameworks/av/media/codec2/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -897,7 +897,7 @@
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-INPUT_FILTER = frameworks/av/media/libstagefright/codec2/docs/doxyfilter.sh
+INPUT_FILTER = frameworks/av/media/codec2/docs/doxyfilter.sh
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
diff --git a/media/codec2/doxygen.sh b/media/codec2/doxygen.sh
new file mode 100755
index 0000000..ca5aeed
--- /dev/null
+++ b/media/codec2/doxygen.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Copyright (C) 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.
+
+# =============================================================================
+# DOCUMENTATION GENERATION
+# =============================================================================
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "error: Android build is not set up. Run this command after lunch." >&2
+ exit 2
+fi
+
+OUT_DIR=$ANDROID_BUILD_TOP/out
+
+# Codec 2.0 source and target paths
+C2_ROOT=$(dirname "$0")
+C2_DOCS_ROOT=$OUT_DIR/target/common/docs/codec2
+C2_OUT_TEMP=$ANDROID_PRODUCT_OUT/gen/ETC/Codec2-docs_intermediates
+
+# Doxygen path
+DOXY=$(which doxygen)
+DOXY_MAC="/Applications/Doxygen.app/Contents/Resources/doxygen"
+if [ -z "$DOXY" -a -x "$DOXY_MAC" ]; then
+ DOXY=$DOXY_MAC
+fi
+
+if [ -z "$DOXY" ]; then
+ echo "error: doxygen is not available" >&2
+ exit 2
+fi
+
+# Create doxygen config
+# ---------------------
+gen_doxy() {
+ local variant=$1
+ local variant_lc=$(echo $variant | tr A-Z a-z)
+ mkdir -p $C2_OUT_TEMP
+ if [ "$variant_lc" == "api" ]; then
+ # only document include directory, no internal sections
+ sed 's/\(^INPUT *=.*\)/\1core\/include\//;
+ s/\(^INTERNAL_DOCS *= *\).*/\1NO/;
+ s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/;
+ s:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$OUT_DIR'\2'$variant_lc':;' \
+ $C2_ROOT/docs/doxygen.config > $C2_OUT_TEMP/doxy-$variant_lc.config
+
+ ls -la $C2_OUT_TEMP/doxy-$variant_lc.config
+ else
+ sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$OUT_DIR'\2'$variant_lc':;' \
+ $C2_ROOT/docs/doxygen.config > $C2_OUT_TEMP/doxy-$variant_lc.config
+ fi
+
+ echo $variant docs are building in $C2_DOCS_ROOT/$variant_lc
+ rm -rf $C2_DOCS_ROOT/$variant_lc
+ mkdir -p $C2_DOCS_ROOT/$variant_lc
+ pushd $ANDROID_BUILD_TOP
+ $DOXY $C2_OUT_TEMP/doxy-$variant_lc.config
+ popd
+}
+
+usage() {
+ echo "usage: $(basename "$0") [target]"
+ echo " where target can be one of:"
+ echo " all: build both API and internal docs (default)"
+ echo " api: build API docs only"
+ echo " internal: build internal docs which include implementation details"
+}
+
+TARGET=${1:-all}
+case "$TARGET" in
+ api) gen_doxy API;;
+ internal) gen_doxy Internal;;
+ all) gen_doxy API; gen_doxy Internal;;
+ -h) usage; exit 0;;
+ *) echo "unknown target '$TARGET'" >&2; usage; exit 2;;
+esac
diff --git a/media/codec2/hal/aidl/Android.bp b/media/codec2/hal/aidl/Android.bp
index 7a9af18..48b6e21 100644
--- a/media/codec2/hal/aidl/Android.bp
+++ b/media/codec2/hal/aidl/Android.bp
@@ -24,7 +24,7 @@
shared_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.media.bufferpool@2.0",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.c2-V1-ndk",
"libbinder_ndk",
"libbase",
@@ -84,7 +84,7 @@
shared_libs: [
"android.hardware.common-V2-ndk",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.c2-V1-ndk",
"libbase",
"libbinder_ndk",
diff --git a/media/codec2/hal/aidl/BufferTypes.cpp b/media/codec2/hal/aidl/BufferTypes.cpp
index bc4948b..a0e6aa5 100644
--- a/media/codec2/hal/aidl/BufferTypes.cpp
+++ b/media/codec2/hal/aidl/BufferTypes.cpp
@@ -302,7 +302,7 @@
"invalid receiver connection id (0).";
return bufferpool2::ResultStatus::CRITICAL_ERROR;
} else {
- if (isNewConnection) {
+ if (foundConnection == mConnections.end()) {
foundConnection = mConnections.try_emplace(
connectionId, receiverConnectionId, now).first;
} else {
diff --git a/media/codec2/hal/aidl/Component.cpp b/media/codec2/hal/aidl/Component.cpp
index 4605af3..ba5f8d4 100644
--- a/media/codec2/hal/aidl/Component.cpp
+++ b/media/codec2/hal/aidl/Component.cpp
@@ -46,13 +46,17 @@
using ::aidl::android::hardware::common::NativeHandle;
using ::aidl::android::hardware::media::bufferpool2::IClientManager;
using ::ndk::ScopedAStatus;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
Listener(const std::shared_ptr<Component>& component) :
- mComponent(component),
- mListener(component->mListener) {
+ mComponent(component),
+ mListener(component->mListener) {
+ CHECK(component);
+ CHECK(component->mListener);
}
virtual void onError_nb(
@@ -137,6 +141,52 @@
std::weak_ptr<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const std::shared_ptr<Component>& component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::DeathContext
struct Component::DeathContext {
std::weak_ptr<Component> mWeakComp;
@@ -149,14 +199,38 @@
const std::shared_ptr<ComponentStore>& store,
const std::shared_ptr<IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{SharedRefBase::make<ComponentInterface>(
- component->intf(), store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager},
mDeathContext(nullptr) {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ ::android::GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = SharedRefBase::make<ComponentInterface>(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -179,8 +253,21 @@
registerFrameData(mListener, work->input);
}
}
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ return ScopedAStatus::fromServiceSpecificError(err);
+ }
+ }
+ return ScopedAStatus::ok();
+ }
- c2_status_t err = mComponent->queue_nb(&c2works);
+ err = mComponent->queue_nb(&c2works);
if (err == C2_OK) {
return ScopedAStatus::ok();
}
@@ -192,7 +279,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
-
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
if (work) {
@@ -289,30 +378,22 @@
const IComponent::BlockPoolAllocator &allocator,
IComponent::BlockPool *blockPool) {
std::shared_ptr<C2BlockPool> c2BlockPool;
- static constexpr IComponent::BlockPoolAllocator::Tag ALLOCATOR_ID =
- IComponent::BlockPoolAllocator::allocatorId;
- static constexpr IComponent::BlockPoolAllocator::Tag IGBA =
- IComponent::BlockPoolAllocator::allocator;
c2_status_t status = C2_OK;
::android::C2PlatformAllocatorDesc allocatorParam;
- switch (allocator.getTag()) {
- case ALLOCATOR_ID: {
- allocatorParam.allocatorId =
- allocator.get<IComponent::BlockPoolAllocator::allocatorId>();
- }
- break;
- case IGBA: {
- allocatorParam.allocatorId = ::android::C2PlatformAllocatorStore::IGBA;
- allocatorParam.igba =
- allocator.get<IComponent::BlockPoolAllocator::allocator>().igba;
+ allocatorParam.allocatorId = allocator.allocatorId;
+ switch (allocator.allocatorId) {
+ case ::android::C2PlatformAllocatorStore::IGBA: {
+ allocatorParam.igba = allocator.gbAllocator->igba;
allocatorParam.waitableFd.reset(
- allocator.get<IComponent::BlockPoolAllocator::allocator>()
- .waitableFd.dup().release());
+ allocator.gbAllocator->waitableFd.dup().release());
}
break;
- default:
- return ScopedAStatus::fromServiceSpecificError(C2_CORRUPTED);
+ default: {
+ // no-op
+ }
+ break;
}
+
#ifdef __ANDROID_APEX__
status = ::android::CreateCodec2BlockPool(
allocatorParam,
@@ -370,6 +451,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
@@ -383,6 +467,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
@@ -403,15 +490,38 @@
return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
}
+ScopedAStatus Component::connectToInputSurface(
+ const std::shared_ptr<IInputSurface>& inputSurface,
+ std::shared_ptr<IInputSurfaceConnection> *connection) {
+ // TODO
+ (void)inputSurface;
+ (void)connection;
+ return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
+}
+
+ScopedAStatus Component::asInputSink(
+ std::shared_ptr<IInputSink> *sink) {
+ // TODO
+ (void)sink;
+ return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
+}
+
void Component::initListener(const std::shared_ptr<Component>& self) {
if (__builtin_available(android __ANDROID_API_T__, *)) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
mInit = res;
}
+ // b/321902635, mListener should not be null.
+ CHECK(mListener);
mDeathRecipient = ::ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(OnBinderDied));
mDeathContext = new DeathContext{ref<Component>()};
diff --git a/media/codec2/hal/aidl/ComponentInterface.cpp b/media/codec2/hal/aidl/ComponentInterface.cpp
index 2d812c9..1f0534d 100644
--- a/media/codec2/hal/aidl/ComponentInterface.cpp
+++ b/media/codec2/hal/aidl/ComponentInterface.cpp
@@ -24,6 +24,8 @@
#include <utils/Timers.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -43,9 +45,10 @@
// Implementation of ConfigurableC2Intf based on C2ComponentInterface
struct CompIntf : public ConfigurableC2Intf {
- CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+ CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
ConfigurableC2Intf{intf->getName(), intf->getId()},
- mIntf{intf} {
+ mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
}
virtual c2_status_t config(
@@ -53,7 +56,34 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures
) override {
- return mIntf->config_vb(params, mayBlock, failures);
+ std::vector<C2Param*> paramsToIntf;
+ std::vector<C2Param*> paramsToLargeFrameIntf;
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->config_vb(params, mayBlock, failures);
+ return err;
+ }
+ for (auto &p : params) {
+ if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+ paramsToLargeFrameIntf.push_back(p);
+ } else {
+ paramsToIntf.push_back(p);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+ }
+ if (err1 != C2_OK) {
+ LOG(ERROR) << "We have a failed config";
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->config(
+ paramsToLargeFrameIntf, mayBlock, failures);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t query(
@@ -61,23 +91,56 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const params
) const override {
- return mIntf->query_vb({}, indices, mayBlock, params);
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->query_vb({}, indices, mayBlock, params);
+ return err;
+ }
+ std::vector<C2Param::Index> paramsToIntf;
+ std::vector<C2Param::Index> paramsToLargeFrameIntf;
+ for (auto &i : indices) {
+ if (mMultiAccessUnitIntf->isParamSupported(i)) {
+ paramsToLargeFrameIntf.push_back(i);
+ } else {
+ paramsToIntf.push_back(i);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->query(
+ {}, paramsToLargeFrameIntf, mayBlock, params);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t querySupportedParams(
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
) const override {
- return mIntf->querySupportedParams_nb(params);
+ c2_status_t err = mIntf->querySupportedParams_nb(params);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedParams(params);
+ }
+ return err;
}
virtual c2_status_t querySupportedValues(
std::vector<C2FieldSupportedValuesQuery>& fields,
c2_blocking_t mayBlock) const override {
- return mIntf->querySupportedValues_vb(fields, mayBlock);
+ c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+ }
+ return err;
}
protected:
std::shared_ptr<C2ComponentInterface> mIntf;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
};
} // unnamed namespace
@@ -85,10 +148,16 @@
// ComponentInterface
ComponentInterface::ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
const std::shared_ptr<ParameterCache>& cache)
: mInterface{intf},
mConfigurable{SharedRefBase::make<CachedConfigurable>(
- std::make_unique<CompIntf>(intf))} {
+ std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
mInit = mConfigurable->init(cache);
}
diff --git a/media/codec2/hal/aidl/ComponentStore.cpp b/media/codec2/hal/aidl/ComponentStore.cpp
index 58407d1..3f687b5 100644
--- a/media/codec2/hal/aidl/ComponentStore.cpp
+++ b/media/codec2/hal/aidl/ComponentStore.cpp
@@ -210,6 +210,8 @@
c2_status_t status =
mStore->createComponent(name, &c2component);
+ ALOGD("createComponent(): listener(%d)", bool(listener));
+
if (status == C2_OK) {
#ifndef __ANDROID_APEX__
c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
@@ -218,7 +220,8 @@
std::shared_ptr<Component> comp =
SharedRefBase::make<Component>(c2component, listener, ref<ComponentStore>(), pool);
*component = comp;
- if (!component) {
+ if (!component || !comp) {
+ ALOGE("createComponent(): component cannot be returned");
status = C2_CORRUPTED;
} else {
reportComponentBirth(comp.get());
@@ -273,6 +276,13 @@
return ScopedAStatus::ok();
}
+ScopedAStatus ComponentStore::createInputSurface(
+ std::shared_ptr<IInputSurface> *inputSurface) {
+ // TODO
+ (void)inputSurface;
+ return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
+}
+
void ComponentStore::onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf) {
// invalidate unsupported struct descriptors if a new interface is loaded as it may have
// exposed new descriptors
diff --git a/media/codec2/hal/aidl/Configurable.cpp b/media/codec2/hal/aidl/Configurable.cpp
index 0326263..2daaac2 100644
--- a/media/codec2/hal/aidl/Configurable.cpp
+++ b/media/codec2/hal/aidl/Configurable.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
+#include <android-base/hex.h>
#include <codec2/aidl/Configurable.h>
#include <codec2/aidl/ParamTypes.h>
@@ -61,7 +62,7 @@
ScopedAStatus CachedConfigurable::query(
const std::vector<int32_t>& indices,
bool mayBlock,
- Params* params) {
+ QueryResult *queryResult) {
typedef C2Param::Index Index;
std::vector<Index> c2heapParamIndices(
(Index*)indices.data(),
@@ -72,13 +73,11 @@
mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
&c2heapParams);
- if (!CreateParamsBlob(params, c2heapParams)) {
+ if (!CreateParamsBlob(&(queryResult->params), c2heapParams)) {
LOG(WARNING) << "query -- invalid output params.";
}
- if (c2res == C2_OK) {
- return ScopedAStatus::ok();
- }
- return ScopedAStatus::fromServiceSpecificError(c2res);
+ queryResult->status.status = c2res;
+ return ScopedAStatus::ok();
}
ScopedAStatus CachedConfigurable::config(
@@ -115,10 +114,8 @@
if (!CreateParamsBlob(&result->params, c2params)) {
LOG(DEBUG) << "config -- invalid output params.";
}
- if (c2res == C2_OK) {
- return ScopedAStatus::ok();
- }
- return ScopedAStatus::fromServiceSpecificError(c2res);
+ result->status.status = c2res;
+ return ScopedAStatus::ok();
}
ScopedAStatus CachedConfigurable::querySupportedParams(
@@ -139,8 +136,6 @@
LOG(WARNING) << "querySupportedParams -- invalid output params.";
break;
}
- } else {
- res = Status::BAD_INDEX;
}
}
paramDesc->resize(dstIx);
@@ -153,7 +148,7 @@
ScopedAStatus CachedConfigurable::querySupportedValues(
const std::vector<FieldSupportedValuesQuery>& fields,
bool mayBlock,
- std::vector<FieldSupportedValuesQueryResult>* result) {
+ QuerySupportedValuesResult *queryValues) {
std::vector<C2FieldSupportedValuesQuery> c2fields;
{
// C2FieldSupportedValuesQuery objects are restricted in that some
@@ -173,22 +168,20 @@
c2_status_t c2res = mIntf->querySupportedValues(
c2fields,
mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK);
- result->resize(fields.size());
+ queryValues->values.resize(fields.size());
size_t dstIx = 0;
for (const C2FieldSupportedValuesQuery &res : c2fields) {
- if (ToAidl(&(*result)[dstIx], res)) {
+ if (ToAidl(&(queryValues->values[dstIx]), res)) {
++dstIx;
} else {
- result->resize(dstIx);
+ queryValues->values.resize(dstIx);
c2res = C2_CORRUPTED;
LOG(WARNING) << "querySupportedValues -- invalid output params.";
break;
}
}
- if (c2res == C2_OK) {
- return ScopedAStatus::ok();
- }
- return ScopedAStatus::fromServiceSpecificError(c2res);
+ queryValues->status.status = c2res;
+ return ScopedAStatus::ok();
}
} // namespace utils
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/Component.h b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
index 4a090e9..9725bcf 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/Component.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
@@ -27,6 +27,11 @@
#include <aidl/android/hardware/media/c2/IComponentInterface.h>
#include <aidl/android/hardware/media/c2/IComponentListener.h>
#include <aidl/android/hardware/media/c2/IComponentStore.h>
+#include <aidl/android/hardware/media/c2/IInputSink.h>
+#include <aidl/android/hardware/media/c2/IInputSurface.h>
+#include <aidl/android/hardware/media/c2/IInputSurfaceConnection.h>
+
+#include <codec2/common/MultiAccessUnitHelper.h>
#include <C2Component.h>
#include <C2Buffer.h>
@@ -43,6 +48,8 @@
namespace c2 {
namespace utils {
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -71,12 +78,19 @@
::ndk::ScopedAStatus configureVideoTunnel(
int32_t avSyncHwId,
common::NativeHandle* handle) override;
+ ::ndk::ScopedAStatus connectToInputSurface(
+ const std::shared_ptr<IInputSurface>& inputSurface,
+ std::shared_ptr<IInputSurfaceConnection> *connection) override;
+ ::ndk::ScopedAStatus asInputSink(
+ std::shared_ptr<IInputSink> *sink) override;
protected:
c2_status_t mInit;
std::shared_ptr<C2Component> mComponent;
std::shared_ptr<ComponentInterface> mInterface;
std::shared_ptr<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
std::shared_ptr<ComponentStore> mStore;
DefaultBufferPoolSender mBufferPoolSender;
@@ -94,6 +108,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
static void OnBinderDied(void *cookie);
static void OnBinderUnlinked(void *cookie);
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
index 7723bee..bd19cd6 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
@@ -22,11 +22,14 @@
#include <aidl/android/hardware/media/c2/BnComponentInterface.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
#include <memory>
+#include <set>
namespace aidl {
namespace android {
@@ -35,12 +38,16 @@
namespace c2 {
namespace utils {
-struct ComponentStore;
+using ::android::MultiAccessUnitInterface;
struct ComponentInterface : public BnComponentInterface {
ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
const std::shared_ptr<ParameterCache>& cache);
+ ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+ const std::shared_ptr<ParameterCache>& cache);
c2_status_t status() const;
::ndk::ScopedAStatus getConfigurable(
std::shared_ptr<IConfigurable> *intf) override;
@@ -51,7 +58,6 @@
c2_status_t mInit;
};
-
} // namespace utils
} // namespace c2
} // namespace media
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/ComponentStore.h b/media/codec2/hal/aidl/include/codec2/aidl/ComponentStore.h
index b3c97d5..0698b0f 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/ComponentStore.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/ComponentStore.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/media/bufferpool2/IClientManager.h>
#include <aidl/android/hardware/media/c2/BnComponentStore.h>
+#include <aidl/android/hardware/media/c2/IInputSurface.h>
#include <C2Component.h>
#include <C2Param.h>
@@ -85,6 +86,8 @@
std::shared_ptr<IComponentInterface> *intf) override;
virtual ::ndk::ScopedAStatus listComponents(
std::vector<IComponentStore::ComponentTraits>* traits) override;
+ virtual ::ndk::ScopedAStatus createInputSurface(
+ std::shared_ptr<IInputSurface> *inputSurface) override;
virtual ::ndk::ScopedAStatus getStructDescriptors(
const std::vector<int32_t>& indices,
std::vector<StructDescriptor> *descs) override;
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/Configurable.h b/media/codec2/hal/aidl/include/codec2/aidl/Configurable.h
index 6cc2c1b..96d3516 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/Configurable.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/Configurable.h
@@ -111,7 +111,7 @@
virtual ::ndk::ScopedAStatus query(
const std::vector<int32_t>& indices,
bool mayBlock,
- Params* params) override;
+ QueryResult* result) override;
virtual ::ndk::ScopedAStatus config(
const ::aidl::android::hardware::media::c2::Params& params,
@@ -126,7 +126,7 @@
virtual ::ndk::ScopedAStatus querySupportedValues(
const std::vector<FieldSupportedValuesQuery>& fields,
bool mayBlock,
- std::vector<FieldSupportedValuesQueryResult>* result) override;
+ QuerySupportedValuesResult* result) override;
protected:
// Common Codec2.0 interface wrapper
diff --git a/media/codec2/hal/client/Android.bp b/media/codec2/hal/client/Android.bp
index 0b5b940..af6f4ae 100644
--- a/media/codec2/hal/client/Android.bp
+++ b/media/codec2/hal/client/Android.bp
@@ -43,7 +43,7 @@
"android.hardware.media.c2@1.0",
"android.hardware.media.c2@1.1",
"android.hardware.media.c2@1.2",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.c2-V1-ndk",
"libbase",
"libbinder",
diff --git a/media/codec2/hal/client/GraphicsTracker.cpp b/media/codec2/hal/client/GraphicsTracker.cpp
index 0848fc6..01b0678 100644
--- a/media/codec2/hal/client/GraphicsTracker.cpp
+++ b/media/codec2/hal/client/GraphicsTracker.cpp
@@ -174,20 +174,16 @@
GraphicsTracker::GraphicsTracker(int maxDequeueCount)
: mBufferCache(new BufferCache()), mMaxDequeue{maxDequeueCount},
- mMaxDequeueRequested{maxDequeueCount},
mMaxDequeueCommitted{maxDequeueCount},
- mMaxDequeueRequestedSeqId{0UL}, mMaxDequeueCommittedSeqId{0ULL},
mDequeueable{maxDequeueCount},
mTotalDequeued{0}, mTotalCancelled{0}, mTotalDropped{0}, mTotalReleased{0},
mInConfig{false}, mStopped{false} {
if (maxDequeueCount < kMaxDequeueMin) {
mMaxDequeue = kMaxDequeueMin;
- mMaxDequeueRequested = kMaxDequeueMin;
mMaxDequeueCommitted = kMaxDequeueMin;
mDequeueable = kMaxDequeueMin;
} else if(maxDequeueCount > kMaxDequeueMax) {
mMaxDequeue = kMaxDequeueMax;
- mMaxDequeueRequested = kMaxDequeueMax;
mMaxDequeueCommitted = kMaxDequeueMax;
mDequeueable = kMaxDequeueMax;
}
@@ -197,34 +193,36 @@
mReadPipeFd.reset(pipefd[0]);
mWritePipeFd.reset(pipefd[1]);
- mEventQueueThread = std::thread([this](){processEvent();});
- writeIncDequeueable(mDequeueable);
+ // ctor does not require lock to be held.
+ writeIncDequeueableLocked(mDequeueable);
CHECK(ret >= 0);
- CHECK(mEventQueueThread.joinable());
}
GraphicsTracker::~GraphicsTracker() {
stop();
- if (mEventQueueThread.joinable()) {
- mEventQueueThread.join();
- }
}
bool GraphicsTracker::adjustDequeueConfLocked(bool *updateDequeue) {
// TODO: can't we adjust during config? not committing it may safe?
*updateDequeue = false;
- if (!mInConfig && mMaxDequeueRequested < mMaxDequeue) {
- int delta = mMaxDequeue - mMaxDequeueRequested;
+ if (!mInConfig && mMaxDequeueRequested.has_value() && mMaxDequeueRequested < mMaxDequeue) {
+ int delta = mMaxDequeue - mMaxDequeueRequested.value();
+ int drained = 0;
// Since we are supposed to increase mDequeuable by one already
int adjustable = mDequeueable + 1;
if (adjustable >= delta) {
- mMaxDequeue = mMaxDequeueRequested;
+ mMaxDequeue = mMaxDequeueRequested.value();
mDequeueable -= (delta - 1);
+ drained = delta - 1;
} else {
mMaxDequeue -= adjustable;
+ drained = mDequeueable;
mDequeueable = 0;
}
+ if (drained > 0) {
+ drainDequeueableLocked(drained);
+ }
if (mMaxDequeueRequested == mMaxDequeue && mMaxDequeueRequested != mMaxDequeueCommitted) {
*updateDequeue = true;
}
@@ -235,6 +233,7 @@
c2_status_t GraphicsTracker::configureGraphics(
const sp<IGraphicBufferProducer>& igbp, uint32_t generation) {
+ // TODO: wait until operations to previous IGBP is completed.
std::shared_ptr<BufferCache> prevCache;
int prevDequeueCommitted;
@@ -254,14 +253,28 @@
if (igbp) {
ret = igbp->getUniqueId(&bqId);
}
- if (ret != ::android::OK || prevCache->mGeneration == generation || prevCache->mBqId == bqId) {
+ if (ret != ::android::OK ||
+ prevCache->mGeneration == generation) {
+ ALOGE("new surface configure fail due to wrong or same bqId or same generation:"
+ "igbp(%d:%llu -> %llu), gen(%lu -> %lu)", (bool)igbp,
+ (unsigned long long)prevCache->mBqId, (unsigned long long)bqId,
+ (unsigned long)prevCache->mGeneration, (unsigned long)generation);
+ std::unique_lock<std::mutex> l(mLock);
+ mInConfig = false;
return C2_BAD_VALUE;
}
- ret = igbp->setMaxDequeuedBufferCount(prevDequeueCommitted);
- if (ret != ::android::OK) {
- // TODO: sort out the error from igbp and return an error accordingly.
- return C2_CORRUPTED;
+ if (igbp) {
+ ret = igbp->setMaxDequeuedBufferCount(prevDequeueCommitted);
+ if (ret != ::android::OK) {
+ ALOGE("new surface maxDequeueBufferCount configure fail");
+ // TODO: sort out the error from igbp and return an error accordingly.
+ std::unique_lock<std::mutex> l(mLock);
+ mInConfig = false;
+ return C2_CORRUPTED;
+ }
}
+ ALOGD("new surface configured with id:%llu gen:%lu maxDequeue:%d",
+ (unsigned long long)bqId, (unsigned long)generation, prevDequeueCommitted);
std::shared_ptr<BufferCache> newCache = std::make_shared<BufferCache>(bqId, generation, igbp);
{
std::unique_lock<std::mutex> l(mLock);
@@ -283,59 +296,74 @@
// (Sometimes maxDequeueCount cannot be committed if the number of
// dequeued buffer count is bigger.)
int maxDequeueToCommit;
- // max dequeue count which is committed to IGBP currently
- // (actually mMaxDequeueCommitted, but needs to be read outside lock.)
- int curMaxDequeueCommitted;
std::unique_lock<std::mutex> cl(mConfigLock);
{
std::unique_lock<std::mutex> l(mLock);
- if (mMaxDequeueRequested == maxDequeueCount) {
+ if (mMaxDequeueRequested.has_value()) {
+ if (mMaxDequeueRequested == maxDequeueCount) {
+ ALOGD("maxDequeueCount requested with %d already", maxDequeueCount);
+ return C2_OK;
+ }
+ } else if (mMaxDequeue == maxDequeueCount) {
+ ALOGD("maxDequeueCount is already %d", maxDequeueCount);
return C2_OK;
}
mInConfig = true;
mMaxDequeueRequested = maxDequeueCount;
cache = mBufferCache;
- curMaxDequeueCommitted = mMaxDequeueCommitted;
if (mMaxDequeue <= maxDequeueCount) {
maxDequeueToCommit = maxDequeueCount;
} else {
// Since mDequeuable is decreasing,
// a delievered ready to allocate event may not be fulfilled.
// Another waiting via a waitable object may be necessary in the case.
- int delta = mMaxDequeue - maxDequeueCount;
- if (delta <= mDequeueable) {
- maxDequeueToCommit = maxDequeueCount;
- mDequeueable -= delta;
- } else {
- maxDequeueToCommit = mMaxDequeue - mDequeueable;
- mDequeueable = 0;
+ int delta = std::min(mMaxDequeue - maxDequeueCount, mDequeueable);
+ maxDequeueToCommit = mMaxDequeue - delta;
+ mDequeueable -= delta;
+ if (delta > 0) {
+ drainDequeueableLocked(delta);
}
}
}
bool committed = true;
- if (cache->mIgbp && maxDequeueToCommit != curMaxDequeueCommitted) {
+ if (cache->mIgbp && maxDequeueToCommit != mMaxDequeueCommitted) {
::android::status_t ret = cache->mIgbp->setMaxDequeuedBufferCount(maxDequeueToCommit);
committed = (ret == ::android::OK);
- if (!committed) {
+ if (committed) {
+ ALOGD("maxDequeueCount committed to IGBP: %d", maxDequeueToCommit);
+ } else {
// This should not happen.
- ALOGE("dequeueCount failed with error(%d)", (int)ret);
+ ALOGE("maxdequeueCount update to IGBP failed with error(%d)", (int)ret);
}
}
+ int oldMaxDequeue = 0;
+ int requested = 0;
{
std::unique_lock<std::mutex> l(mLock);
mInConfig = false;
+ oldMaxDequeue = mMaxDequeue;
+ mMaxDequeue = maxDequeueToCommit; // we already drained dequeueable
if (committed) {
+ clearCacheIfNecessaryLocked(cache, maxDequeueToCommit);
mMaxDequeueCommitted = maxDequeueToCommit;
- int delta = mMaxDequeueCommitted - mMaxDequeue;
+ if (mMaxDequeueRequested == mMaxDequeueCommitted &&
+ mMaxDequeueRequested == mMaxDequeue) {
+ mMaxDequeueRequested.reset();
+ }
+ if (mMaxDequeueRequested.has_value()) {
+ requested = mMaxDequeueRequested.value();
+ }
+ int delta = mMaxDequeueCommitted - oldMaxDequeue;
if (delta > 0) {
mDequeueable += delta;
- l.unlock();
- writeIncDequeueable(delta);
+ writeIncDequeueableLocked(delta);
}
}
}
+ ALOGD("maxDqueueCount change %d -> %d: pending: %d",
+ oldMaxDequeue, maxDequeueToCommit, requested);
if (!committed) {
return C2_CORRUPTED;
@@ -350,48 +378,60 @@
std::unique_lock<std::mutex> cl(mConfigLock);
{
std::unique_lock<std::mutex> l(mLock);
- if (mMaxDequeue == mMaxDequeueRequested && mMaxDequeueCommitted != mMaxDequeueRequested) {
- dequeueCommit = mMaxDequeue;
- mInConfig = true;
- cache = mBufferCache;
- } else {
+ if (!mMaxDequeueRequested.has_value() || mMaxDequeue != mMaxDequeueRequested) {
return;
}
+ if (mMaxDequeueCommitted == mMaxDequeueRequested) {
+ // already committed. may not happen.
+ mMaxDequeueRequested.reset();
+ return;
+ }
+ dequeueCommit = mMaxDequeue;
+ mInConfig = true;
+ cache = mBufferCache;
}
bool committed = true;
if (cache->mIgbp) {
::android::status_t ret = cache->mIgbp->setMaxDequeuedBufferCount(dequeueCommit);
committed = (ret == ::android::OK);
- if (!committed) {
+ if (committed) {
+ ALOGD("delayed maxDequeueCount update to IGBP: %d", dequeueCommit);
+ } else {
// This should not happen.
- ALOGE("dequeueCount failed with error(%d)", (int)ret);
+ ALOGE("delayed maxdequeueCount update to IGBP failed with error(%d)", (int)ret);
}
}
- int cleared = 0;
{
// cache == mCache here, since we locked config.
std::unique_lock<std::mutex> l(mLock);
mInConfig = false;
if (committed) {
- if (cache->mIgbp && dequeueCommit < mMaxDequeueCommitted) {
- // we are shrinking # of buffers, so clearing the cache.
- for (auto it = cache->mBuffers.begin(); it != cache->mBuffers.end();) {
- uint64_t bid = it->second->mId;
- if (mDequeued.count(bid) == 0 || mDeallocating.count(bid) > 0) {
- ++cleared;
- it = cache->mBuffers.erase(it);
- } else {
- ++it;
- }
- }
- }
+ clearCacheIfNecessaryLocked(cache, dequeueCommit);
mMaxDequeueCommitted = dequeueCommit;
}
+ mMaxDequeueRequested.reset();
}
- if (cleared > 0) {
- ALOGD("%d buffers are cleared from cache, due to IGBP capacity change", cleared);
- }
+}
+void GraphicsTracker::clearCacheIfNecessaryLocked(const std::shared_ptr<BufferCache> &cache,
+ int maxDequeueCommitted) {
+ int cleared = 0;
+ size_t origCacheSize = cache->mBuffers.size();
+ if (cache->mIgbp && maxDequeueCommitted < mMaxDequeueCommitted) {
+ // we are shrinking # of buffers in the case, so evict the previous
+ // cached buffers.
+ for (auto it = cache->mBuffers.begin(); it != cache->mBuffers.end();) {
+ uint64_t bid = it->second->mId;
+ if (mDequeued.count(bid) == 0 || mDeallocating.count(bid) > 0) {
+ ++cleared;
+ it = cache->mBuffers.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ ALOGD("Cache size %zu -> %zu: maybe_cleared(%d), dequeued(%zu)",
+ origCacheSize, cache->mBuffers.size(), cleared, mDequeued.size());
}
int GraphicsTracker::getCurDequeueable() {
@@ -400,70 +440,58 @@
}
void GraphicsTracker::stop() {
- bool expected = false;
- std::unique_lock<std::mutex> l(mEventLock);
- bool updated = mStopped.compare_exchange_strong(expected, true);
- if (updated) {
- int writeFd = mWritePipeFd.release();
+ // TODO: wait until all operation to current IGBP
+ // being completed.
+ std::unique_lock<std::mutex> l(mLock);
+ if (mStopped) {
+ return;
+ }
+ mStopped = true;
+ int writeFd = mWritePipeFd.release();
+ if (writeFd >= 0) {
::close(writeFd);
- int readFd = mReadPipeFd.release();
- ::close(readFd);
- mEventCv.notify_one();
}
}
-void GraphicsTracker::writeIncDequeueable(int inc) {
+void GraphicsTracker::writeIncDequeueableLocked(int inc) {
CHECK(inc > 0 && inc < kMaxDequeueMax);
thread_local char buf[kMaxDequeueMax];
- int diff = 0;
- {
- std::unique_lock<std::mutex> l(mEventLock);
- if (mStopped) {
- return;
- }
- CHECK(mWritePipeFd.get() >= 0);
- int ret = ::write(mWritePipeFd.get(), buf, inc);
- if (ret == inc) {
- return;
- }
- diff = ret < 0 ? inc : inc - ret;
-
- // Partial write or EINTR. This will not happen in a real scenario.
- mIncDequeueable += diff;
- if (mIncDequeueable > 0) {
- l.unlock();
- mEventCv.notify_one();
- ALOGW("updating dequeueable to pipefd pending");
- }
+ if (mStopped) { // reading end closed;
+ return;
}
+ int writeFd = mWritePipeFd.get();
+ if (writeFd < 0) {
+ // initialization fail and not valid though.
+ return;
+ }
+ int ret = ::write(writeFd, buf, inc);
+ // Since this is non-blocking i/o, it never returns EINTR.
+ //
+ // ::write() to pipe guarantee to succeed atomically if it writes less than
+ // the given PIPE_BUF. And the buffer size in pipe/fifo is at least 4K and our total
+ // max pending buffer size is 64. So it never returns EAGAIN here either.
+ // See pipe(7) for further information.
+ //
+ // Other errors are serious errors and we cannot synchronize mDequeueable to
+ // length of pending buffer in pipe/fifo anymore. So better to abort here.
+ // TODO: do not abort here. (b/318717399)
+ CHECK(ret == inc);
}
-void GraphicsTracker::processEvent() {
- // This is for partial/failed writes to the writing end.
- // This may not happen in the real scenario.
+void GraphicsTracker::drainDequeueableLocked(int dec) {
+ CHECK(dec > 0 && dec < kMaxDequeueMax);
thread_local char buf[kMaxDequeueMax];
- while (true) {
- std::unique_lock<std::mutex> l(mEventLock);
- if (mStopped) {
- break;
- }
- if (mIncDequeueable > 0) {
- int inc = mIncDequeueable > kMaxDequeueMax ? kMaxDequeueMax : mIncDequeueable;
- int ret = ::write(mWritePipeFd.get(), buf, inc);
- int written = ret <= 0 ? 0 : ret;
- mIncDequeueable -= written;
- if (mIncDequeueable > 0) {
- l.unlock();
- if (ret < 0) {
- ALOGE("write to writing end failed %d", errno);
- } else {
- ALOGW("partial write %d(%d)", inc, written);
- }
- continue;
- }
- }
- mEventCv.wait(l);
+ if (mStopped) {
+ return;
}
+ int readFd = mReadPipeFd.get();
+ if (readFd < 0) {
+ // initializationf fail and not valid though.
+ return;
+ }
+ int ret = ::read(readFd, buf, dec);
+ // TODO: no dot abort here. (b/318717399)
+ CHECK(ret == dec);
}
c2_status_t GraphicsTracker::getWaitableFd(int *pipeFd) {
@@ -539,8 +567,7 @@
return;
}
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
}
}
@@ -715,14 +742,13 @@
return C2_OK;
}
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
}
return C2_OK;
}
void GraphicsTracker::commitDeallocate(
- std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid) {
+ std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid, bool *updateDequeue) {
std::unique_lock<std::mutex> l(mLock);
size_t del1 = mDequeued.erase(bid);
size_t del2 = mDeallocating.erase(bid);
@@ -730,9 +756,11 @@
if (cache) {
cache->unblockSlot(slotId);
}
+ if (adjustDequeueConfLocked(updateDequeue)) {
+ return;
+ }
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
}
@@ -758,7 +786,10 @@
// cache->mIgbp is not null, if completed is false.
(void)cache->mIgbp->cancelBuffer(slotId, rFence);
- commitDeallocate(cache, slotId, bid);
+ commitDeallocate(cache, slotId, bid, &updateDequeue);
+ if (updateDequeue) {
+ updateDequeueConf();
+ }
return C2_OK;
}
@@ -785,8 +816,7 @@
return C2_BAD_STATE;
}
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
return C2_BAD_STATE;
}
std::shared_ptr<BufferItem> buffer = it->second;
@@ -828,8 +858,7 @@
return;
}
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
return;
}
}
@@ -843,6 +872,9 @@
ALOGE("retrieving AHB-ID for GraphicBlock failed");
return C2_CORRUPTED;
}
+ std::shared_ptr<_C2BlockPoolData> poolData =
+ _C2BlockFactory::GetGraphicBlockPoolData(blk);
+ _C2BlockFactory::DisownIgbaBlock(poolData);
std::shared_ptr<BufferCache> cache;
std::shared_ptr<BufferItem> buffer;
std::shared_ptr<BufferItem> oldBuffer;
@@ -870,13 +902,19 @@
if (!gb) {
ALOGE("render: realloc-ing a new buffer for migration failed");
std::shared_ptr<BufferCache> nullCache;
- commitDeallocate(nullCache, -1, bid);
+ commitDeallocate(nullCache, -1, bid, &updateDequeue);
+ if (updateDequeue) {
+ updateDequeueConf();
+ }
return C2_REFUSED;
}
if (cache->mIgbp->attachBuffer(&(newBuffer->mSlot), gb) != ::android::OK) {
ALOGE("render: attaching a new buffer to IGBP failed");
std::shared_ptr<BufferCache> nullCache;
- commitDeallocate(nullCache, -1, bid);
+ commitDeallocate(nullCache, -1, bid, &updateDequeue);
+ if (updateDequeue) {
+ updateDequeueConf();
+ }
return C2_REFUSED;
}
cache->waitOnSlot(newBuffer->mSlot);
@@ -890,11 +928,13 @@
CHECK(renderRes != ::android::BAD_VALUE);
ALOGE("render: failed to queueBuffer() err = %d", renderRes);
(void) cache->mIgbp->cancelBuffer(buffer->mSlot, input.fence);
- commitDeallocate(cache, buffer->mSlot, bid);
+ commitDeallocate(cache, buffer->mSlot, bid, &updateDequeue);
+ if (updateDequeue) {
+ updateDequeueConf();
+ }
return C2_REFUSED;
}
- updateDequeue = false;
commitRender(cache, buffer, oldBuffer, output->bufferReplaced, &updateDequeue);
if (updateDequeue) {
updateDequeueConf();
@@ -909,8 +949,7 @@
if (mBufferCache->mGeneration == generation) {
if (!adjustDequeueConfLocked(&updateDequeue)) {
mDequeueable++;
- l.unlock();
- writeIncDequeueable(1);
+ writeIncDequeueableLocked(1);
}
}
}
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 2d19ecc..9ed9458 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -635,21 +635,22 @@
if (heapParams) {
heapParams->reserve(heapParams->size() + numIndices);
}
- c2_aidl::Params result;
+ c2_aidl::IConfigurable::QueryResult result;
ndk::ScopedAStatus transStatus = mBase->query(indices, (mayBlock == C2_MAY_BLOCK), &result);
c2_status_t status = GetC2Status(transStatus, "query");
if (status != C2_OK) {
return status;
}
+ status = static_cast<c2_status_t>(result.status.status);
std::vector<C2Param*> paramPointers;
- if (!c2_aidl::utils::ParseParamsBlob(¶mPointers, result)) {
+ if (!c2_aidl::utils::ParseParamsBlob(¶mPointers, result.params)) {
LOG(ERROR) << "query -- error while parsing params.";
return C2_CORRUPTED;
}
size_t i = 0;
- for (auto it = paramPointers.begin();
- it != paramPointers.end(); ) {
+ size_t numUpdatedStackParams = 0;
+ for (auto it = paramPointers.begin(); it != paramPointers.end(); ) {
C2Param* paramPointer = *it;
if (numStackIndices > 0) {
--numStackIndices;
@@ -676,7 +677,9 @@
status = C2_BAD_INDEX;
continue;
}
- if (!stackParams[i++]->updateFrom(*paramPointer)) {
+ if (stackParams[i++]->updateFrom(*paramPointer)) {
+ ++numUpdatedStackParams;
+ } else {
LOG(WARNING) << "query -- param update failed: "
"index = "
<< paramPointer->index() << ".";
@@ -696,6 +699,13 @@
}
++it;
}
+ size_t numQueried = numUpdatedStackParams;
+ if (heapParams) {
+ numQueried += heapParams->size();
+ }
+ if (status == C2_OK && indices.size() != numQueried) {
+ status = C2_BAD_INDEX;
+ }
return status;
}
@@ -714,6 +724,7 @@
if (status != C2_OK) {
return status;
}
+ status = static_cast<c2_status_t>(result.status.status);
size_t i = failures->size();
failures->resize(i + result.failures.size());
for (const c2_aidl::SettingResult& sf : result.failures) {
@@ -764,21 +775,23 @@
}
}
- std::vector<c2_aidl::FieldSupportedValuesQueryResult> result;
+ c2_aidl::IConfigurable::QuerySupportedValuesResult result;
+
ndk::ScopedAStatus transStatus = mBase->querySupportedValues(
inFields, (mayBlock == C2_MAY_BLOCK), &result);
c2_status_t status = GetC2Status(transStatus, "querySupportedValues");
if (status != C2_OK) {
return status;
}
- if (result.size() != fields.size()) {
+ status = static_cast<c2_status_t>(result.status.status);
+ if (result.values.size() != fields.size()) {
LOG(ERROR) << "querySupportedValues -- "
"input and output lists "
"have different sizes.";
return C2_CORRUPTED;
}
for (size_t i = 0; i < fields.size(); ++i) {
- if (!c2_aidl::utils::FromAidl(&fields[i], inFields[i], result[i])) {
+ if (!c2_aidl::utils::FromAidl(&fields[i], inFields[i], result.values[i])) {
LOG(ERROR) << "querySupportedValues -- "
"invalid returned value.";
return C2_CORRUPTED;
@@ -2059,6 +2072,8 @@
id = id == C2PlatformAllocatorStore::BUFFERQUEUE ?
C2PlatformAllocatorStore::IGBA : id;
+ c2_aidl::IComponent::BlockPoolAllocator allocator;
+ allocator.allocatorId = id;
if (id == C2PlatformAllocatorStore::IGBA) {
std::shared_ptr<AidlGraphicBufferAllocator> gba =
mGraphicBufferAllocators->create();
@@ -2068,12 +2083,11 @@
if (status != C2_OK) {
return status;
}
- c2_aidl::IComponent::BlockPoolAllocator allocator;
- allocator.set<c2_aidl::IComponent::BlockPoolAllocator::allocator>();
- allocator.get<c2_aidl::IComponent::BlockPoolAllocator::allocator>().igba =
+ c2_aidl::IComponent::GbAllocator gbAllocator;
+ gbAllocator.waitableFd = std::move(waitableFd);
+ gbAllocator.igba =
c2_aidl::IGraphicBufferAllocator::fromBinder(gba->asBinder());
- allocator.get<c2_aidl::IComponent::BlockPoolAllocator::allocator>().waitableFd =
- std::move(waitableFd);
+ allocator.gbAllocator = std::move(gbAllocator);
::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool(
allocator, &aidlBlockPool);
status = GetC2Status(transStatus, "createBlockPool");
@@ -2083,7 +2097,7 @@
mGraphicBufferAllocators->setCurrentId(aidlBlockPool.blockPoolId);
} else {
::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool(
- static_cast<int32_t>(id), &aidlBlockPool);
+ allocator, &aidlBlockPool);
status = GetC2Status(transStatus, "createBlockPool");
if (status != C2_OK) {
return status;
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
index 4640243..dd6c869 100644
--- a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
@@ -27,6 +27,7 @@
#include <mutex>
#include <set>
#include <thread>
+#include <optional>
#include <C2Buffer.h>
@@ -234,12 +235,14 @@
std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
std::set<uint64_t> mDeallocating;
+ // These member variables are read and modified accessed as follows.
+ // 1. mConfigLock being held
+ // Set mInConfig true with mLock in the beginning
+ // Clear mInConfig with mLock in the end
+ // 2. mLock is held and mInConfig is false.
int mMaxDequeue;
- int mMaxDequeueRequested;
int mMaxDequeueCommitted;
-
- uint32_t mMaxDequeueRequestedSeqId;
- uint32_t mMaxDequeueCommittedSeqId;
+ std::optional<int> mMaxDequeueRequested;
int mDequeueable;
@@ -271,13 +274,6 @@
::android::base::unique_fd mWritePipeFd; // The writing end file descriptor
std::atomic<bool> mStopped;
- std::thread mEventQueueThread; // Thread to handle interrupted
- // writes to the writing end.
- std::mutex mEventLock;
- std::condition_variable mEventCv;
-
- bool mStopEventThread;
- int mIncDequeueable; // pending # of write to increase dequeueable eventfd
private:
explicit GraphicsTracker(int maxDequeueCount);
@@ -289,6 +285,9 @@
bool adjustDequeueConfLocked(bool *updateDequeueConf);
void updateDequeueConf();
+ void clearCacheIfNecessaryLocked(
+ const std::shared_ptr<BufferCache> &cache,
+ int maxDequeueCommitted);
c2_status_t requestAllocate(std::shared_ptr<BufferCache> *cache);
c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
@@ -305,7 +304,9 @@
bool cached, int slotId, const sp<Fence> &fence,
std::shared_ptr<BufferItem> *buffer,
bool *updateDequeue);
- void commitDeallocate(std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid);
+ void commitDeallocate(std::shared_ptr<BufferCache> &cache,
+ int slotId, uint64_t bid,
+ bool *updateDequeue);
void commitRender(const std::shared_ptr<BufferCache> &cache,
const std::shared_ptr<BufferItem> &buffer,
const std::shared_ptr<BufferItem> &oldBuffer,
@@ -318,8 +319,8 @@
bool *cached, int *rSlotId, sp<Fence> *rFence,
std::shared_ptr<BufferItem> *buffer);
- void writeIncDequeueable(int inc);
- void processEvent();
+ void writeIncDequeueableLocked(int inc);
+ void drainDequeueableLocked(int dec);
};
} // namespace aidl::android::hardware::media::c2::implementation
diff --git a/media/codec2/hal/common/Android.bp b/media/codec2/hal/common/Android.bp
index 2aedd8b..7d7b285 100644
--- a/media/codec2/hal/common/Android.bp
+++ b/media/codec2/hal/common/Android.bp
@@ -11,6 +11,7 @@
srcs: [
"BufferTypes.cpp",
+ "MultiAccessUnitHelper.cpp",
],
export_include_dirs: ["include/"],
@@ -26,7 +27,10 @@
"libcodec2_vndk",
"liblog",
"libstagefright_foundation",
+ "server_configurable_flags",
],
+
+ static_libs: ["aconfig_mediacodec_flags_c_lib"],
}
cc_library_static {
diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
new file mode 100644
index 0000000..2473e59
--- /dev/null
+++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
@@ -0,0 +1,729 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-MultiAccessUnitHelper"
+#include <android-base/logging.h>
+
+#include <com_android_media_codec_flags.h>
+
+#include <codec2/common/MultiAccessUnitHelper.h>
+#include <android-base/properties.h>
+
+#include <C2BufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+static C2R MultiAccessUnitParamsSetter(
+ bool mayBlock, C2InterfaceHelper::C2P<C2LargeFrame::output> &me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.maxSize).supportsAtAll(me.v.maxSize)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.maxSize)));
+ } else if (!me.F(me.v.thresholdSize).supportsAtAll(me.v.thresholdSize)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.thresholdSize)));
+ } else if (me.v.maxSize < me.v.thresholdSize) {
+ me.set().maxSize = me.v.thresholdSize;
+ } else if (me.v.thresholdSize == 0 && me.v.maxSize > 0) {
+ me.set().thresholdSize = me.v.maxSize;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ res.retrieveFailures(&failures);
+ if (!failures.empty()) {
+ me.set().maxSize = 0;
+ me.set().thresholdSize = 0;
+ }
+ return res;
+}
+
+MultiAccessUnitInterface::MultiAccessUnitInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ std::shared_ptr<C2ReflectorHelper> helper)
+ : C2InterfaceHelper(helper), mC2ComponentIntf(interface) {
+ setDerivedInstance(this);
+ addParameter(
+ DefineParam(mLargeFrameParams, C2_PARAMKEY_OUTPUT_LARGE_FRAME)
+ .withDefault(new C2LargeFrame::output(0u, 0, 0))
+ .withFields({
+ C2F(mLargeFrameParams, maxSize).inRange(
+ 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u)),
+ C2F(mLargeFrameParams, thresholdSize).inRange(
+ 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u))
+ })
+ .withSetter(MultiAccessUnitParamsSetter)
+ .build());
+ std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
+ querySupportedParams(&supportedParams);
+ // Adding to set to do intf seperation in query/config
+ for (std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) {
+ mSupportedParamIndexSet.insert(desc->index());
+ }
+
+ if (mC2ComponentIntf) {
+ c2_status_t err = mC2ComponentIntf->query_vb({&mKind}, {}, C2_MAY_BLOCK, nullptr);
+ }
+}
+
+bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) {
+ return (mSupportedParamIndexSet.count(index) != 0);
+}
+
+C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const {
+ return *mLargeFrameParams;
+}
+
+C2Component::kind_t MultiAccessUnitInterface::kind() const {
+ return (C2Component::kind_t)(mKind.value);
+}
+
+void MultiAccessUnitInterface::getDecoderSampleRateAndChannelCount(
+ uint32_t &sampleRate_, uint32_t &channelCount_) const {
+ if (mC2ComponentIntf) {
+ C2StreamSampleRateInfo::output sampleRate;
+ C2StreamChannelCountInfo::output channelCount;
+ c2_status_t res = mC2ComponentIntf->query_vb(
+ {&sampleRate, &channelCount}, {}, C2_MAY_BLOCK, nullptr);
+ if (res == C2_OK) {
+ sampleRate_ = sampleRate.value;
+ channelCount_ = channelCount.value;
+ }
+ }
+}
+
+//C2MultiAccessUnitBuffer
+class C2MultiAccessUnitBuffer : public C2Buffer {
+ public:
+ explicit C2MultiAccessUnitBuffer(
+ const std::vector<C2ConstLinearBlock> &blocks):
+ C2Buffer(blocks) {
+ }
+};
+
+//MultiAccessUnitHelper
+MultiAccessUnitHelper::MultiAccessUnitHelper(
+ const std::shared_ptr<MultiAccessUnitInterface>& intf):
+ mInit(false),
+ mInterface(intf) {
+ std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
+ if(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator) == C2_OK) {
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
+ mInit = true;
+ }
+}
+
+MultiAccessUnitHelper::~MultiAccessUnitHelper() {
+ std::unique_lock<std::mutex> l(mLock);
+ mFrameHolder.clear();
+}
+
+bool MultiAccessUnitHelper::isEnabledOnPlatform() {
+ bool result = com::android::media::codec::flags::provider_->large_audio_frame();
+ if (!result) {
+ false;
+ }
+ //TODO: remove this before launch
+ result = ::android::base::GetBoolProperty("debug.media.c2.large.audio.frame", true);
+ LOG(DEBUG) << "MultiAccessUnitHelper " << (result ? "enabled" : "disabled");
+ return result;
+}
+
+std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() {
+ return mInterface;
+}
+
+bool MultiAccessUnitHelper::getStatus() {
+ return mInit;
+}
+
+void MultiAccessUnitHelper::reset() {
+ std::lock_guard<std::mutex> l(mLock);
+ mFrameHolder.clear();
+}
+
+c2_status_t MultiAccessUnitHelper::error(
+ std::list<std::unique_ptr<C2Work>> * const worklist) {
+ if (worklist == nullptr) {
+ LOG(ERROR) << "Provided null worklist for error()";
+ return C2_OK;
+ }
+ std::unique_lock<std::mutex> l(mLock);
+ for (auto frame = mFrameHolder.begin(); frame != mFrameHolder.end(); frame++) {
+ if (frame->mLargeWork) {
+ finalizeWork(*frame, 0, true);
+ worklist->push_back(std::move(frame->mLargeWork));
+ frame->reset();
+ }
+ }
+ mFrameHolder.clear();
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::flush(
+ std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) {
+ c2_status_t c2res = C2_OK;
+ std::lock_guard<std::mutex> l(mLock);
+ for (std::unique_ptr<C2Work>& w : *c2flushedWorks) {
+ bool foundFlushedFrame = false;
+ std::list<MultiAccessUnitInfo>::iterator frame =
+ mFrameHolder.begin();
+ while (frame != mFrameHolder.end() && !foundFlushedFrame) {
+ auto it = frame->mComponentFrameIds.find(
+ w->input.ordinal.frameIndex.peekull());
+ if (it != frame->mComponentFrameIds.end()) {
+ LOG(DEBUG) << "Multi access-unit flush"
+ << w->input.ordinal.frameIndex.peekull()
+ << " with " << frame->inOrdinal.frameIndex.peekull();
+ w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
+ bool removeEntry = w->worklets.empty()
+ || !w->worklets.front()
+ || (w->worklets.front()->output.flags
+ & C2FrameData::FLAG_INCOMPLETE) == 0;
+ if (removeEntry) {
+ frame->mComponentFrameIds.erase(it);
+ }
+ foundFlushedFrame = true;
+ }
+ if (frame->mComponentFrameIds.empty()) {
+ frame = mFrameHolder.erase(frame);
+ } else {
+ ++frame;
+ }
+ }
+ }
+ return c2res;
+}
+
+c2_status_t MultiAccessUnitHelper::scatter(
+ std::list<std::unique_ptr<C2Work>> &largeWork,
+ std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) {
+ LOG(DEBUG) << "Multiple access-unit: scatter";
+ if (processedWork == nullptr) {
+ LOG(ERROR) << "MultiAccessUnitHelper provided with no work list";
+ return C2_CORRUPTED;
+ }
+ for (std::unique_ptr<C2Work>& w : largeWork) {
+ std::list<std::unique_ptr<C2Work>> sliceWork;
+ C2WorkOrdinalStruct inputOrdinal = w->input.ordinal;
+ // To hold correspondence and processing bits b/w input and output
+ MultiAccessUnitInfo frameInfo(inputOrdinal);
+ std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds;
+ uint64_t newFrameIdx = mFrameIndex++;
+ // TODO: Do not split buffers if component inherantly supports MultipleFrames.
+ // if thats case, only replace frameindex.
+ auto cloneInputWork = [&newFrameIdx](std::unique_ptr<C2Work>& inWork, uint32_t flags) {
+ std::unique_ptr<C2Work> newWork(new C2Work);
+ newWork->input.flags = (C2FrameData::flags_t)flags;
+ newWork->input.ordinal = inWork->input.ordinal;
+ newWork->input.ordinal.frameIndex = newFrameIdx;
+ if (!inWork->input.configUpdate.empty()) {
+ for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) {
+ newWork->input.configUpdate.push_back(
+ std::move(C2Param::Copy(*(param.get()))));
+ }
+ }
+ newWork->input.infoBuffers = (inWork->input.infoBuffers);
+ if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) {
+ newWork->worklets.emplace_back(new C2Worklet);
+ newWork->worklets.front()->component = inWork->worklets.front()->component;
+ std::vector<std::unique_ptr<C2Tuning>> tunings;
+ for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
+ tunings.push_back(std::move(
+ std::unique_ptr<C2Tuning>(
+ static_cast<C2Tuning*>(
+ C2Param::Copy(*(tuning.get())).release()))));
+ }
+ newWork->worklets.front()->tunings = std::move(tunings);
+ }
+ return newWork;
+ };
+ if (w->input.buffers.empty()
+ || (w->input.buffers.front() == nullptr)
+ || (!w->input.buffers.front()->hasInfo(
+ C2AccessUnitInfos::input::PARAM_TYPE))) {
+ LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex "
+ << inputOrdinal.frameIndex.peekull()
+ << ") -> newFrameIndex " << newFrameIdx
+ <<" : input ts " << inputOrdinal.timestamp.peekull();
+ sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags)));
+ if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
+ sliceWork.back()->input.buffers = std::move(w->input.buffers);
+ }
+ frameSet.insert(newFrameIdx);
+ processedWork->push_back(std::move(sliceWork));
+ } else {
+ const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers;
+ if (inBuffers.front()->data().linearBlocks().size() == 0) {
+ LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks.";
+ return C2_CORRUPTED;
+ }
+ const std::vector<C2ConstLinearBlock>& multiAU =
+ inBuffers.front()->data().linearBlocks();
+ std::shared_ptr<const C2AccessUnitInfos::input> auInfo =
+ std::static_pointer_cast<const C2AccessUnitInfos::input>(
+ w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE));
+ uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size();
+ for (int idx = 0; idx < auInfo->flexCount(); ++idx) {
+ std::vector<C2ConstLinearBlock> au;
+ const C2AccessUnitInfosStruct &info = auInfo->m.values[idx];
+ std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags);
+ frameSet.insert(newFrameIdx);
+ newFrameIdx = mFrameIndex++;
+ newWork->input.ordinal.timestamp = info.timestamp;
+ au.push_back(multiAU.front().subBlock(offset, info.size));
+ if ((offset + info.size) > multiAUSize) {
+ LOG(ERROR) << "ERROR: access-unit offset > buffer size"
+ << " current offset " << (offset + info.size)
+ << " buffer size " << multiAUSize;
+ return C2_CORRUPTED;
+ }
+ newWork->input.buffers.push_back(
+ std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
+ LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
+ << inputOrdinal.frameIndex.peekull()
+ << " total offset " << offset << " info.size " << info.size
+ << " : TS " << newWork->input.ordinal.timestamp.peekull();
+ // add to worklist
+ sliceWork.push_back(std::move(newWork));
+ processedWork->push_back(std::move(sliceWork));
+ offset += info.size;
+ }
+ }
+ if (!processedWork->empty()) {
+ {
+ C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam();
+ if (mInterface->kind() == C2Component::KIND_DECODER) {
+ uint32_t sampleRate = 0;
+ uint32_t channelCount = 0;
+ uint32_t frameSize = 0;
+ mInterface->getDecoderSampleRateAndChannelCount(
+ sampleRate, channelCount);
+ if (sampleRate > 0 && channelCount > 0) {
+ frameSize = channelCount * 2;
+ multiAccessParams.maxSize =
+ (multiAccessParams.maxSize / frameSize) * frameSize;
+ multiAccessParams.thresholdSize =
+ (multiAccessParams.thresholdSize / frameSize) * frameSize;
+ }
+ }
+ frameInfo.mLargeFrameTuning = multiAccessParams;
+ std::lock_guard<std::mutex> l(mLock);
+ mFrameHolder.push_back(std::move(frameInfo));
+ }
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::gather(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::unique_ptr<C2Work>>* const processedWork) {
+ LOG(DEBUG) << "Multi access-unit gather process";
+ if (processedWork == nullptr) {
+ LOG(ERROR) << "Nothing provided for processed work";
+ return C2_CORRUPTED;
+ }
+ auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) {
+ processedWork->push_back(std::move(work));
+ };
+ {
+ std::lock_guard<std::mutex> l(mLock);
+ for (auto& work : c2workItems) {
+ LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size();
+ uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull();
+ bool removeEntry = work->worklets.empty()
+ || !work->worklets.front()
+ || (work->worklets.front()->output.flags
+ & C2FrameData::FLAG_INCOMPLETE) == 0;
+ bool foundFrame = false;
+ std::list<MultiAccessUnitInfo>::iterator frame =
+ mFrameHolder.begin();
+ while (!foundFrame && frame != mFrameHolder.end()) {
+ auto it = frame->mComponentFrameIds.find(thisFrameIndex);
+ if (it != frame->mComponentFrameIds.end()) {
+ foundFrame = true;
+ LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex
+ << " worklstsSze " << work->worklets.size()
+ << ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull();
+ if (work->result != C2_OK
+ || work->worklets.empty()
+ || !work->worklets.front()
+ || (frame->mLargeFrameTuning.thresholdSize == 0
+ || frame->mLargeFrameTuning.maxSize == 0)) {
+ if (removeEntry) {
+ frame->mComponentFrameIds.erase(it);
+ removeEntry = false;
+ }
+ if (frame->mLargeWork) {
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ }
+ c2_status_t workResult = work->result;
+ frame->mLargeWork = std::move(work);
+ frame->mLargeWork->input.ordinal.frameIndex =
+ frame->inOrdinal.frameIndex;
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ if (workResult != C2_OK) {
+ frame->mAccessUnitInfos.clear();
+ }
+ } else if (C2_OK != processWorklets(*frame, work, addOutWork)) {
+ LOG(DEBUG) << "Error while processing work";
+ }
+ if (removeEntry) {
+ LOG(DEBUG) << "Removing entry: " << thisFrameIndex
+ << " -> " << frame->inOrdinal.frameIndex.peekull();
+ frame->mComponentFrameIds.erase(it);
+ }
+ // This is to take care of the last bytes and to decide to send with
+ // FLAG_INCOMPLETE or not.
+ if ((frame->mWview
+ && (frame->mWview->offset() > frame->mLargeFrameTuning.thresholdSize))
+ || frame->mComponentFrameIds.empty()) {
+ if (frame->mLargeWork) {
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ }
+ }
+ if (frame->mComponentFrameIds.empty()) {
+ LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex;
+ frame = mFrameHolder.erase(frame);
+ continue;
+ }
+ } else {
+ LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex
+ << " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull();
+ }
+ frame++;
+ }
+ if (!foundFrame) {
+ LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex;
+ }
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) {
+ if (!mInit) {
+ LOG(ERROR) << "Large buffer allocator failed";
+ return C2_NO_MEMORY;
+ }
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize;
+ c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock);
+ LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset()
+ << " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity();
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error allocating Multi access-unit Buffer";
+ return err;
+ }
+ frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get());
+ LOG(DEBUG) << "Allocated buffer : requested size : " <<
+ frame.mLargeFrameTuning.maxSize
+ << " alloc size " << frame.mWview->size();
+ return C2_OK;
+}
+
+/*
+ * For every work from the component, we try to do aggregation of work here.
+*/
+c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame,
+ std::unique_ptr<C2Work>& work,
+ const std::function <void(std::unique_ptr<C2Work>&)>& addWork) {
+ // This will allocate work, worklet, c2Block
+ auto allocateWork = [&](MultiAccessUnitInfo &frame,
+ bool allocateWorket = false,
+ bool allocateBuffer = false) {
+ c2_status_t ret = C2_OK;
+ if (frame.mLargeWork == nullptr) {
+ frame.mLargeWork.reset(new C2Work);
+ frame.mLargeWork->input.ordinal = frame.inOrdinal;
+ frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+ }
+ if (allocateWorket) {
+ if (frame.mLargeWork->worklets.size() == 0) {
+ frame.mLargeWork->worklets.emplace_back(new C2Worklet);
+ }
+ }
+ if (allocateBuffer) {
+ if (frame.mWview == nullptr) {
+ ret = createLinearBlock(frame);
+ }
+ }
+ return ret;
+ };
+ // we will only have one worklet.
+ bool foundEndOfStream = false;
+ for (auto worklet = work->worklets.begin();
+ worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) {
+ uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME
+ | C2FrameData::FLAG_DISCARD_FRAME
+ | C2FrameData::FLAG_CORRUPT;
+ if ((*worklet)->output.flags & flagsForNoCopy) {
+ if (frame.mLargeWork) {
+ finalizeWork(frame);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ }
+ frame.mLargeWork = std::move(work);
+ frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+ finalizeWork(frame);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_OK;
+ }
+ c2_status_t c2ret = allocateWork(frame, true);
+ if (c2ret != C2_OK) {
+ return c2ret;
+ }
+ C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output;
+ if (!(*worklet)->output.configUpdate.empty()) {
+ for (auto& configUpdate : (*worklet)->output.configUpdate) {
+ outputFramedata.configUpdate.push_back(std::move(configUpdate));
+ }
+ (*worklet)->output.configUpdate.clear();
+ }
+ outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(),
+ (*worklet)->output.infoBuffers.begin(),
+ (*worklet)->output.infoBuffers.end());
+ int64_t sampleTimeUs = 0;
+ uint32_t frameSize = 0;
+ uint32_t sampleRate = 0;
+ uint32_t channelCount = 0;
+ mInterface->getDecoderSampleRateAndChannelCount(sampleRate, channelCount);
+ if (sampleRate > 0 && channelCount > 0) {
+ sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2);
+ frameSize = channelCount * 2;
+ }
+ LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize
+ << " threshold " << frame.mLargeFrameTuning.thresholdSize;
+ if ((*worklet)->output.buffers.size() > 0) {
+ allocateWork(frame, true, true);
+ }
+ LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers"
+ << " ts: " << (*worklet)->output.ordinal.timestamp.peekull();
+ int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull();
+ int64_t timestamp = workletTimestamp;
+ uint32_t flagsForCopy = ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG;
+ for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) {
+ std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx];
+ if (!buffer || buffer->data().linearBlocks().empty()) {
+ continue;
+ }
+ const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks();
+ if (blocks.size() > 0) {
+ uint32_t inputOffset = 0;
+ uint32_t inputSize = blocks.front().size();
+ frame.mInfos.insert(frame.mInfos.end(),
+ buffer->info().begin(), buffer->info().end());
+ if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) {
+ // For decoders we only split multiples of 16bChannelCount*2
+ inputSize -= (inputSize % frameSize);
+ }
+ while (inputOffset < inputSize) {
+ if (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize) {
+ frame.mLargeWork->result = C2_OK;
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, true);
+ }
+ if (mInterface->kind() == C2Component::KIND_ENCODER) {
+ if (inputSize > frame.mLargeFrameTuning.maxSize) {
+ LOG(ERROR) << "Enc: Output buffer too small for AU, configured with "
+ << frame.mLargeFrameTuning.maxSize
+ << " block size: " << blocks.front().size()
+ << "alloc size " << frame.mWview->size();
+ if (frame.mLargeWork
+ && frame.mWview && frame.mWview->offset() > 0) {
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, false);
+ }
+ frame.mLargeWork->result = C2_NO_MEMORY;
+ finalizeWork(frame, 0, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_NO_MEMORY;
+ } else if (inputSize > frame.mWview->size()) {
+ LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size "
+ << frame.mWview->offset();
+ if (frame.mLargeWork
+ && frame.mWview && frame.mWview->offset() > 0) {
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, true);
+ }
+ }
+ }
+ C2ReadView rView = blocks.front().map().get();
+ if (rView.error()) {
+ LOG(ERROR) << "Buffer read view error";
+ frame.mLargeWork->result = rView.error();
+ frame.mLargeWork->worklets.clear();
+ finalizeWork(frame, 0, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_NO_MEMORY;
+ }
+ uint32_t toCopy = 0;
+ if (mInterface->kind() == C2Component::KIND_ENCODER) {
+ toCopy = inputSize;
+ } else {
+ toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset));
+ timestamp = workletTimestamp + inputOffset * sampleTimeUs;
+ LOG(DEBUG) << "ts " << timestamp
+ << " copiedOutput " << inputOffset
+ << " sampleTimeUs " << sampleTimeUs;
+ }
+ LOG(DEBUG) << " Copy size " << toCopy
+ << " ts " << timestamp;
+ memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy);
+ frame.mWview->setOffset(frame.mWview->offset() + toCopy);
+ inputOffset += toCopy;
+ mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp);
+ }
+ } else {
+ frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer));
+ LOG(DEBUG) << "Copying worklets without linear buffer";
+ }
+ }
+ uint32_t flagsForCsdOrEnd = (*worklet)->output.flags
+ & (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG);
+ if (flagsForCsdOrEnd) {
+ LOG(DEBUG) << "Output worklet has CSD/EOS data";
+ frame.mLargeWork->result = C2_OK;
+ // we can assign timestamp as this will be evaluated in finalizeWork
+ frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp;
+ finalizeWork(frame, flagsForCsdOrEnd, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::finalizeWork(
+ MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) {
+ if (frame.mLargeWork == nullptr) {
+ return C2_OK;
+ }
+ //prepare input ordinal
+ frame.mLargeWork->input.ordinal = frame.inOrdinal;
+ // remove this
+ int64_t timeStampUs = frame.inOrdinal.timestamp.peekull();
+ if (!frame.mAccessUnitInfos.empty()) {
+ timeStampUs = frame.mAccessUnitInfos.front().timestamp;
+ } else if (!frame.mLargeWork->worklets.empty()) {
+ std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front();
+ if (worklet) {
+ timeStampUs = worklet->output.ordinal.timestamp.peekull();
+ }
+ }
+ LOG(DEBUG) << "Finalizing work with input Idx "
+ << frame.mLargeWork->input.ordinal.frameIndex.peekull()
+ << " timestamp " << timeStampUs;
+ uint32_t finalFlags = 0;
+ if ((!forceComplete)
+ && (frame.mLargeWork->result == C2_OK)
+ && (!frame.mComponentFrameIds.empty())) {
+ finalFlags |= C2FrameData::FLAG_INCOMPLETE;
+ }
+ if (frame.mLargeWork->result == C2_OK) {
+ finalFlags |= inFlags;
+ }
+ // update worklet if present
+ if (!frame.mLargeWork->worklets.empty() &&
+ frame.mLargeWork->worklets.front() != nullptr) {
+ frame.mLargeWork->workletsProcessed = 1;
+ C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output;
+ outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull();
+ outFrameData.ordinal.timestamp = timeStampUs;
+ finalFlags |= frame.mLargeWork->worklets.front()->output.flags;
+ outFrameData.flags = (C2FrameData::flags_t)finalFlags;
+ // update buffers
+ if (frame.mBlock && (frame.mWview->offset() > 0)) {
+ size_t size = frame.mWview->offset();
+ LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size
+ << " timestamp as " << timeStampUs
+ << "frameIndex " << outFrameData.ordinal.frameIndex.peekull();
+ frame.mWview->setOffset(0);
+ std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer(
+ frame.mBlock->share(0, size, ::C2Fence()));
+ if (frame.mAccessUnitInfos.size() > 0) {
+ if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) {
+ frame.mAccessUnitInfos.back().flags |=
+ C2FrameData::FLAG_END_OF_STREAM;
+ }
+ std::shared_ptr<C2AccessUnitInfos::output> largeFrame =
+ C2AccessUnitInfos::output::AllocShared(
+ frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos);
+ frame.mInfos.push_back(largeFrame);
+ frame.mAccessUnitInfos.clear();
+ }
+ for (auto &info : frame.mInfos) {
+ c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info));
+ }
+ frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer));
+ frame.mInfos.clear();
+ frame.mBlock.reset();
+ frame.mWview.reset();
+ }
+ }
+ LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags;
+ return C2_OK;
+}
+
+void MultiAccessUnitHelper::mergeAccessUnitInfo(
+ MultiAccessUnitInfo &frame,
+ uint32_t flags_,
+ uint32_t size,
+ int64_t timestamp) {
+ // Remove flags that are not part of Access unit info
+ uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE
+ | C2FrameData::FLAG_DISCARD_FRAME
+ | C2FrameData::FLAG_CORRUPT
+ | C2FrameData::FLAG_CORRECTED);
+ if (frame.mAccessUnitInfos.empty()) {
+ frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+ return;
+ }
+ if ((mInterface->kind() == C2Component::KIND_DECODER) &&
+ (frame.mAccessUnitInfos.back().flags == flags)) {
+ // merge access units here
+ C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back();
+ s.size += size; // don't have to update timestamp
+ } else {
+ frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+ }
+}
+
+void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() {
+ mBlock.reset();
+ mWview.reset();
+ mInfos.clear();
+ mAccessUnitInfos.clear();
+ mLargeWork.reset();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
new file mode 100644
index 0000000..ef5cff9
--- /dev/null
+++ b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+#define CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+
+#include <hidl/Status.h>
+#include <hwbinder/IBinder.h>
+
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <set>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+struct MultiAccessUnitHelper;
+
+struct MultiAccessUnitInterface : public C2InterfaceHelper {
+ explicit MultiAccessUnitInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ std::shared_ptr<C2ReflectorHelper> helper);
+
+ bool isParamSupported(C2Param::Index index);
+ C2LargeFrame::output getLargeFrameParam() const;
+ C2Component::kind_t kind() const;
+
+protected:
+ void getDecoderSampleRateAndChannelCount(
+ uint32_t &sampleRate_, uint32_t &channelCount_) const;
+ const std::shared_ptr<C2ComponentInterface> mC2ComponentIntf;
+ std::shared_ptr<C2LargeFrame::output> mLargeFrameParams;
+ C2ComponentKindSetting mKind;
+ std::set<C2Param::Index> mSupportedParamIndexSet;
+
+ friend struct MultiAccessUnitHelper;
+};
+
+struct MultiAccessUnitHelper {
+public:
+ MultiAccessUnitHelper(
+ const std::shared_ptr<MultiAccessUnitInterface>& intf);
+
+ virtual ~MultiAccessUnitHelper();
+
+ static bool isEnabledOnPlatform();
+
+ /*
+ * Scatters the incoming linear buffer into access-unit sized buffers
+ * based on the access-unit info.
+ */
+ c2_status_t scatter(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::list<std::unique_ptr<C2Work>>> * const processedWork);
+
+ /*
+ * Gathers different access-units into a single buffer based on the scatter list
+ * and the configured max and threshold sizes. This also generates the associated
+ * access-unit information and attach it with the final result.
+ */
+ c2_status_t gather(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::unique_ptr<C2Work>> * const processedWork);
+
+ /*
+ * Flushes the codec and generated the list of flushed buffers.
+ */
+ c2_status_t flush(
+ std::list<std::unique_ptr<C2Work>> * const c2flushedWorks);
+
+ /*
+ * Gets all the pending buffers under generation in c2workItems.
+ */
+ c2_status_t error(std::list<std::unique_ptr<C2Work>> * const c2workItems);
+
+ /*
+ * Get the interface object of this handler.
+ */
+ std::shared_ptr<MultiAccessUnitInterface> getInterface();
+
+ /*
+ * Gets the status of the object. This really is to make sure that
+ * all the allocators are configured properly within the handler.
+ */
+ bool getStatus();
+
+ /*
+ * Resets the structures inside the handler.
+ */
+ void reset();
+
+protected:
+
+ struct MultiAccessUnitInfo {
+ /*
+ * From the input
+ * Ordinal of the input frame
+ */
+ C2WorkOrdinalStruct inOrdinal;
+
+ /*
+ * Frame indexes of the scattered buffers
+ */
+ std::set<uint64_t> mComponentFrameIds;
+
+ /*
+ * For the output
+ * Current output block.
+ */
+ std::shared_ptr<C2LinearBlock> mBlock;
+
+ /*
+ * Write view of current block
+ */
+ std::shared_ptr<C2WriteView> mWview;
+
+ /*
+ * C2Info related to the current mBlock
+ */
+ std::vector<std::shared_ptr<const C2Info>> mInfos;
+
+ /*
+ * C2AccessUnitInfos for the current buffer
+ */
+ std::vector<C2AccessUnitInfosStruct> mAccessUnitInfos;
+
+ /*
+ * Current tuning used to process this input work
+ */
+ C2LargeFrame::output mLargeFrameTuning;
+
+ /*
+ * Current output C2Work being processed
+ */
+ std::unique_ptr<C2Work> mLargeWork;
+
+ MultiAccessUnitInfo(C2WorkOrdinalStruct ordinal):inOrdinal(ordinal) {
+
+ }
+
+ /*
+ * Resets this frame
+ */
+ void reset();
+ };
+
+ /*
+ * Creates a linear block to be used with work
+ */
+ c2_status_t createLinearBlock(MultiAccessUnitInfo &frame);
+
+ /*
+ * Processes worklets from the component
+ */
+ c2_status_t processWorklets(MultiAccessUnitInfo &frame,
+ std::unique_ptr<C2Work> &work,
+ const std::function <void(std::unique_ptr<C2Work>&)> &addWork);
+
+ /*
+ * Finalizes the work to be send out.
+ */
+ c2_status_t finalizeWork(MultiAccessUnitInfo &frame,
+ uint32_t flags = 0, bool forceComplete = false);
+
+ /*
+ * Merges different access unit infos if possible
+ */
+ void mergeAccessUnitInfo(MultiAccessUnitInfo &frame,
+ uint32_t flags,
+ uint32_t size,
+ int64_t timestamp);
+
+ bool mInit;
+
+ // Interface of this module
+ std::shared_ptr<MultiAccessUnitInterface> mInterface;
+ // Local pool id used for output buffer allocation
+ C2BlockPool::local_id_t mBlockPoolId;
+ // C2Blockpool for output buffer allocation
+ std::shared_ptr<C2BlockPool> mLinearPool;
+ // Allocator for output buffer allocation
+ std::shared_ptr<C2Allocator> mLinearAllocator;
+ // FrameIndex for the current outgoing work
+ std::atomic_uint64_t mFrameIndex;
+ // Mutex to protect mFrameHolder
+ std::mutex mLock;
+ // List of Infos that contains the input and
+ // output work and buffer objects
+ std::list<MultiAccessUnitInfo> mFrameHolder;
+};
+
+} // namespace android
+
+#endif // CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
diff --git a/media/codec2/hal/hidl/1.0/utils/Android.bp b/media/codec2/hal/hidl/1.0/utils/Android.bp
index 2f2ecd1..9646a0b 100644
--- a/media/codec2/hal/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.0/utils/Android.bp
@@ -52,7 +52,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index 0aeed08..ebbaafc 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -135,6 +135,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &handler):
+ Listener(component), mHandler(handler) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHandler) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHandler->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHandler) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHandler->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHandler;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +254,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -240,7 +310,6 @@
// Methods from ::android::hardware::media::c2::V1_0::IComponent
Return<Status> Component::queue(const WorkBundle& workBundle) {
std::list<std::unique_ptr<C2Work>> c2works;
-
if (!objcpy(&c2works, workBundle)) {
return Status::CORRUPTED;
}
@@ -252,7 +321,19 @@
registerFrameData(mListener, work->input);
}
}
-
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,6 +342,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +553,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +566,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -501,8 +591,14 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
+
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
mInit = res;
diff --git a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
index 12078e0..5a5e780 100644
--- a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
@@ -25,7 +25,6 @@
#include <hidl/HidlBinderSupport.h>
#include <utils/Timers.h>
-#include <C2BqBufferPriv.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -45,9 +44,10 @@
// Implementation of ConfigurableC2Intf based on C2ComponentInterface
struct CompIntf : public ConfigurableC2Intf {
- CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+ CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
ConfigurableC2Intf{intf->getName(), intf->getId()},
- mIntf{intf} {
+ mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
}
virtual c2_status_t config(
@@ -55,7 +55,34 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures
) override {
- return mIntf->config_vb(params, mayBlock, failures);
+ std::vector<C2Param*> paramsToIntf;
+ std::vector<C2Param*> paramsToLargeFrameIntf;
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->config_vb(params, mayBlock, failures);
+ return err;
+ }
+ for (auto &p : params) {
+ if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+ paramsToLargeFrameIntf.push_back(p);
+ } else {
+ paramsToIntf.push_back(p);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+ }
+ if (err1 != C2_OK) {
+ LOG(ERROR) << "We have a failed config";
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->config(
+ paramsToLargeFrameIntf, mayBlock, failures);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t query(
@@ -63,33 +90,74 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const params
) const override {
- return mIntf->query_vb({}, indices, mayBlock, params);
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->query_vb({}, indices, mayBlock, params);
+ return err;
+ }
+ std::vector<C2Param::Index> paramsToIntf;
+ std::vector<C2Param::Index> paramsToLargeFrameIntf;
+ for (auto &i : indices) {
+ if (mMultiAccessUnitIntf->isParamSupported(i)) {
+ paramsToLargeFrameIntf.push_back(i);
+ } else {
+ paramsToIntf.push_back(i);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->query(
+ {}, paramsToLargeFrameIntf, mayBlock, params);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t querySupportedParams(
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
) const override {
- return mIntf->querySupportedParams_nb(params);
+ c2_status_t err = mIntf->querySupportedParams_nb(params);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedParams(params);
+ }
+ return err;
}
virtual c2_status_t querySupportedValues(
std::vector<C2FieldSupportedValuesQuery>& fields,
c2_blocking_t mayBlock) const override {
- return mIntf->querySupportedValues_vb(fields, mayBlock);
+ c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+ }
+ return err;
}
protected:
std::shared_ptr<C2ComponentInterface> mIntf;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
};
} // unnamed namespace
+
// ComponentInterface
ComponentInterface::ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
const std::shared_ptr<ParameterCache>& cache)
: mInterface{intf},
- mConfigurable{new CachedConfigurable(std::make_unique<CompIntf>(intf))} {
+ mConfigurable{new CachedConfigurable(
+ std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
mInit = mConfigurable->init(cache);
}
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
index 3f55618..aed94ec 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
@@ -30,6 +30,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -54,6 +56,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -113,6 +117,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -135,6 +141,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
index 9102f92..2995faf 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
@@ -23,10 +23,15 @@
#include <android/hardware/media/c2/1.0/IComponentInterface.h>
#include <hidl/Status.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
#include <C2.h>
+#include <set>
#include <memory>
namespace android {
@@ -39,6 +44,7 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
+using ::android::MultiAccessUnitInterface;
struct ComponentStore;
@@ -46,6 +52,11 @@
ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
const std::shared_ptr<ParameterCache>& cache);
+
+ ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+ const std::shared_ptr<ParameterCache>& cache);
c2_status_t status() const;
virtual Return<sp<IConfigurable>> getConfigurable() override;
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
index 1c5c7d6..2054fe6 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
@@ -44,6 +44,7 @@
"res/bbb_opus_stereo_128kbps_48000hz.info",
"res/bbb_opus_stereo_128kbps_48000hz.opus",
"res/bbb_raw_1ch_8khz_s32le.info",
+ "res/bbb_raw_1ch_8khz_s32le_largeframe.info",
"res/bbb_raw_1ch_8khz_s32le.raw",
"res/bbb_vorbis_stereo_128kbps_48000hz.info",
"res/bbb_vorbis_stereo_128kbps_48000hz.vorbis",
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index ce9fc39..0c30d95 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -32,7 +32,11 @@
#include <codec2/hidl/client.h>
#include "media_c2_hidl_test_common.h"
-using DecodeTestParameters = std::tuple<std::string, std::string, uint32_t, bool>;
+
+using DecodeTestParameters = std::tuple<std::string /*instance_name*/,
+ std::string /*component_name*/,
+ uint32_t /*stream_index*/,
+ bool /*signal end-of-stream nor not*/>;
static std::vector<DecodeTestParameters> gDecodeTestParameters;
using CsdFlushTestParameters = std::tuple<std::string, std::string, bool>;
@@ -56,6 +60,7 @@
{"g711-mlaw", "bbb_g711mulaw_1ch_8khz.raw", "bbb_g711mulaw_1ch_8khz.info"},
{"gsm", "bbb_gsm_1ch_8khz_13kbps.raw", "bbb_gsm_1ch_8khz_13kbps.info"},
{"raw", "bbb_raw_1ch_8khz_s32le.raw", "bbb_raw_1ch_8khz_s32le.info"},
+ {"raw", "bbb_raw_1ch_8khz_s32le.raw", "bbb_raw_1ch_8khz_s32le_largeframe.info"},
{"flac", "bbb_flac_stereo_680kbps_48000hz.flac", "bbb_flac_stereo_680kbps_48000hz.info"},
};
@@ -90,8 +95,8 @@
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(
- mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+ getBufferPoolVer());
ASSERT_NE(mLinearPool, nullptr);
std::vector<std::unique_ptr<C2Param>> queried;
@@ -137,6 +142,9 @@
struct outputMetaData {
uint64_t timestampUs;
uint32_t rangeLength;
+ // The following is used only if C2AccessUnitInfos::output
+ // is present as part of C2Buffer.
+ std::vector<C2AccessUnitInfosStruct> largeFrameInfo;
};
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
@@ -161,8 +169,18 @@
.capacity();
// List of timestamp values and output size to calculate timestamp
if (mTimestampDevTest) {
- outputMetaData meta = {mTimestampUs, rangeLength};
+ outputMetaData meta = {mTimestampUs, rangeLength, {}};
oBufferMetaData.push_back(meta);
+ std::shared_ptr<const C2AccessUnitInfos::output> inBufferInfo =
+ std::static_pointer_cast<const C2AccessUnitInfos::output>(
+ work->worklets.front()->output.buffers[0]->getInfo(
+ C2AccessUnitInfos::output::PARAM_TYPE));
+ if (inBufferInfo) {
+ for (int nMeta = 0; nMeta < inBufferInfo->flexCount(); nMeta++) {
+ oBufferMetaData.back().largeFrameInfo.push_back(
+ inBufferInfo->m.values[nMeta]);
+ }
+ }
}
}
bool mCsd = false;
@@ -203,6 +221,12 @@
std::string mInfoFile;
size_t mStreamIndex = 0;
+ // These are used only with large frame codec
+ // Specifies the maximum output size in bytes.
+ uint32_t mMaxOutputSize;
+ //Specifies the threshold output size in bytes.
+ uint32_t mOutputThresholdSize;
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -249,6 +273,96 @@
ALOGV("Component Valid");
}
+bool isLargeAudioFrameSupported(const std::shared_ptr<android::Codec2Client::Component> &comp,
+ std::vector<C2FieldSupportedValues>& supportedValues) {
+ C2LargeFrame::output largeFrameParams;
+ std::vector<C2FieldSupportedValuesQuery> validValueInfos = {
+ C2FieldSupportedValuesQuery::Current(
+ C2ParamField(&largeFrameParams, &C2LargeFrame::maxSize)),
+ C2FieldSupportedValuesQuery::Current(
+ C2ParamField(&largeFrameParams,
+ &C2LargeFrame::thresholdSize))};
+ c2_status_t c2err = comp->querySupportedValues(validValueInfos, C2_DONT_BLOCK);
+ if (c2err != C2_OK || validValueInfos.size() != 2) {
+ return false;
+ }
+ supportedValues.clear();
+ for (int i = 0; i < 2; i++) {
+ if (validValueInfos[i].values.type == C2FieldSupportedValues::EMPTY) {
+ return false;
+ }
+ supportedValues.push_back(validValueInfos[i].values);
+ }
+ return true;
+}
+
+c2_status_t configureLargeFrameParams(const std::shared_ptr<android::Codec2Client::Component> &comp,
+ uint32_t& maxOutput, uint32_t& outputThreshold,
+ const std::vector<C2FieldSupportedValues>& supportedValues) {
+
+ if (supportedValues.empty()) {
+ ALOGE("Error: No supported values in large audio frame params");
+ return C2_BAD_VALUE;
+ }
+
+ auto boundBySupportedValues = [](const C2FieldSupportedValues& supportedValues, uint32_t& value)
+ -> c2_status_t {
+ uint32_t oBufMin = 0, oBufMax = 0;
+ switch (supportedValues.type) {
+ case C2FieldSupportedValues::type_t::RANGE:
+ {
+ const auto& range = supportedValues.range;
+ oBufMax = (uint32_t)(range.max).ref<uint32_t>();
+ oBufMin = (uint32_t)(range.min).ref<uint32_t>();
+ value = (value > oBufMax) ? oBufMax :
+ (value < oBufMin) ? oBufMin : value;
+ break;
+ }
+
+ case C2FieldSupportedValues::type_t::VALUES:
+ {
+ uint32_t lastValue;
+ for (const C2Value::Primitive& prim : supportedValues.values) {
+ lastValue = (uint32_t)prim.ref<uint32_t>();
+ if (lastValue > value) {
+ value = lastValue;
+ break;
+ }
+ }
+ if (value > lastValue) {
+ value = lastValue;
+ }
+ break;
+ }
+
+ default:
+ return C2_BAD_VALUE;
+ }
+ return C2_OK;
+ };
+ c2_status_t c2_err = boundBySupportedValues(supportedValues[0], maxOutput);
+ if (c2_err != C2_OK) {
+ return c2_err;
+ }
+ c2_err = boundBySupportedValues(supportedValues[1], outputThreshold);
+ if (c2_err != C2_OK) {
+ return c2_err;
+ }
+ if (outputThreshold > maxOutput) {
+ outputThreshold = maxOutput;
+ }
+ ALOGV("Setting large frame format : Max: %d - Threshold: %d", maxOutput, outputThreshold);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ C2LargeFrame::output largeFrameParams(0u, maxOutput, outputThreshold);
+ std::vector<C2Param*> configParam{&largeFrameParams};
+ c2_status_t status = comp->config(configParam, C2_DONT_BLOCK, &failures);
+ if (status != C2_OK || failures.size() != 0u) {
+ ALOGE("Large frame Audio configuration failed for maxSize: %d, thresholdSize: %d",
+ maxOutput, outputThreshold);
+ }
+ return status;
+}
+
// Set Default config param.
bool setupConfigParam(const std::shared_ptr<android::Codec2Client::Component>& component,
int32_t* bitStreamInfo) {
@@ -317,6 +431,10 @@
typedef std::unique_lock<std::mutex> ULock;
int frameID = offset;
int maxRetry = 0;
+ std::shared_ptr<C2Buffer> buffer;
+ std::vector<C2FieldSupportedValues> largeFrameValues;
+ bool isComponentSupportsLargeAudioFrame = isLargeAudioFrameSupported(component,
+ largeFrameValues);
while (1) {
if (frameID == (int)Info->size() || frameID == (offset + range)) break;
uint32_t flags = 0;
@@ -336,7 +454,9 @@
ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
}
int64_t timestamp = (*Info)[frameID].timestamp;
- flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+ flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+ ? C2FrameData::FLAG_CODEC_CONFIG
+ : 0;
if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
@@ -374,7 +494,17 @@
memcpy(view.base(), data, size);
- work->input.buffers.emplace_back(new LinearBuffer(block));
+ buffer.reset(new LinearBuffer(block));
+ if (!(*Info)[frameID].largeFrameInfo.empty() && isComponentSupportsLargeAudioFrame) {
+ const std::vector<C2AccessUnitInfosStruct>& meta =
+ (*Info)[frameID].largeFrameInfo;
+ ALOGV("Large Audio frame supported for %s, frameID: %d, size: %zu",
+ component->getName().c_str(), frameID, meta.size());
+ const std::shared_ptr<C2AccessUnitInfos::input> largeFrame =
+ C2AccessUnitInfos::input::AllocShared(meta.size(), 0u, meta);
+ buffer->setInfo(largeFrame);
+ }
+ work->input.buffers.push_back(buffer);
free(data);
}
work->worklets.clear();
@@ -401,9 +531,37 @@
auto itOut = oBufferMetaData.begin();
EXPECT_EQ(*itIn, itOut->timestampUs);
uint64_t expectedTimeStamp = *itIn;
- while (itOut != oBufferMetaData.end()) {
+ bool err= false;
+ while (!err && itOut != oBufferMetaData.end()) {
EXPECT_EQ(expectedTimeStamp, itOut->timestampUs);
if (expectedTimeStamp != itOut->timestampUs) break;
+ if (!itOut->largeFrameInfo.empty()) {
+ // checking large audio frame metadata
+ if (itOut->largeFrameInfo[0].timestamp != itOut->timestampUs) {
+ ALOGE("Metadata first time stamp doesn't match");
+ err = true;
+ break;
+ }
+ uint64_t totalSize = 0;
+ uint64_t sampleSize = 0;
+ int64_t nextTimestamp = itOut->timestampUs;
+ for (auto& meta : itOut->largeFrameInfo) {
+ if (nextTimestamp != meta.timestamp) {
+ ALOGE("Metadata timestamp error: expect: %lld, got: %lld",
+ (long long)nextTimestamp, (long long)meta.timestamp);
+ err = true;
+ break;
+ }
+ totalSize += meta.size;
+ sampleSize = (meta.size / (nChannels * 2));
+ nextTimestamp += sampleSize * 1000000ll / nSampleRate;
+ }
+ if (totalSize != itOut->rangeLength) {
+ ALOGE("Metadata size error: expected:%lld, got: %d",
+ (long long)totalSize, itOut->rangeLength);
+ err = true;
+ }
+ }
// buffer samples = ((total bytes) / (ac * (bits per sample / 8))
samplesReceived += ((itOut->rangeLength) / (nChannels * 2));
expectedTimeStamp = samplesReceived * 1000000ll / nSampleRate;
@@ -451,7 +609,8 @@
android::Vector<FrameInfo> Info;
int32_t numCsds = populateInfoVector(mInfoFile, &Info, mTimestampDevTest, &mTimestampUslist);
- ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << mInfoFile;
+ ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << mInfoFile <<
+ " #CSD " << numCsds;
// Reset total no of frames received
mFramesReceived = 0;
@@ -472,10 +631,23 @@
std::cout << "[ WARN ] Test Skipped \n";
return;
}
+ getInputChannelInfo(mComponent, mMime, bitStreamInfo);
+ std::vector<C2FieldSupportedValues> supportedValues;
+ if (!Info.top().largeFrameInfo.empty()) {
+ if (!isLargeAudioFrameSupported(mComponent, supportedValues)) {
+ GTEST_SKIP() << "As component does not support large frame";
+ }
+ // time_sec * sample_rate * channel_count * 2 (bytes_per_channel)
+ mMaxOutputSize = 60 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ mOutputThresholdSize = 50 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ ASSERT_EQ(configureLargeFrameParams(mComponent, mMaxOutputSize,
+ mOutputThresholdSize, supportedValues), C2_OK);
+ }
ASSERT_EQ(mComponent->start(), C2_OK);
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
+
ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
mFlushedIndices, mLinearPool, eleStream, &Info, 0,
(int)Info.size(), signalEOS));
@@ -527,18 +699,26 @@
std::cout << "[ WARN ] Test Skipped \n";
return;
}
+ getInputChannelInfo(mComponent, mMime, bitStreamInfo);
+ std::vector<C2FieldSupportedValues> supportedValues;
+ if (!Info.top().largeFrameInfo.empty()) {
+ if (!isLargeAudioFrameSupported(mComponent, supportedValues)) {
+ GTEST_SKIP() << "As component does not support large frame";
+ }
+ // time_sec * sample_rate * channel_count * 2 (bytes_per_channel)
+ mMaxOutputSize = 60 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ mOutputThresholdSize = 50 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ ASSERT_EQ(configureLargeFrameParams(mComponent, mMaxOutputSize,
+ mOutputThresholdSize, supportedValues), C2_OK);
+ }
ASSERT_EQ(mComponent->start(), C2_OK);
// request EOS for thumbnail
// signal EOS flag with last frame
- size_t i = -1;
- uint32_t flags;
- do {
- i++;
- flags = 0;
- if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
-
- } while (!(flags & SYNC_FRAME));
+ size_t i;
+ for (i = 0; i < Info.size(); i++) {
+ if (Info[i].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+ }
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
@@ -613,6 +793,18 @@
std::cout << "[ WARN ] Test Skipped \n";
return;
}
+ getInputChannelInfo(mComponent, mMime, bitStreamInfo);
+ std::vector<C2FieldSupportedValues> supportedValues;
+ if (!Info.top().largeFrameInfo.empty()) {
+ if (!isLargeAudioFrameSupported(mComponent, supportedValues)) {
+ GTEST_SKIP() << "As component does not support large frame";
+ }
+ // time_sec * sample_rate * channel_count * 2 (bytes_per_channel)
+ mMaxOutputSize = 60 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ mOutputThresholdSize = 50 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ ASSERT_EQ(configureLargeFrameParams(mComponent, mMaxOutputSize,
+ mOutputThresholdSize, supportedValues), C2_OK);
+ }
ASSERT_EQ(mComponent->start(), C2_OK);
// flush
std::list<std::unique_ptr<C2Work>> flushedWork;
@@ -643,14 +835,11 @@
mFlushedIndices.clear();
int index = numFramesFlushed;
bool keyFrame = false;
- uint32_t flags = 0;
while (index < (int)Info.size()) {
- if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
- if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
- flags = 0;
eleStream.ignore(Info[index].bytesCount);
index++;
}
@@ -684,24 +873,38 @@
int bytesCount = 0;
uint32_t frameId = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
+ uint32_t nLargeFrames = 0;
bool codecConfig = false;
// This test introduces empty CSD after every 20th frame
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20))
- flags = 32;
- else
- flags = 0;
+ vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
bytesCount = 0;
+ Info.push_back({bytesCount, vtsFlags, timestamp, {}});
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
- codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
+ Info.push_back({bytesCount, vtsFlags, timestamp, {}});
+ if ((vtsFlags & (1 << VTS_BIT_FLAG_LARGE_AUDIO_FRAME)) != 0) {
+ eleInfo >> nLargeFrames;
+ // this is a large audio frame.
+ while(nLargeFrames-- > 0) {
+ eleInfo >> bytesCount;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ Info.editItemAt(Info.size() - 1).largeFrameInfo.push_back(
+ {(uint32_t)bytesCount, vtsFlags, timestamp});
+ }
+ }
}
- Info.push_back({bytesCount, flags, timestamp});
frameId++;
}
eleInfo.close();
@@ -716,6 +919,18 @@
std::cout << "[ WARN ] Test Skipped \n";
return;
}
+ getInputChannelInfo(mComponent, mMime, bitStreamInfo);
+ std::vector<C2FieldSupportedValues> supportedValues;
+ if (!Info.top().largeFrameInfo.empty()) {
+ if (!isLargeAudioFrameSupported(mComponent, supportedValues)) {
+ GTEST_SKIP() << "As component does not support large frame";
+ }
+ // time_sec * sample_rate * channel_count * 2 (bytes_per_channel)
+ mMaxOutputSize = 60 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ mOutputThresholdSize = 50 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ ASSERT_EQ(configureLargeFrameParams(mComponent, mMaxOutputSize,
+ mOutputThresholdSize, supportedValues), C2_OK);
+ }
ASSERT_EQ(mComponent->start(), C2_OK);
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
@@ -771,7 +986,18 @@
std::cout << "[ WARN ] Test Skipped \n";
return;
}
-
+ getInputChannelInfo(mComponent, mMime, bitStreamInfo);
+ std::vector<C2FieldSupportedValues> supportedValues;
+ if (!Info.top().largeFrameInfo.empty()) {
+ if (!isLargeAudioFrameSupported(mComponent, supportedValues)) {
+ GTEST_SKIP() << "As component does not support large frame";
+ }
+ // time_sec * sample_rate * channel_count * 2 (bytes_per_channel)
+ mMaxOutputSize = 60 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ mOutputThresholdSize = 50 * bitStreamInfo[0] * bitStreamInfo[1] * 2;
+ ASSERT_EQ(configureLargeFrameParams(mComponent, mMaxOutputSize,
+ mOutputThresholdSize, supportedValues), C2_OK);
+ }
ASSERT_EQ(mComponent->start(), C2_OK);
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
@@ -869,4 +1095,4 @@
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
-}
+}
\ No newline at end of file
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml
index 6c04683..9b9c62f 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml
+++ b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml
@@ -38,6 +38,8 @@
<option name="push-file" key="bbb_opus_stereo_128kbps_48000hz.info" value="/data/local/tmp/media/bbb_opus_stereo_128kbps_48000hz.info" />
<option name="push-file" key="bbb_opus_stereo_128kbps_48000hz.opus" value="/data/local/tmp/media/bbb_opus_stereo_128kbps_48000hz.opus" />
<option name="push-file" key="bbb_raw_1ch_8khz_s32le.info" value="/data/local/tmp/media/bbb_raw_1ch_8khz_s32le.info" />
+ <option name="push-file" key="bbb_raw_1ch_8khz_s32le_largeframe.info"
+ value="/data/local/tmp/media/bbb_raw_1ch_8khz_s32le_largeframe.info" />
<option name="push-file" key="bbb_raw_1ch_8khz_s32le.raw" value="/data/local/tmp/media/bbb_raw_1ch_8khz_s32le.raw" />
<option name="push-file" key="bbb_vorbis_stereo_128kbps_48000hz.info" value="/data/local/tmp/media/bbb_vorbis_stereo_128kbps_48000hz.info" />
<option name="push-file" key="bbb_vorbis_stereo_128kbps_48000hz.vorbis" value="/data/local/tmp/media/bbb_vorbis_stereo_128kbps_48000hz.vorbis" />
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index f36bc41..92b0bf5 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -233,18 +233,36 @@
int32_t numCsds = 0;
int32_t bytesCount = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
+ uint32_t nLargeFrames = 0;
while (1) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ if (vtsFlags == 0xFF) {
+ ALOGE("unrecognized flag(0x%x) entry in info file %s", flags, info.c_str());
+ return -1;
+ }
eleInfo >> timestamp;
- bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0 ;
if (codecConfig) numCsds++;
- bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+ bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
timestampUslist->push_back(timestamp);
}
- frameInfo->push_back({bytesCount, flags, timestamp});
+ frameInfo->push_back({bytesCount, vtsFlags, timestamp, {}});
+ if (vtsFlags & (1 << VTS_BIT_FLAG_LARGE_AUDIO_FRAME)) {
+ eleInfo >> nLargeFrames;
+ while(nLargeFrames-- > 0) {
+ eleInfo >> bytesCount;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ frameInfo->editItemAt(frameInfo->size() - 1).largeFrameInfo.push_back(
+ {vtsFlags, static_cast<uint32_t>(bytesCount), timestamp});
+ }
+ }
}
ALOGV("numCsds : %d", numCsds);
eleInfo.close();
@@ -273,3 +291,12 @@
ASSERT_EQ(flushedIndices.empty(), true);
flushedWork.clear();
}
+
+int mapInfoFlagstoVtsFlags(int infoFlags) {
+ if (infoFlags == 0) return 0;
+ else if (infoFlags == 0x1) return (1 << VTS_BIT_FLAG_SYNC_FRAME);
+ else if (infoFlags == 0x10) return (1 << VTS_BIT_FLAG_NO_SHOW_FRAME);
+ else if (infoFlags == 0x20) return (1 << VTS_BIT_FLAG_CSD_FRAME);
+ else if (infoFlags == 0x40) return (1 << VTS_BIT_FLAG_LARGE_AUDIO_FRAME);
+ return 0xFF;
+}
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 48e80a4..708fe15 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -29,9 +29,6 @@
#include <chrono>
#include <fstream>
-#define FLAG_NON_DISPLAY_FRAME (1 << 4)
-#define FLAG_CONFIG_DATA (1 << 5)
-
#define MAX_RETRY 20
#define TIME_OUT 400ms
#define MAX_INPUT_BUFFERS 8
@@ -53,10 +50,20 @@
// Component name prefix
extern std::string sComponentNamePrefix;
+enum c2_vts_flags_t {
+ VTS_BIT_FLAG_SYNC_FRAME = 1,
+ VTS_BIT_FLAG_NO_SHOW_FRAME = 2,
+ VTS_BIT_FLAG_CSD_FRAME = 3,
+ VTS_BIT_FLAG_LARGE_AUDIO_FRAME = 4,
+};
+
struct FrameInfo {
int bytesCount;
- uint32_t flags;
+ uint32_t vtsFlags;
int64_t timestamp;
+ // This is used when access-units are marked with
+ // VTS_BIT_FLAG_LARGE_AUDIO_FRAME
+ std::vector<C2AccessUnitInfosStruct> largeFrameInfo;
};
template <typename... T>
@@ -83,7 +90,6 @@
virtual void onWorkDone(const std::weak_ptr<android::Codec2Client::Component>& comp,
std::list<std::unique_ptr<C2Work>>& workItems) override {
/* TODO */
- ALOGD("onWorkDone called");
(void)comp;
if (callBack) callBack(workItems);
}
@@ -100,7 +106,6 @@
uint32_t errorCode) override {
/* TODO */
(void)comp;
- ALOGD("onError called");
if (errorCode != 0) ALOGE("Error : %u", errorCode);
}
@@ -165,4 +170,7 @@
void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
std::list<std::unique_ptr<C2Work>>& workQueue,
std::list<uint64_t>& flushedIndices, std::mutex& queueLock);
+
+int mapInfoFlagstoVtsFlags(int infoFlags);
+
#endif // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le_largeframe.info b/media/codec2/hal/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le_largeframe.info
new file mode 100644
index 0000000..291e323
--- /dev/null
+++ b/media/codec2/hal/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le_largeframe.info
@@ -0,0 +1,5 @@
+16384 64 0 1 16384 1 0
+49152 64 1024000 3 16384 1 1024000 16384 1 2048000 16384 1 3072000
+32768 64 4096000 2 16384 1 4096000 16384 1 5120000
+49152 64 6144000 3 16384 1 6144000 16384 1 7168000 16384 1 8192000
+10924 64 9216000 1 10924 1 9216000
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 2cf0d6e..f8fd425 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -29,6 +29,7 @@
#include <C2BufferPriv.h>
#include <C2Config.h>
#include <C2Debug.h>
+#include <codec2/common/HalSelection.h>
#include <codec2/hidl/client.h>
#include <gui/BufferQueue.h>
#include <gui/IConsumerListener.h>
@@ -120,8 +121,8 @@
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(
- mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+ getBufferPoolVer());
ASSERT_NE(mLinearPool, nullptr);
std::vector<std::unique_ptr<C2Param>> queried;
@@ -407,30 +408,45 @@
surfaceMode_t surfMode) {
using namespace android;
sp<IGraphicBufferProducer> producer = nullptr;
+ sp<IGraphicBufferConsumer> consumer = nullptr;
+ sp<GLConsumer> texture = nullptr;
+ sp<ANativeWindow> surface = nullptr;
static std::atomic_uint32_t surfaceGeneration{0};
uint32_t generation =
(getpid() << 10) |
((surfaceGeneration.fetch_add(1, std::memory_order_relaxed) + 1) & ((1 << 10) - 1));
int32_t maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
+ C2BlockPool::local_id_t poolId = C2BlockPool::BASIC_GRAPHIC;
+ std::shared_ptr<Codec2Client::Configurable> configurable;
+ bool aidl = ::android::IsCodec2AidlHalSelected();
+ if (aidl) {
+ // AIDL does not support blockpool-less mode.
+ c2_status_t poolRet = component->createBlockPool(
+ C2PlatformAllocatorStore::IGBA, &poolId, &configurable);
+ ASSERT_EQ(poolRet, C2_OK) << "setOutputSurface failed";
+ }
+
if (surfMode == SURFACE) {
- sp<IGraphicBufferConsumer> consumer = nullptr;
BufferQueue::createBufferQueue(&producer, &consumer);
ASSERT_NE(producer, nullptr) << "createBufferQueue returned invalid producer";
ASSERT_NE(consumer, nullptr) << "createBufferQueue returned invalid consumer";
- sp<GLConsumer> texture =
+ texture =
new GLConsumer(consumer, 0 /* tex */, GLConsumer::TEXTURE_EXTERNAL,
true /* useFenceSync */, false /* isControlledByApp */);
- sp<ANativeWindow> gSurface = new Surface(producer);
- ASSERT_NE(gSurface, nullptr) << "getSurface failed";
+ surface = new Surface(producer);
+ ASSERT_NE(surface, nullptr) << "failed to create Surface object";
producer->setGenerationNumber(generation);
}
- c2_status_t err = component->setOutputSurface(C2BlockPool::BASIC_GRAPHIC, producer, generation,
+ c2_status_t err = component->setOutputSurface(poolId, producer, generation,
maxDequeueBuffers);
- ASSERT_EQ(err, C2_OK) << "setOutputSurface failed";
+ std::string surfStr = surfMode == NO_SURFACE ? "NO_SURFACE" :
+ (surfMode == NULL_SURFACE ? "NULL_SURFACE" : "WITH_SURFACE");
+
+ ASSERT_EQ(err, C2_OK) << "setOutputSurface failed, surfMode: " << surfStr;
}
void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
@@ -463,7 +479,9 @@
}
int64_t timestamp = (*Info)[frameID].timestamp;
- flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+ flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+ ? C2FrameData::FLAG_CODEC_CONFIG
+ : 0;
if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
@@ -711,17 +729,19 @@
ASSERT_EQ(eleInfo.is_open(), true) << mInputFile << " - file not found";
int bytesCount = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
uint32_t timestampMax = 0;
while (1) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
timestamp += timestampOffset;
- Info.push_back({bytesCount, flags, timestamp});
- bool codecConfig =
- flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
- bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+ Info.push_back({bytesCount, vtsFlags, timestamp, {}});
+ bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
+ bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
{
ULock l(mQueueLock);
@@ -795,20 +815,15 @@
int32_t numCsds = populateInfoVector(mInfoFile, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << mInfoFile;
- uint32_t flags = 0;
for (size_t i = 0; i < MAX_ITERATIONS; i++) {
ASSERT_EQ(mComponent->start(), C2_OK);
// request EOS for thumbnail
// signal EOS flag with last frame
size_t j = -1;
- do {
- j++;
- flags = 0;
- if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
-
- } while (!(flags & SYNC_FRAME));
-
+ for (j = 0; j < Info.size(); j++) {
+ if (Info[j].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+ }
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
@@ -908,14 +923,11 @@
// Seek to next key frame and start decoding till the end
int index = numFramesFlushed;
bool keyFrame = false;
- uint32_t flags = 0;
while (index < (int)Info.size()) {
- if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
- if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
- flags = 0;
eleStream.ignore(Info[index].bytesCount);
index++;
}
@@ -949,24 +961,24 @@
int bytesCount = 0;
uint32_t frameId = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
bool codecConfig = false;
// This test introduces empty CSD after every 20th frame
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20))
- flags = 32;
- else
- flags = 0;
+ vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
bytesCount = 0;
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
- codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
}
- Info.push_back({bytesCount, flags, timestamp});
+ Info.push_back({bytesCount, vtsFlags, timestamp, {}});
frameId++;
}
eleInfo.close();
@@ -1046,12 +1058,9 @@
}
int offset = framesToDecode;
- uint32_t flags = 0;
while (1) {
while (offset < (int)Info.size()) {
- flags = 0;
- if (Info[offset].flags) flags = 1u << (Info[offset].flags - 1);
- if (flags & SYNC_FRAME) {
+ if (Info[offset].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index fbb4f18..8ecb9c0 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <android/binder_process.h>
+#include <codec2/common/HalSelection.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <stdio.h>
@@ -71,7 +72,9 @@
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &mGraphicAllocator),
C2_OK);
- mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator, mBlockPoolId++);
+ C2PooledBlockPool::BufferPoolVer ver = ::android::IsCodec2AidlHalSelected() ?
+ C2PooledBlockPool::VER_AIDL2 : C2PooledBlockPool::VER_HIDL;
+ mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator, mBlockPoolId++, ver);
ASSERT_NE(mGraphicPool, nullptr);
std::vector<std::unique_ptr<C2Param>> queried;
diff --git a/media/codec2/hal/hidl/1.1/utils/Android.bp b/media/codec2/hal/hidl/1.1/utils/Android.bp
index 4f86511..d8b5db5 100644
--- a/media/codec2/hal/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.1/utils/Android.bp
@@ -54,7 +54,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
@@ -66,7 +65,6 @@
"com.android.media.swcodec",
],
-
defaults: ["hidl_defaults"],
srcs: [
@@ -97,6 +95,7 @@
"android.hardware.media.omx@1.0",
"libbase",
"libcodec2",
+ "libcodec2_hal_common",
"libcodec2_hidl@1.0",
"libcodec2_hidl_plugin_stub",
"libcodec2_vndk",
@@ -173,4 +172,3 @@
"libhidlbase",
],
}
-
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index d0f4f19..5073983 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -29,6 +29,8 @@
#include <hidl/HidlBinderSupport.h>
#include <utils/Timers.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2BqBufferPriv.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -44,6 +46,8 @@
namespace utils {
using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
@@ -135,6 +139,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +258,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -252,6 +326,19 @@
registerFrameData(mListener, work->input);
}
}
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,6 +348,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +559,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +572,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -508,7 +604,12 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
index f16de24..8f0478f 100644
--- a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
+++ b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
@@ -29,6 +29,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -57,6 +59,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -118,6 +122,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_1::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -140,6 +146,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.2/utils/Android.bp b/media/codec2/hal/hidl/1.2/utils/Android.bp
index b92dc07..a339946 100644
--- a/media/codec2/hal/hidl/1.2/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.2/utils/Android.bp
@@ -58,7 +58,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
@@ -102,6 +101,7 @@
"android.hardware.media.omx@1.0",
"libbase",
"libcodec2",
+ "libcodec2_hal_common",
"libcodec2_hidl@1.0",
"libcodec2_hidl@1.1",
"libcodec2_hidl_plugin_stub",
@@ -197,4 +197,3 @@
name: "libcodec2-hidl-client-defaults",
defaults: ["libcodec2-hidl-client-defaults@1.2"],
}
-
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 036c900..bbdbef5 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -44,6 +44,8 @@
namespace utils {
using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
@@ -135,6 +137,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +256,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -252,7 +324,19 @@
registerFrameData(mListener, work->input);
}
}
-
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,7 +345,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
-
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
if (work) {
@@ -469,6 +555,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +568,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -539,7 +631,12 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
index 6a73392..f2b77bc 100644
--- a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
+++ b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
@@ -29,6 +29,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -57,6 +59,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -123,6 +127,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_2::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -145,6 +151,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 6b45e0e..7b1721e 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -90,6 +90,28 @@
return v == "true";
}
+// Flags can come with individual BufferInfos
+// when used with large frame audio
+constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+ {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+ {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+ {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+};
+
+static uint32_t convertFlags(uint32_t flags, bool toC2) {
+ return std::transform_reduce(
+ flagList.begin(), flagList.end(),
+ 0u,
+ std::bit_or{},
+ [flags, toC2](const std::pair<uint32_t, uint32_t> &entry) {
+ if (toC2) {
+ return (flags & entry.first) ? entry.second : 0;
+ } else {
+ return (flags & entry.second) ? entry.first : 0;
+ }
+ });
+}
+
} // namespace
CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -245,7 +267,8 @@
if (buffer->meta()->findInt32("decode-only", &tmp) && tmp) {
flags |= C2FrameData::FLAG_DROP_FRAME;
}
- ALOGV("[%s] queueInputBuffer: buffer->size() = %zu", mName, buffer->size());
+ ALOGV("[%s] queueInputBuffer: buffer->size() = %zu time: %lld",
+ mName, buffer->size(), (long long)timeUs);
std::list<std::unique_ptr<C2Work>> items;
std::unique_ptr<C2Work> work(new C2Work);
work->input.ordinal.timestamp = timeUs;
@@ -296,6 +319,34 @@
uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
output->rotation[frameIndex] = rotation;
}
+ sp<RefBase> obj;
+ if (buffer->meta()->findObject("accessUnitInfo", &obj)) {
+ ALOGV("Filling C2Info from multiple access units");
+ sp<WrapperObject<std::vector<AccessUnitInfo>>> infos{
+ (decltype(infos.get()))obj.get()};
+ std::vector<AccessUnitInfo> &accessUnitInfoVec = infos->value;
+ std::vector<C2AccessUnitInfosStruct> multipleAccessUnitInfos;
+ uint32_t outFlags = 0;
+ for (int i = 0; i < accessUnitInfoVec.size(); i++) {
+ outFlags = 0;
+ outFlags = convertFlags(accessUnitInfoVec[i].mFlags, true);
+ if (eos && (outFlags & C2FrameData::FLAG_END_OF_STREAM)) {
+ outFlags &= (~C2FrameData::FLAG_END_OF_STREAM);
+ }
+ multipleAccessUnitInfos.emplace_back(
+ outFlags,
+ accessUnitInfoVec[i].mSize,
+ accessUnitInfoVec[i].mTimestamp);
+ ALOGV("%d) flags: %d, size: %d, time: %llu",
+ i, outFlags, accessUnitInfoVec[i].mSize,
+ (long long)accessUnitInfoVec[i].mTimestamp);
+
+ }
+ const std::shared_ptr<C2AccessUnitInfos::input> c2AccessUnitInfos =
+ C2AccessUnitInfos::input::AllocShared(
+ multipleAccessUnitInfos.size(), 0u, multipleAccessUnitInfos);
+ c2buffer->setInfo(c2AccessUnitInfos);
+ }
work->input.buffers.push_back(c2buffer);
if (encryptedBlock) {
work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(
@@ -1604,8 +1655,14 @@
padding = 0;
}
if (delay || padding) {
- // We need write access to the buffers, and we're already in
- // array mode.
+ // We need write access to the buffers, so turn them into array mode.
+ // TODO: b/321930152 - define SkipCutOutputBuffers that takes output from
+ // component, runs it through SkipCutBuffer and allocate local buffer to be
+ // used by fwk. Make initSkipCutBuffer() return OutputBuffers similar to
+ // toArrayMode().
+ if (!output->buffers->isArrayMode()) {
+ output->buffers = output->buffers->toArrayMode(numOutputSlots);
+ }
output->buffers->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
}
}
@@ -2259,12 +2316,34 @@
case OutputBuffers::DISCARD:
break;
case OutputBuffers::NOTIFY_CLIENT:
+ {
// TRICKY: we want popped buffers reported in order, so sending
// the callback while holding the lock here. This assumes that
// onOutputBufferAvailable() does not block. onOutputBufferAvailable()
// callbacks are always sent with the Output lock held.
+ if (c2Buffer) {
+ std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+ std::static_pointer_cast<const C2AccessUnitInfos::output>(
+ c2Buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+ if (bufferMetadata && bufferMetadata->flexCount() > 0) {
+ uint32_t flag = 0;
+ std::vector<AccessUnitInfo> accessUnitInfos;
+ for (int nMeta = 0; nMeta < bufferMetadata->flexCount(); nMeta++) {
+ const C2AccessUnitInfosStruct &bufferMetadataStruct =
+ bufferMetadata->m.values[nMeta];
+ flag = convertFlags(bufferMetadataStruct.flags, false);
+ accessUnitInfos.emplace_back(flag,
+ static_cast<size_t>(bufferMetadataStruct.size),
+ static_cast<size_t>(bufferMetadataStruct.timestamp));
+ }
+ sp<WrapperObject<std::vector<AccessUnitInfo>>> obj{
+ new WrapperObject<std::vector<AccessUnitInfo>>{accessUnitInfos}};
+ outBuffer->meta()->setObject("accessUnitInfo", obj);
+ }
+ }
mCallback->onOutputBufferAvailable(index, outBuffer);
break;
+ }
case OutputBuffers::REALLOCATE:
if (++reallocTryNum > kMaxReallocTry) {
output.unlock();
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 670923b..8a48777 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -18,11 +18,14 @@
#define LOG_TAG "CCodecBuffers"
#include <utils/Log.h>
+#include <numeric>
+
#include <C2AllocatorGralloc.h>
#include <C2PlatformSupport.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/CodecBase.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <mediadrm/ICrypto.h>
@@ -147,6 +150,165 @@
return copy;
}
+// MultiAccessUnitSkipCutBuffer for buffer and bufferInfos
+
+class MultiAccessUnitSkipCutBuffer : public SkipCutBuffer {
+
+public:
+ explicit MultiAccessUnitSkipCutBuffer(
+ int32_t skip, int32_t cut, size_t num16BitChannels):
+ SkipCutBuffer(skip, cut, num16BitChannels),
+ mFrontPaddingDelay(0), mSize(0) {
+ }
+
+ virtual ~MultiAccessUnitSkipCutBuffer() {
+
+ }
+
+ void submitMultiAccessUnits(
+ const sp<MediaCodecBuffer>& buffer,
+ int32_t sampleRate, size_t num16BitChannels,
+ std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+ if (infos == nullptr) {
+ // there is nothing to do more.
+ SkipCutBuffer::submit(buffer);
+ return;
+ }
+ typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+ CHECK_EQ(mSize, SkipCutBuffer::size());
+ sp<BufferInfosWrapper> bufferInfos{new BufferInfosWrapper(decltype(bufferInfos->value)())};
+ uint32_t availableSize = buffer->size() + SkipCutBuffer::size();
+ uint32_t frontPadding = mFrontPadding;
+ int32_t lastEmptyAccessUnitIndex = -1;
+ int64_t byteInUs = 0;
+ if (sampleRate > 0 && num16BitChannels > 0) {
+ byteInUs = (1000000u / (sampleRate * num16BitChannels * 2));
+ }
+ if (frontPadding > 0) {
+ mInfos.clear();
+ mSize = 0;
+ }
+ for (int i = 0 ; i < infos->flexCount() && frontPadding > 0; i++) {
+ uint32_t flagsInPadding = 0;
+ int64_t timeInPadding = 0;
+ if (infos->m.values[i].size <= frontPadding) {
+ // we have more front padding so this buffer is not going to be used.
+ int32_t consumed = infos->m.values[i].size;
+ frontPadding -= consumed;
+ mFrontPaddingDelay += byteInUs * (consumed);
+ availableSize -= consumed;
+ flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+ timeInPadding = infos->m.values[i].timestamp;
+ } else {
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ mFrontPaddingDelay += byteInUs * (frontPadding);
+ info.size -= frontPadding;
+ info.timestamp -= mFrontPaddingDelay;
+ availableSize -= frontPadding;
+ flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+ timeInPadding = infos->m.values[i].timestamp;
+ frontPadding = 0;
+ mInfos.push_back(info);
+ mSize += info.size;
+ }
+ if (flagsInPadding != 0) {
+ bufferInfos->value.emplace_back(
+ flagsInPadding, 0, timeInPadding);
+ }
+ lastEmptyAccessUnitIndex = i;
+ }
+ if (frontPadding <= 0) {
+ // process what's already in the buffer first
+ auto it = mInfos.begin();
+ while (it != mInfos.end() && availableSize > mBackPadding) {
+ // we have samples to send out.
+ if ((availableSize - it->size) >= mBackPadding) {
+ // this is totally used here.
+ int32_t consumed = it->size;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(it->flags), consumed, it->timestamp);
+ availableSize -= consumed;
+ mSize -= consumed;
+ it = mInfos.erase(it);
+ } else {
+ int32_t consumed = availableSize - mBackPadding;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(it->flags),
+ consumed,
+ it->timestamp);
+ it->size -= consumed;
+ it->timestamp += consumed * byteInUs;
+ availableSize -= consumed;
+ mSize -= consumed;
+ it++;
+ }
+ }
+ // if buffer has more process all of it and keep the remaining info.
+ for (int i = (lastEmptyAccessUnitIndex + 1) ; i < infos->flexCount() ; i++) {
+ // upddate updatedInfo and mInfos
+ if (availableSize > mBackPadding) {
+ // we have to take data from the new buffer.
+ if (availableSize - infos->m.values[i].size >= mBackPadding) {
+ // we are using this info
+ int32_t consumed = infos->m.values[i].size;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(infos->m.values[i].flags),
+ consumed,
+ infos->m.values[i].timestamp - mFrontPaddingDelay);
+ availableSize -= consumed;
+ } else {
+ // if we need to update the size
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ int32_t consumed = availableSize - mBackPadding;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(infos->m.values[i].flags),
+ consumed,
+ infos->m.values[i].timestamp - mFrontPaddingDelay);
+ info.size -= consumed;
+ info.timestamp = info.timestamp - mFrontPaddingDelay +
+ consumed * byteInUs;
+ mInfos.push_back(info);
+ availableSize -= consumed;
+ mSize += info.size;
+ }
+ } else {
+ // we have to maintain infos
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ info.timestamp -= mFrontPaddingDelay;
+ mInfos.push_back(info);
+ mSize += info.size;
+ }
+ }
+ }
+ SkipCutBuffer::submit(buffer);
+ infos = nullptr;
+ if (!bufferInfos->value.empty()) {
+ buffer->meta()->setObject("accessUnitInfo", bufferInfos);
+ }
+ }
+protected:
+ // Flags can come with individual BufferInfos
+ // when used with large frame audio
+ constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+ {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+ {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+ {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+ };
+
+ static uint32_t toMediaCodecFlags(uint32_t flags) {
+ return std::transform_reduce(
+ flagList.begin(), flagList.end(),
+ 0u,
+ std::bit_or{},
+ [flags](const std::pair<uint32_t, uint32_t> &entry) {
+ return (flags & entry.second) ? entry.first : 0;
+ });
+ }
+ std::list<C2AccessUnitInfosStruct> mInfos;
+ int64_t mFrontPaddingDelay;
+ size_t mSize;
+};
+
// OutputBuffers
OutputBuffers::OutputBuffers(const char *componentName, const char *name)
@@ -201,6 +363,15 @@
}
}
+bool OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+ int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+ if (mSkipCutBuffer == nullptr) {
+ return false;
+ }
+ mSkipCutBuffer->submitMultiAccessUnits(buffer, sampleRate, channelCount, infos);
+ return true;
+}
+
void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut) {
if (mSkipCutBuffer != nullptr) {
size_t prevSize = mSkipCutBuffer->size();
@@ -208,7 +379,7 @@
ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
}
}
- mSkipCutBuffer = new SkipCutBuffer(skip, cut, mChannelCount);
+ mSkipCutBuffer = new MultiAccessUnitSkipCutBuffer(skip, cut, mChannelCount);
}
bool OutputBuffers::convert(
@@ -1160,7 +1331,16 @@
ALOGD("[%s] copy buffer failed", mName);
return WOULD_BLOCK;
}
- submit(c2Buffer);
+ if (buffer && buffer->hasInfo(C2AccessUnitInfos::output::PARAM_TYPE)) {
+ std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+ std::static_pointer_cast<const C2AccessUnitInfos::output>(
+ buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+ if (submit(c2Buffer, mSampleRate, mChannelCount, bufferMetadata)) {
+ buffer->removeInfo(C2AccessUnitInfos::output::PARAM_TYPE);
+ }
+ } else {
+ submit(c2Buffer);
+ }
handleImageData(c2Buffer);
*clientBuffer = c2Buffer;
ALOGV("[%s] grabbed buffer %zu", mName, *index);
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index cbef644..f0936bc 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -20,6 +20,7 @@
#include <optional>
#include <string>
+#include <vector>
#include <C2Config.h>
#include <DataConverter.h>
@@ -33,6 +34,8 @@
struct ICrypto;
class MemoryDealer;
class SkipCutBuffer;
+class MultiAccessUnitSkipCutBuffer;
+struct AccessUnitInfo;
constexpr size_t kLinearBufferSize = 1048576;
// This can fit an 8K frame.
@@ -382,13 +385,17 @@
sp<MediaCodecBuffer>* outBuffer);
protected:
- sp<SkipCutBuffer> mSkipCutBuffer;
+
+ sp<MultiAccessUnitSkipCutBuffer> mSkipCutBuffer;
/**
* Update the SkipCutBuffer object. No-op if it's never initialized.
*/
void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
+ bool submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+ int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos);
+
/**
* Submit buffer to SkipCutBuffer object, if initialized.
*/
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 6d49fa8..c22deca 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -402,10 +402,19 @@
add(ConfigMapper(KEY_MAX_INPUT_SIZE, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE, "value")
.limitTo(D::INPUT));
+
// remove when codecs switch to PARAMKEY
deprecated(ConfigMapper(KEY_MAX_INPUT_SIZE, "coded.max-frame-size", "value")
.limitTo(D::INPUT));
+ // large frame params
+ add(ConfigMapper(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE,
+ C2_PARAMKEY_OUTPUT_LARGE_FRAME, "max-size")
+ .limitTo(D::AUDIO & D::OUTPUT));
+ add(ConfigMapper(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+ C2_PARAMKEY_OUTPUT_LARGE_FRAME, "threshold-size")
+ .limitTo(D::AUDIO & D::OUTPUT));
+
// Rotation
// Note: SDK rotation is clock-wise, while C2 rotation is counter-clock-wise
add(ConfigMapper(KEY_ROTATION, C2_PARAMKEY_VUI_ROTATION, "value")
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 3bb6593..5c1755e 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -1049,7 +1049,11 @@
// Unwrap raw buffer handle from the C2Handle
native_handle_t *nh = UnwrapNativeCodec2GrallocHandle(handle);
if (!nh) {
- return;
+ nh = UnwrapNativeCodec2AhwbHandle(handle);
+ if (!nh) {
+ ALOGE("handle is not compatible to neither C2HandleGralloc nor C2HandleAhwb");
+ return;
+ }
}
// Import the raw handle so IMapper can use the buffer. The imported
// handle must be freed when the client is done with the buffer.
diff --git a/media/codec2/tests/aidl/Android.bp b/media/codec2/tests/aidl/Android.bp
new file mode 100644
index 0000000..2ad245c
--- /dev/null
+++ b/media/codec2/tests/aidl/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+ name: "c2aidl_gtracker_test",
+ test_suites: ["device-tests"],
+ defaults: [
+ "libcodec2-aidl-client-defaults",
+ ],
+
+ header_libs: [
+ "libcodec2_client_headers",
+ "libcodec2_internal",
+ "libcodec2_vndk_headers",
+ ],
+
+ srcs: [
+ "GraphicsTracker_test.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcodec2_client",
+ "libgui",
+ "libnativewindow",
+ "libui",
+ ],
+}
diff --git a/media/codec2/tests/aidl/GraphicsTracker_test.cpp b/media/codec2/tests/aidl/GraphicsTracker_test.cpp
new file mode 100644
index 0000000..9008086
--- /dev/null
+++ b/media/codec2/tests/aidl/GraphicsTracker_test.cpp
@@ -0,0 +1,820 @@
+/*
+ * 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.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "GraphicsTracker_test"
+#include <unistd.h>
+
+#include <android/hardware_buffer.h>
+#include <codec2/aidl/GraphicsTracker.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
+#include <gui/IConsumerListener.h>
+#include <gui/Surface.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <C2BlockInternal.h>
+#include <C2FenceFactory.h>
+
+#include <atomic>
+#include <memory>
+#include <iostream>
+#include <thread>
+
+using ::aidl::android::hardware::media::c2::implementation::GraphicsTracker;
+using ::android::BufferItem;
+using ::android::BufferQueue;
+using ::android::Fence;
+using ::android::GraphicBuffer;
+using ::android::IGraphicBufferProducer;
+using ::android::IGraphicBufferConsumer;
+using ::android::IProducerListener;
+using ::android::IConsumerListener;
+using ::android::OK;
+using ::android::sp;
+using ::android::wp;
+
+namespace {
+struct BqStatistics {
+ std::atomic<int> mDequeued;
+ std::atomic<int> mQueued;
+ std::atomic<int> mBlocked;
+ std::atomic<int> mDropped;
+ std::atomic<int> mDiscarded;
+ std::atomic<int> mReleased;
+
+ void log() {
+ ALOGD("Dequeued: %d, Queued: %d, Blocked: %d, "
+ "Dropped: %d, Discarded %d, Released %d",
+ (int)mDequeued, (int)mQueued, (int)mBlocked,
+ (int)mDropped, (int)mDiscarded, (int)mReleased);
+ }
+
+ void clear() {
+ mDequeued = 0;
+ mQueued = 0;
+ mBlocked = 0;
+ mDropped = 0;
+ mDiscarded = 0;
+ mReleased = 0;
+ }
+};
+
+struct DummyConsumerListener : public android::BnConsumerListener {
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+};
+
+struct TestConsumerListener : public android::BnConsumerListener {
+ TestConsumerListener(const sp<IGraphicBufferConsumer> &consumer)
+ : BnConsumerListener(), mConsumer(consumer) {}
+ void onFrameAvailable(const BufferItem&) override {
+ constexpr static int kRenderDelayUs = 1000000/30; // 30fps
+ BufferItem buffer;
+ // consume buffer
+ 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);
+ }
+ }
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+
+ wp<IGraphicBufferConsumer> mConsumer;
+};
+
+struct TestProducerListener : public android::BnProducerListener {
+ TestProducerListener(std::shared_ptr<GraphicsTracker> tracker,
+ std::shared_ptr<BqStatistics> &stat,
+ uint32_t generation) : BnProducerListener(),
+ mTracker(tracker), mStat(stat), mGeneration(generation) {}
+ virtual void onBufferReleased() override {
+ auto tracker = mTracker.lock();
+ if (tracker) {
+ mStat->mReleased++;
+ tracker->onReleased(mGeneration);
+ }
+ }
+ virtual bool needsReleaseNotify() override { return true; }
+ virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
+
+ std::weak_ptr<GraphicsTracker> mTracker;
+ std::shared_ptr<BqStatistics> mStat;
+ uint32_t mGeneration;
+};
+
+struct Frame {
+ AHardwareBuffer *buffer_;
+ sp<Fence> fence_;
+
+ Frame() : buffer_{nullptr}, fence_{nullptr} {}
+ Frame(AHardwareBuffer *buffer, sp<Fence> fence)
+ : buffer_(buffer), fence_(fence) {}
+ ~Frame() {
+ if (buffer_) {
+ AHardwareBuffer_release(buffer_);
+ }
+ }
+};
+
+struct FrameQueue {
+ bool mStopped;
+ bool mDrain;
+ std::queue<std::shared_ptr<Frame>> mQueue;
+ std::mutex mMutex;
+ std::condition_variable mCond;
+
+ FrameQueue() : mStopped{false}, mDrain{false} {}
+
+ bool queueItem(AHardwareBuffer *buffer, sp<Fence> fence) {
+ std::shared_ptr<Frame> frame = std::make_shared<Frame>(buffer, fence);
+ if (mStopped) {
+ return false;
+ }
+ if (!frame) {
+ return false;
+ }
+ std::unique_lock<std::mutex> l(mMutex);
+ mQueue.emplace(frame);
+ l.unlock();
+ mCond.notify_all();
+ return true;
+ }
+
+ void stop(bool drain = false) {
+ bool stopped = false;
+ {
+ std::unique_lock<std::mutex> l(mMutex);
+ if (!mStopped) {
+ mStopped = true;
+ mDrain = drain;
+ stopped = true;
+ }
+ l.unlock();
+ if (stopped) {
+ mCond.notify_all();
+ }
+ }
+ }
+
+ bool waitItem(std::shared_ptr<Frame> *frame) {
+ while(true) {
+ std::unique_lock<std::mutex> l(mMutex);
+ if (!mDrain && mStopped) {
+ // stop without consuming the queue.
+ return false;
+ }
+ if (!mQueue.empty()) {
+ *frame = mQueue.front();
+ mQueue.pop();
+ return true;
+ } else if (mStopped) {
+ // stop after consuming the queue.
+ return false;
+ }
+ mCond.wait(l);
+ }
+ }
+};
+
+} // namespace anonymous
+
+class GraphicsTrackerTest : public ::testing::Test {
+public:
+ const uint64_t kTestUsageFlag = GRALLOC_USAGE_SW_WRITE_OFTEN;
+
+ void queueBuffer(FrameQueue *queue) {
+ while (true) {
+ std::shared_ptr<Frame> frame;
+ if (!queue->waitItem(&frame)) {
+ break;
+ }
+ uint64_t bid;
+ if (__builtin_available(android __ANDROID_API_T__, *)) {
+ if (AHardwareBuffer_getId(frame->buffer_, &bid) !=
+ android::NO_ERROR) {
+ break;
+ }
+ } else {
+ break;
+ }
+ android::status_t ret = frame->fence_->wait(-1);
+ if (ret != android::NO_ERROR) {
+ mTracker->deallocate(bid, frame->fence_);
+ mBqStat->mDiscarded++;
+ continue;
+ }
+
+ std::shared_ptr<C2GraphicBlock> blk =
+ _C2BlockFactory::CreateGraphicBlock(frame->buffer_);
+ if (!blk) {
+ mTracker->deallocate(bid, Fence::NO_FENCE);
+ mBqStat->mDiscarded++;
+ continue;
+ }
+ IGraphicBufferProducer::QueueBufferInput input(
+ 0, false,
+ HAL_DATASPACE_UNKNOWN, android::Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput output{};
+ c2_status_t res = mTracker->render(
+ blk->share(C2Rect(1, 1), C2Fence()),
+ input, &output);
+ if (res != C2_OK) {
+ mTracker->deallocate(bid, Fence::NO_FENCE);
+ mBqStat->mDiscarded++;
+ continue;
+ }
+ if (output.bufferReplaced) {
+ mBqStat->mDropped++;
+ }
+ mBqStat->mQueued++;
+ }
+ }
+
+ void stopTrackerAfterUs(int us) {
+ ::usleep(us);
+ mTracker->stop();
+ }
+
+protected:
+ bool init(int maxDequeueCount) {
+ mTracker = GraphicsTracker::CreateGraphicsTracker(maxDequeueCount);
+ if (!mTracker) {
+ return false;
+ }
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ if (!mProducer || !mConsumer) {
+ return false;
+ }
+ return true;
+ }
+ bool configure(sp<IProducerListener> producerListener,
+ sp<IConsumerListener> consumerListener,
+ int maxAcquiredCount = 1, bool controlledByApp = true) {
+ if (mConsumer->consumerConnect(
+ consumerListener, controlledByApp) != ::android::NO_ERROR) {
+ return false;
+ }
+ if (mConsumer->setMaxAcquiredBufferCount(maxAcquiredCount) != ::android::NO_ERROR) {
+ return false;
+ }
+ IGraphicBufferProducer::QueueBufferOutput qbo{};
+ if (mProducer->connect(producerListener,
+ NATIVE_WINDOW_API_MEDIA, true, &qbo) != ::android::NO_ERROR) {
+ return false;
+ }
+ if (mProducer->setDequeueTimeout(0) != ::android::NO_ERROR) {
+ return false;
+ }
+ return true;
+ }
+
+ virtual void TearDown() override {
+ mBqStat->log();
+ mBqStat->clear();
+
+ if (mTracker) {
+ mTracker->stop();
+ mTracker.reset();
+ }
+ if (mProducer) {
+ mProducer->disconnect(NATIVE_WINDOW_API_MEDIA);
+ }
+ mProducer.clear();
+ mConsumer.clear();
+ }
+
+protected:
+ std::shared_ptr<BqStatistics> mBqStat = std::make_shared<BqStatistics>();
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ std::shared_ptr<GraphicsTracker> mTracker;
+};
+
+
+TEST_F(GraphicsTrackerTest, AllocateAndBlockedTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 10;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new DummyConsumerListener()));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ c2_status_t ret = mTracker->configureGraphics(mProducer, generation);
+ ASSERT_EQ(C2_OK, ret);
+ ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable());
+
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ uint64_t bid;
+
+ // Allocate and check dequeueable
+ if (__builtin_available(android __ANDROID_API_T__, *)) {
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence);
+ ASSERT_EQ(C2_OK, ret);
+ mBqStat->mDequeued++;
+ ASSERT_EQ(maxDequeueCount - (i + 1), mTracker->getCurDequeueable());
+ ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bid));
+ ALOGD("alloced : bufferId: %llu", (unsigned long long)bid);
+ AHardwareBuffer_release(buf);
+ }
+ } else {
+ GTEST_SKIP();
+ }
+
+ // Allocate should be blocked
+ ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence);
+ ALOGD("alloc : err(%d, %d)", ret, C2_BLOCKING);
+ ASSERT_EQ(C2_BLOCKING, ret);
+ mBqStat->mBlocked++;
+ ASSERT_EQ(0, mTracker->getCurDequeueable());
+}
+
+TEST_F(GraphicsTrackerTest, AllocateAndDeallocateTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 10;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new DummyConsumerListener()));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ c2_status_t ret = mTracker->configureGraphics(mProducer, generation);
+ ASSERT_EQ(C2_OK, ret);
+
+ ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable());
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ uint64_t bid;
+ std::vector<uint64_t> bids;
+
+ // Allocate and store buffer id
+ if (__builtin_available(android __ANDROID_API_T__, *)) {
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence);
+ ASSERT_EQ(C2_OK, ret);
+ mBqStat->mDequeued++;
+ ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bid));
+ bids.push_back(bid);
+ ALOGD("alloced : bufferId: %llu", (unsigned long long)bid);
+ AHardwareBuffer_release(buf);
+ }
+ } else {
+ GTEST_SKIP();
+ }
+
+ // Deallocate and check dequeueable
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ ALOGD("dealloc : bufferId: %llu", (unsigned long long)bids[i]);
+ ret = mTracker->deallocate(bids[i], Fence::NO_FENCE);
+ ASSERT_EQ(C2_OK, ret);
+ ASSERT_EQ(i + 1, mTracker->getCurDequeueable());
+ mBqStat->mDiscarded++;
+ }
+}
+
+TEST_F(GraphicsTrackerTest, DropAndReleaseTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 10;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new DummyConsumerListener()));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ c2_status_t ret = mTracker->configureGraphics(mProducer, generation);
+ ASSERT_EQ(C2_OK, ret);
+
+ ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable());
+
+ FrameQueue frameQueue;
+ std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue);
+ AHardwareBuffer *buf1, *buf2;
+ sp<Fence> fence1, fence2;
+
+ ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf1, &fence1);
+ ASSERT_EQ(C2_OK, ret);
+ mBqStat->mDequeued++;
+ ASSERT_EQ(maxDequeueCount - 1, mTracker->getCurDequeueable());
+
+ ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf2, &fence2);
+ ASSERT_EQ(C2_OK, ret);
+ mBqStat->mDequeued++;
+ ASSERT_EQ(maxDequeueCount - 2, mTracker->getCurDequeueable());
+
+ // Queue two buffers without consuming, one should be dropped
+ ASSERT_TRUE(frameQueue.queueItem(buf1, fence1));
+ ASSERT_TRUE(frameQueue.queueItem(buf2, fence2));
+
+ frameQueue.stop(true);
+ if (queueThread.joinable()) {
+ queueThread.join();
+ }
+
+ ASSERT_EQ(maxDequeueCount - 1, mTracker->getCurDequeueable());
+
+ // 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));
+ // Nothing to consume
+ ASSERT_NE(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable());
+ ASSERT_EQ(1, mBqStat->mReleased);
+ ASSERT_EQ(1, mBqStat->mDropped);
+}
+
+TEST_F(GraphicsTrackerTest, RenderTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 10;
+ const int maxNumAlloc = 20;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+
+ int waitFd = -1;
+ ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd));
+ C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd);
+
+
+ FrameQueue frameQueue;
+ std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue);
+
+ int numAlloc = 0;
+
+ while (numAlloc < maxNumAlloc) {
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ c2_status_t ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence);
+ if (ret == C2_BLOCKING) {
+ mBqStat->mBlocked++;
+ c2_status_t waitRes = waitFence.wait(3000000000);
+ if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) {
+ continue;
+ }
+ ALOGE("alloc wait failed: c2_err(%d)", waitRes);
+ break;
+ }
+ if (ret != C2_OK) {
+ ALOGE("alloc error: c2_err(%d)", ret);
+ break;
+ }
+ mBqStat->mDequeued++;
+ if (!frameQueue.queueItem(buf, fence)) {
+ ALOGE("queue to render failed");
+ break;
+ }
+ ++numAlloc;
+ }
+
+ frameQueue.stop(true);
+ // Wait more than enough time(1 sec) to render all queued frames for sure.
+ ::usleep(1000000);
+
+ if (queueThread.joinable()) {
+ queueThread.join();
+ }
+ ASSERT_EQ(numAlloc, maxNumAlloc);
+ ASSERT_EQ(numAlloc, mBqStat->mDequeued);
+ ASSERT_EQ(mBqStat->mDequeued, mBqStat->mQueued);
+ ASSERT_EQ(mBqStat->mDequeued, mBqStat->mReleased + mBqStat->mDropped);
+}
+
+TEST_F(GraphicsTrackerTest, StopAndWaitTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 2;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+
+ int waitFd = -1;
+ ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd));
+ C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd);
+
+ AHardwareBuffer *buf1, *buf2;
+ sp<Fence> fence;
+
+ ASSERT_EQ(C2_OK, mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf1, &fence));
+ mBqStat->mDequeued++;
+ AHardwareBuffer_release(buf1);
+
+ ASSERT_EQ(C2_OK, mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf2, &fence));
+ mBqStat->mDequeued++;
+ AHardwareBuffer_release(buf2);
+
+ ASSERT_EQ(0, mTracker->getCurDequeueable());
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(3000000000));
+
+ std::thread stopThread(&GraphicsTrackerTest::stopTrackerAfterUs, this, 500000);
+ ASSERT_EQ(C2_BAD_STATE, waitFence.wait(3000000000));
+
+ if (stopThread.joinable()) {
+ stopThread.join();
+ }
+}
+
+TEST_F(GraphicsTrackerTest, SurfaceChangeTest) {
+ uint32_t generation = 1;
+ const int maxDequeueCount = 10;
+
+ const int maxNumAlloc = 20;
+
+ const int firstPassAlloc = 12;
+ const int firstPassRender = 8;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+
+ int waitFd = -1;
+ ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd));
+ C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd);
+
+ AHardwareBuffer *bufs[maxNumAlloc];
+ sp<Fence> fences[maxNumAlloc];
+
+ FrameQueue frameQueue;
+ std::thread queueThread(&GraphicsTrackerTest::queueBuffer, this, &frameQueue);
+ int numAlloc = 0;
+
+ for (int i = 0; i < firstPassRender; ++i) {
+ ASSERT_EQ(C2_OK, mTracker->allocate(
+ 0, 0, 0, kTestUsageFlag, &bufs[i], &fences[i]));
+ mBqStat->mDequeued++;
+ numAlloc++;
+ ASSERT_EQ(true, frameQueue.queueItem(bufs[i], fences[i]));
+ }
+
+ while (numAlloc < firstPassAlloc) {
+ c2_status_t ret = mTracker->allocate(
+ 0, 0, 0, kTestUsageFlag, &bufs[numAlloc], &fences[numAlloc]);
+ if (ret == C2_BLOCKING) {
+ mBqStat->mBlocked++;
+ c2_status_t waitRes = waitFence.wait(3000000000);
+ if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) {
+ continue;
+ }
+ ALOGE("alloc wait failed: c2_err(%d)", waitRes);
+ break;
+ }
+ if (ret != C2_OK) {
+ ALOGE("alloc error: c2_err(%d)", ret);
+ break;
+ }
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ ASSERT_EQ(numAlloc, firstPassAlloc);
+
+ // switching surface
+ sp<IGraphicBufferProducer> oldProducer = mProducer;
+ sp<IGraphicBufferConsumer> oldConsumer = mConsumer;
+ mProducer.clear();
+ mConsumer.clear();
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ ASSERT_TRUE((bool)mProducer && (bool)mConsumer);
+
+ generation += 1;
+
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+
+ ASSERT_EQ(OK, oldProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
+ oldProducer.clear();
+ oldConsumer.clear();
+
+ for (int i = firstPassRender ; i < firstPassAlloc; ++i) {
+ ASSERT_EQ(true, frameQueue.queueItem(bufs[i], fences[i]));
+ }
+
+ while (numAlloc < maxNumAlloc) {
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ c2_status_t ret = mTracker->allocate(0, 0, 0, kTestUsageFlag, &buf, &fence);
+ if (ret == C2_BLOCKING) {
+ mBqStat->mBlocked++;
+ c2_status_t waitRes = waitFence.wait(3000000000);
+ if (waitRes == C2_TIMED_OUT || waitRes == C2_OK) {
+ continue;
+ }
+ ALOGE("alloc wait failed: c2_err(%d)", waitRes);
+ break;
+ }
+ if (ret != C2_OK) {
+ ALOGE("alloc error: c2_err(%d)", ret);
+ break;
+ }
+ mBqStat->mDequeued++;
+ if (!frameQueue.queueItem(buf, fence)) {
+ ALOGE("queue to render failed");
+ break;
+ }
+ ++numAlloc;
+ }
+
+ ASSERT_EQ(numAlloc, maxNumAlloc);
+
+ frameQueue.stop(true);
+ // Wait more than enough time(1 sec) to render all queued frames for sure.
+ ::usleep(1000000);
+
+ if (queueThread.joinable()) {
+ queueThread.join();
+ }
+ // mReleased should not be checked. IProducerListener::onBufferReleased()
+ // from the previous Surface could be missing after a new Surface was
+ // configured. Instead check # of dequeueable and queueBuffer() calls.
+ ASSERT_EQ(numAlloc, mBqStat->mQueued);
+ ASSERT_EQ(maxDequeueCount, mTracker->getCurDequeueable());
+
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+
+ ASSERT_EQ(C2_OK, mTracker->allocate(
+ 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ AHardwareBuffer_release(buf);
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate(
+ 0, 0, 0, kTestUsageFlag, &bufs[0], &fences[0]));
+}
+
+TEST_F(GraphicsTrackerTest, maxDequeueIncreaseTest) {
+ uint32_t generation = 1;
+ int maxDequeueCount = 10;
+ int dequeueIncrease = 4;
+
+ int numAlloc = 0;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+
+ int waitFd = -1;
+ ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd));
+ C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd);
+
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ uint64_t bids[maxDequeueCount];
+ if (__builtin_available(android __ANDROID_API_T__, *)) {
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bids[i]));
+ AHardwareBuffer_release(buf);
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ } else {
+ GTEST_SKIP();
+ }
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[0], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+
+ maxDequeueCount += dequeueIncrease;
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+ for (int i = 0; i < dequeueIncrease + 1; ++i) {
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ AHardwareBuffer_release(buf);
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[1], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+
+ maxDequeueCount += dequeueIncrease;
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+ for (int i = 0; i < dequeueIncrease + 1; ++i) {
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ AHardwareBuffer_release(buf);
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+}
+
+TEST_F(GraphicsTrackerTest, maxDequeueDecreaseTest) {
+ uint32_t generation = 1;
+ int maxDequeueCount = 12;
+ int dequeueDecrease = 4;
+
+ int numAlloc = 0;
+
+ ASSERT_TRUE(init(maxDequeueCount));
+ ASSERT_TRUE(configure(new TestProducerListener(mTracker, mBqStat, generation),
+ new TestConsumerListener(mConsumer), 1, false));
+
+ ASSERT_EQ(OK, mProducer->setGenerationNumber(generation));
+ ASSERT_EQ(C2_OK, mTracker->configureGraphics(mProducer, generation));
+
+ int waitFd = -1;
+ ASSERT_EQ(C2_OK, mTracker->getWaitableFd(&waitFd));
+ C2Fence waitFence = _C2FenceFactory::CreatePipeFence(waitFd);
+
+ AHardwareBuffer *buf;
+ sp<Fence> fence;
+ uint64_t bids[maxDequeueCount];
+ if (__builtin_available(android __ANDROID_API_T__, *)) {
+ for (int i = 0; i < maxDequeueCount; ++i) {
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ ASSERT_EQ(OK, AHardwareBuffer_getId(buf, &bids[i]));
+ AHardwareBuffer_release(buf);
+ mBqStat->mDequeued++;
+ numAlloc++;
+ }
+ } else {
+ GTEST_SKIP();
+ }
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+
+ int discardIdx = 0;
+ maxDequeueCount -= dequeueDecrease;
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+ for (int i = 0; i < dequeueDecrease + 1; ++i) {
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+ }
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ mBqStat->mDequeued++;
+
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+ maxDequeueCount -= dequeueDecrease;
+
+ ASSERT_EQ(C2_OK, mTracker->configureMaxDequeueCount(maxDequeueCount));
+ for (int i = 0; i < dequeueDecrease - 1; ++i) {
+ ASSERT_EQ(C2_TIMED_OUT, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_BLOCKING, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ ASSERT_EQ(C2_OK, mTracker->deallocate(bids[discardIdx++], Fence::NO_FENCE));
+ mBqStat->mDiscarded++;
+ }
+ ASSERT_EQ(C2_OK, waitFence.wait(1000000000));
+ ASSERT_EQ(C2_OK, mTracker->allocate( 0, 0, 0, kTestUsageFlag, &buf, &fence));
+ mBqStat->mDequeued++;
+}
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index af2683b..9f57bfd 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -84,7 +84,7 @@
"libbase",
"libdmabufheap",
"android.hardware.media.bufferpool@2.0",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
],
local_include_dirs: [
@@ -102,7 +102,7 @@
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
"android.hardware.media.bufferpool@2.0",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.c2-V1-ndk",
"libbase",
"libbinder_ndk",
@@ -162,11 +162,12 @@
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.media.bufferpool2-V1-ndk",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.c2-V1-ndk",
],
shared_libs: [
+ "libbinder",
"libbinder_ndk",
"libui",
"libdl",
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index 107ce89..60b5b29 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -383,7 +383,7 @@
}
uint8_t *pointer = nullptr;
- err = mapper.lock(handle, usage, bounds, (void **)&pointer, nullptr, nullptr);
+ err = mapper.lock(handle, usage, bounds, (void **)&pointer);
if (err != NO_ERROR || pointer == nullptr) {
return C2_CORRUPTED;
}
diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp
index 52ebe25..5d50fc3 100644
--- a/media/codec2/vndk/C2Fence.cpp
+++ b/media/codec2/vndk/C2Fence.cpp
@@ -335,7 +335,8 @@
p.reset();
}
} else {
- ALOGE("Create sync fence from invalid fd");
+ ALOGV("Create sync fence from invalid fd");
+ return C2Fence();
}
return C2Fence(p);
}
@@ -531,7 +532,9 @@
p = SyncFenceImpl::CreateFromNativeHandle(handle);
break;
default:
- ALOGD("Unsupported fence type %d", type);
+ ALOGV("Unsupported fence type %d", type);
+ // If this is malformed-handle close the handle here.
+ (void) native_handle_close(handle);
// return a null-fence in this case
break;
}
diff --git a/media/codec2/vndk/platform/C2IgbaBuffer.cpp b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
index 2051e8f..eafdb22 100644
--- a/media/codec2/vndk/platform/C2IgbaBuffer.cpp
+++ b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
@@ -193,7 +193,7 @@
width, height, format, usage, kBlockingFetchTimeoutNs, &origId, block, &fence);
if (res == C2_BLOCKING) {
- return C2_TIMED_OUT;
+ return C2_BLOCKING;
}
if (res != C2_OK) {
return res;
diff --git a/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp b/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp
index e1ff3eb..1b06ea7 100644
--- a/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp
+++ b/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp
@@ -19,6 +19,8 @@
#include "aaudio/AAudioTesting.h"
#include <fuzzer/FuzzedDataProvider.h>
+#include <functional>
+
constexpr int32_t kRandomStringLength = 256;
constexpr int32_t kMaxRuns = 100;
constexpr int64_t kNanosPerMillisecond = 1000 * 1000;
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 2c9e173..b44dc18 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -217,6 +217,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"latest_android_media_audio_common_types_cpp_export_shared",
],
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index f050a20..ae37152 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1704,10 +1704,14 @@
__func__, mPortId, deviceId, mSelectedDeviceId, mRoutedDeviceId);
if (mSelectedDeviceId != deviceId) {
mSelectedDeviceId = deviceId;
- if (mStatus == NO_ERROR && mSelectedDeviceId != mRoutedDeviceId) {
+ if (mStatus == NO_ERROR) {
+ // allow track invalidation when track is not playing to propagate
+ // the updated mSelectedDeviceId
if (isPlaying_l()) {
- android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
- mProxy->interrupt();
+ if (mSelectedDeviceId != mRoutedDeviceId) {
+ android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ mProxy->interrupt();
+ }
} else {
// if the track is idle, try to restore now and
// defer to next start if not possible
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 60bb4f0..234e858 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -43,8 +43,17 @@
}
],
"postsubmit": [
+ // TODO(b/302036943): Enable once we make it pass with AIDL HAL on CF.
+ // {
+ // "name": "audioeffect_analysis"
+ // },
{
- "name": "audioeffect_analysis"
+ "name": "CtsVirtualDevicesTestCases",
+ "options" : [
+ {
+ "include-filter": "android.virtualdevice.cts.VirtualAudioTest"
+ }
+ ]
}
]
}
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
index 26121cd..e12ae23 100644
--- a/media/libaudioclient/tests/audioeffect_tests.cpp
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -80,7 +80,7 @@
uint32_t numEffects = AudioEffect::kMaxPreProcessing;
status_t ret = AudioEffect::queryDefaultPreProcessing(audioRecord->getSessionId(), descriptors,
&numEffects);
- if (ret != OK) {
+ if (ret != OK || numEffects > AudioEffect::kMaxPreProcessing) {
return false;
}
for (int i = 0; i < numEffects; i++) {
@@ -247,6 +247,7 @@
ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
EXPECT_EQ(NO_ERROR, capture->create());
EXPECT_EQ(NO_ERROR, capture->start());
+ ASSERT_NE(capture->getAudioRecordHandle(), nullptr);
if (!isEffectDefaultOnRecord(&descriptors[i].type, &descriptors[i].uuid,
capture->getAudioRecordHandle())) {
selectedEffect = i;
@@ -265,6 +266,7 @@
ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
EXPECT_EQ(NO_ERROR, capture->create());
EXPECT_EQ(NO_ERROR, capture->start());
+ ASSERT_NE(capture->getAudioRecordHandle(), nullptr);
EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
@@ -287,6 +289,7 @@
ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
EXPECT_EQ(NO_ERROR, capture->create());
EXPECT_EQ(NO_ERROR, capture->start());
+ ASSERT_NE(capture->getAudioRecordHandle(), nullptr);
EXPECT_TRUE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
capture->getAudioRecordHandle()))
<< "Effect should have been default on record. " << type;
@@ -304,6 +307,7 @@
ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
EXPECT_EQ(NO_ERROR, capture->create());
EXPECT_EQ(NO_ERROR, capture->start());
+ ASSERT_NE(capture->getAudioRecordHandle(), nullptr);
EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index 8c63a6d..61edd4d 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -14,9 +14,13 @@
* limitations under the License.
*/
+#include <sstream>
+
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioRecordTest"
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include "audio_test_utils.h"
@@ -25,32 +29,40 @@
class AudioRecordTest : public ::testing::Test {
public:
- virtual void SetUp() override {
+ void SetUp() override {
mAC = new AudioCapture(AUDIO_SOURCE_DEFAULT, 44100, AUDIO_FORMAT_PCM_16_BIT,
AUDIO_CHANNEL_IN_FRONT);
ASSERT_NE(nullptr, mAC);
ASSERT_EQ(OK, mAC->create()) << "record creation failed";
}
- virtual void TearDown() override {
+ void TearDown() override {
if (mAC) ASSERT_EQ(OK, mAC->stop());
}
sp<AudioCapture> mAC;
};
-class AudioRecordCreateTest
- : public ::testing::TestWithParam<
- std::tuple<uint32_t, audio_format_t, audio_channel_mask_t, audio_input_flags_t,
- audio_session_t, audio_source_t>> {
+using RecordCreateTestParam = std::tuple<uint32_t, audio_format_t, audio_channel_mask_t,
+ audio_input_flags_t, audio_session_t, audio_source_t>;
+enum {
+ RECORD_PARAM_SAMPLE_RATE,
+ RECORD_PARAM_FORMAT,
+ RECORD_PARAM_CHANNEL_MASK,
+ RECORD_PARAM_FLAGS,
+ RECORD_PARAM_SESSION_ID,
+ RECORD_PARAM_INPUT_SOURCE
+};
+
+class AudioRecordCreateTest : public ::testing::TestWithParam<RecordCreateTestParam> {
public:
AudioRecordCreateTest()
- : mSampleRate(std::get<0>(GetParam())),
- mFormat(std::get<1>(GetParam())),
- mChannelMask(std::get<2>(GetParam())),
- mFlags(std::get<3>(GetParam())),
- mSessionId(std::get<4>(GetParam())),
- mInputSource(std::get<5>(GetParam())){};
+ : mSampleRate(std::get<RECORD_PARAM_SAMPLE_RATE>(GetParam())),
+ mFormat(std::get<RECORD_PARAM_FORMAT>(GetParam())),
+ mChannelMask(std::get<RECORD_PARAM_CHANNEL_MASK>(GetParam())),
+ mFlags(std::get<RECORD_PARAM_FLAGS>(GetParam())),
+ mSessionId(std::get<RECORD_PARAM_SESSION_ID>(GetParam())),
+ mInputSource(std::get<RECORD_PARAM_INPUT_SOURCE>(GetParam())){};
const uint32_t mSampleRate;
const audio_format_t mFormat;
@@ -62,14 +74,14 @@
sp<AudioCapture> mAC;
- virtual void SetUp() override {
+ void SetUp() override {
mAC = new AudioCapture(mInputSource, mSampleRate, mFormat, mChannelMask, mFlags, mSessionId,
mTransferType);
ASSERT_NE(nullptr, mAC);
ASSERT_EQ(OK, mAC->create()) << "record creation failed";
}
- virtual void TearDown() override {
+ void TearDown() override {
if (mAC) ASSERT_EQ(OK, mAC->stop());
}
};
@@ -197,6 +209,18 @@
EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
}
+static std::string GetRecordTestName(const testing::TestParamInfo<RecordCreateTestParam>& info) {
+ const auto& p = info.param;
+ std::ostringstream s;
+ s << std::get<RECORD_PARAM_SAMPLE_RATE>(p) << "_"
+ << audio_format_to_string(std::get<RECORD_PARAM_FORMAT>(p)) << "__"
+ << audio_channel_mask_to_string(std::get<RECORD_PARAM_CHANNEL_MASK>(p)) << "__"
+ << "Flags_0x" << std::hex << std::get<RECORD_PARAM_FLAGS>(p) << std::dec << "__"
+ << "Session_" << std::get<RECORD_PARAM_SESSION_ID>(p) << "__"
+ << audio_source_to_string(std::get<RECORD_PARAM_INPUT_SOURCE>(p));
+ return s.str();
+}
+
// for port primary input
INSTANTIATE_TEST_SUITE_P(AudioRecordPrimaryInput, AudioRecordCreateTest,
::testing::Combine(::testing::Values(8000, 11025, 12000, 16000, 22050,
@@ -207,7 +231,8 @@
AUDIO_CHANNEL_IN_FRONT_BACK),
::testing::Values(AUDIO_INPUT_FLAG_NONE),
::testing::Values(AUDIO_SESSION_NONE),
- ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)),
+ GetRecordTestName);
// for port fast input
INSTANTIATE_TEST_SUITE_P(AudioRecordFastInput, AudioRecordCreateTest,
@@ -219,7 +244,8 @@
AUDIO_CHANNEL_IN_FRONT_BACK),
::testing::Values(AUDIO_INPUT_FLAG_FAST),
::testing::Values(AUDIO_SESSION_NONE),
- ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)),
+ GetRecordTestName);
// misc
INSTANTIATE_TEST_SUITE_P(AudioRecordMiscInput, AudioRecordCreateTest,
@@ -232,4 +258,35 @@
AUDIO_SOURCE_CAMCORDER,
AUDIO_SOURCE_VOICE_RECOGNITION,
AUDIO_SOURCE_VOICE_COMMUNICATION,
- AUDIO_SOURCE_UNPROCESSED)));
+ AUDIO_SOURCE_UNPROCESSED)),
+ GetRecordTestName);
+
+namespace {
+
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override {
+ TraceTestState("Started", test_info);
+ }
+ void OnTestEnd(const ::testing::TestInfo& test_info) override {
+ TraceTestState("Finished", test_info);
+ }
+ void OnTestPartResult(const ::testing::TestPartResult& result) override { LOG(INFO) << result; }
+
+ private:
+ static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
+ LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+ }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ android::base::SetMinimumLogSeverity(::android::base::DEBUG);
+ // This is for death handlers instantiated by the framework code.
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ android::ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudiohal/FactoryHal.cpp b/media/libaudiohal/FactoryHal.cpp
index f88915d..c414e19 100644
--- a/media/libaudiohal/FactoryHal.cpp
+++ b/media/libaudiohal/FactoryHal.cpp
@@ -50,9 +50,8 @@
* This list need to keep sync with AudioHalVersionInfo.VERSIONS in
* media/java/android/media/AudioHalVersionInfo.java.
*/
-static const std::array<AudioHalVersionInfo, 5> sAudioHALVersions = {
- // TODO: remove this comment to get AIDL
- // AudioHalVersionInfo(AudioHalVersionInfo::Type::AIDL, 1, 0),
+static const std::array<AudioHalVersionInfo, 6> sAudioHALVersions = {
+ AudioHalVersionInfo(AudioHalVersionInfo::Type::AIDL, 1, 0),
AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 7, 1),
AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 7, 0),
AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 6, 0),
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index 8a843ed..2af18cc 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -448,6 +448,7 @@
}
*config = VALUE_OR_RETURN_STATUS(
::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput));
+ if (mixPortConfig.id == 0) return BAD_VALUE; // HAL suggests a different config.
::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
args.portConfigId = mixPortConfig.id;
const bool isOffload = isBitPositionFlagSet(
@@ -520,6 +521,7 @@
}
*config = VALUE_OR_RETURN_STATUS(
::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput));
+ if (mixPortConfig.id == 0) return BAD_VALUE; // HAL suggests a different config.
::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
args.portConfigId = mixPortConfig.id;
RecordTrackMetadata aidlTrackMetadata{
@@ -704,8 +706,7 @@
*config, isInput, 0 /*portId*/));
AudioPortConfig portConfig;
std::lock_guard l(mLock);
- return mMapper.findOrCreatePortConfig(
- requestedPortConfig, std::set<int32_t>(), &portConfig);
+ return mMapper.setPortConfig(requestedPortConfig, std::set<int32_t>(), &portConfig);
}
MicrophoneInfoProvider::Info const* DeviceHalAidl::getMicrophoneInfo() {
@@ -777,7 +778,7 @@
Hal2AidlMapper::Cleanups cleanups(mMapperAccessor);
{
std::lock_guard l(mLock);
- RETURN_STATUS_IF_ERROR(mMapper.findOrCreatePortConfig(
+ RETURN_STATUS_IF_ERROR(mMapper.setPortConfig(
requestedPortConfig, {} /*destinationPortIds*/, &devicePortConfig, &cleanups));
}
auto aidlEffect = sp<effect::EffectHalAidl>::cast(effect);
@@ -902,15 +903,39 @@
}
status_t DeviceHalAidl::prepareToDisconnectExternalDevice(const struct audio_port_v7* port) {
- // There is not AIDL API defined for `prepareToDisconnectExternalDevice`.
- // Call `setConnectedState` instead.
- // TODO(b/279824103): call prepareToDisconnectExternalDevice when it is added.
- RETURN_STATUS_IF_ERROR(setConnectedState(port, false /*connected*/));
- std::lock_guard l(mLock);
- mDeviceDisconnectionNotified.insert(port->id);
- // Return that there was no error as otherwise the disconnection procedure will not be
- // considered complete for upper layers, and 'setConnectedState' will not be called again
- return OK;
+ ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
+ TIME_CHECK();
+ if (mModule == nullptr) return NO_INIT;
+ if (port == nullptr) {
+ return BAD_VALUE;
+ }
+ const bool isInput = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::portDirection(port->role, port->type)) ==
+ ::aidl::android::AudioPortDirection::INPUT;
+ AudioPort aidlPort = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::legacy2aidl_audio_port_v7_AudioPort(*port, isInput));
+ if (aidlPort.ext.getTag() != AudioPortExt::device) {
+ ALOGE("%s: provided port is not a device port (module %s): %s",
+ __func__, mInstance.c_str(), aidlPort.toString().c_str());
+ return BAD_VALUE;
+ }
+ status_t status = NO_ERROR;
+ {
+ std::lock_guard l(mLock);
+ status = mMapper.prepareToDisconnectExternalDevice(aidlPort);
+ }
+ if (status == UNKNOWN_TRANSACTION) {
+ // If there is not AIDL API defined for `prepareToDisconnectExternalDevice`.
+ // Call `setConnectedState` instead.
+ RETURN_STATUS_IF_ERROR(setConnectedState(port, false /*connected*/));
+ std::lock_guard l(mLock);
+ mDeviceDisconnectionNotified.insert(port->id);
+ // Return that there was no error as otherwise the disconnection procedure will not be
+ // considered complete for upper layers, and 'setConnectedState' will not be called again
+ return OK;
+ } else {
+ return status;
+ }
}
status_t DeviceHalAidl::setConnectedState(const struct audio_port_v7 *port, bool connected) {
@@ -924,11 +949,10 @@
std::lock_guard l(mLock);
if (mDeviceDisconnectionNotified.erase(port->id) > 0) {
// For device disconnection, APM will first call `prepareToDisconnectExternalDevice`
- // and then call `setConnectedState`. However, there is no API for
- // `prepareToDisconnectExternalDevice` yet. In that case, `setConnectedState` will be
- // called when calling `prepareToDisconnectExternalDevice`. Do not call to the HAL if
- // previous call is successful. Also remove the cache here to avoid a large cache after
- // a long run.
+ // and then call `setConnectedState`. If `prepareToDisconnectExternalDevice` doesn't
+ // exit, `setConnectedState` will be called when calling
+ // `prepareToDisconnectExternalDevice`. Do not call to the HAL if previous call is
+ // successful. Also remove the cache here to avoid a large cache after a long run.
return OK;
}
}
@@ -950,7 +974,7 @@
if (mModule == nullptr) return NO_INIT;
{
std::lock_guard l(mLock);
- mMapper.resetUnusedPatchesAndPortConfigs();
+ mMapper.resetUnusedPatchesPortConfigsAndPorts();
}
ModuleDebug debug{ .simulateDeviceConnections = enabled };
status_t status = statusTFromBinderStatus(mModule->setModuleDebug(debug));
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index a8f9f7e..3dbc14a 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -24,7 +24,6 @@
#include <aidl/android/hardware/audio/core/IModule.h>
#include <android/binder_manager.h>
-#include <binder/IServiceManager.h>
#include <media/AidlConversionNdkCpp.h>
#include <media/AidlConversionUtil.h>
#include <utils/Log.h>
@@ -115,29 +114,6 @@
return OK;
}
-status_t DevicesFactoryHalAidl::getHalPids(std::vector<pid_t> *pids) {
- if (pids == nullptr) {
- return BAD_VALUE;
- }
- // The functionality for retrieving debug infos of services is not exposed via the NDK.
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == nullptr) {
- return NO_INIT;
- }
- std::set<pid_t> pidsSet;
- const auto moduleServiceName = std::string(IModule::descriptor) + "/";
- auto debugInfos = sm->getServiceDebugInfo();
- for (const auto& info : debugInfos) {
- if (info.pid > 0 &&
- info.name.size() > moduleServiceName.size() && // '>' as there must be instance name
- info.name.substr(0, moduleServiceName.size()) == moduleServiceName) {
- pidsSet.insert(info.pid);
- }
- }
- *pids = {pidsSet.begin(), pidsSet.end()};
- return NO_ERROR;
-}
-
status_t DevicesFactoryHalAidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
// Dynamic registration of module instances is not supported. The functionality
// in the audio server which is related to this callback can be removed together
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.h b/media/libaudiohal/impl/DevicesFactoryHalAidl.h
index 97e3796..17bfe43 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.h
@@ -35,8 +35,6 @@
// necessary to release references to the returned object.
status_t openDevice(const char *name, sp<DeviceHalInterface> *device) override;
- status_t getHalPids(std::vector<pid_t> *pids) override;
-
status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
android::detail::AudioHalVersionInfo getHalVersion() const override;
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
index eef60b5..1cac9da 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
@@ -163,29 +163,6 @@
return BAD_VALUE;
}
-status_t DevicesFactoryHalHidl::getHalPids(std::vector<pid_t> *pids) {
- std::set<pid_t> pidsSet;
- auto factories = copyDeviceFactories();
- for (const auto& factory : factories) {
- using ::android::hidl::base::V1_0::DebugInfo;
-
- DebugInfo debugInfo;
- auto ret = factory->getDebugInfo([&] (const auto &info) {
- debugInfo = info;
- });
- if (!ret.isOk()) {
- return INVALID_OPERATION;
- }
- if (debugInfo.pid == (int)IServiceManager::PidConstant::NO_PID) {
- continue;
- }
- pidsSet.insert(debugInfo.pid);
- }
-
- *pids = {pidsSet.begin(), pidsSet.end()};
- return NO_ERROR;
-}
-
status_t DevicesFactoryHalHidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
ALOG_ASSERT(callback != nullptr);
bool needToCallCallback = false;
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.h b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
index 3285af7..e38d86d 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
@@ -43,8 +43,6 @@
// necessary to release references to the returned object.
status_t openDevice(const char *name, sp<DeviceHalInterface> *device) override;
- status_t getHalPids(std::vector<pid_t> *pids) override;
-
status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
android::detail::AudioHalVersionInfo getHalVersion() const override;
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index 196b432..39999a5 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -180,18 +180,6 @@
State state;
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getState(&state)));
- // in case of buffer/ioHandle re-configure for an opened effect, close it and re-open
- if (state != State::INIT && mCommon != common) {
- ALOGI("%s at state %s, common parameter change from %s to %s, closing effect", __func__,
- android::internal::ToString(state).c_str(), mCommon.toString().c_str(),
- common.toString().c_str());
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->close()));
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getState(&state)));
- mStatusQ.reset();
- mInputQ.reset();
- mOutputQ.reset();
- }
-
if (state == State::INIT) {
ALOGI("%s at state %s, opening effect with input %s output %s", __func__,
android::internal::ToString(state).c_str(), common.input.toString().c_str(),
@@ -199,16 +187,7 @@
IEffect::OpenEffectReturn openReturn;
RETURN_STATUS_IF_ERROR(
statusTFromBinderStatus(mEffect->open(common, std::nullopt, &openReturn)));
-
- if (mIsProxyEffect) {
- mStatusQ = std::static_pointer_cast<EffectProxy>(mEffect)->getStatusMQ();
- mInputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getInputMQ();
- mOutputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getOutputMQ();
- } else {
- mStatusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
- mInputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
- mOutputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
- }
+ updateMqs(openReturn);
if (status_t status = updateEventFlags(); status != OK) {
ALOGV("%s closing at status %d", __func__, status);
@@ -225,6 +204,18 @@
return *static_cast<int32_t*>(pReplyData) = OK;
}
+void EffectConversionHelperAidl::updateMqs(const IEffect::OpenEffectReturn& ret) {
+ if (mIsProxyEffect) {
+ mStatusQ = std::static_pointer_cast<EffectProxy>(mEffect)->getStatusMQ();
+ mInputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getInputMQ();
+ mOutputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getOutputMQ();
+ } else {
+ mStatusQ = std::make_shared<StatusMQ>(ret.statusMQ);
+ mInputQ = std::make_shared<DataMQ>(ret.inputDataMQ);
+ mOutputQ = std::make_shared<DataMQ>(ret.outputDataMQ);
+ }
+}
+
status_t EffectConversionHelperAidl::handleGetConfig(uint32_t cmdSize __unused,
const void* pCmdData __unused,
uint32_t* replySize, void* pReplyData) {
@@ -517,5 +508,13 @@
return desc;
}
+status_t EffectConversionHelperAidl::reopen() {
+ IEffect::OpenEffectReturn openReturn;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->reopen(&openReturn)));
+
+ updateMqs(openReturn);
+ return OK;
+}
+
} // namespace effect
} // namespace android
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index 5db334c..8b9efb3 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -47,6 +47,7 @@
bool isBypassingOrTunnel() const;
::aidl::android::hardware::audio::effect::Descriptor getDescriptor() const;
+ status_t reopen();
protected:
const int32_t mSessionId;
@@ -108,6 +109,8 @@
std::shared_ptr<android::hardware::EventFlag> mEfGroup = nullptr;
status_t updateEventFlags();
+ void updateMqs(const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
+
status_t handleInit(uint32_t cmdSize, const void* pCmdData, uint32_t* replySize,
void* pReplyData);
status_t handleSetConfig(uint32_t cmdSize, const void* pCmdData, uint32_t* replySize,
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index f26444c..2836727 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -56,6 +56,7 @@
using ::aidl::android::hardware::audio::effect::Descriptor;
using ::aidl::android::hardware::audio::effect::IEffect;
using ::aidl::android::hardware::audio::effect::IFactory;
+using ::aidl::android::hardware::audio::effect::kEventFlagDataMqUpdate;
using ::aidl::android::hardware::audio::effect::State;
namespace android {
@@ -165,26 +166,37 @@
// write to input FMQ here, wait for statusMQ STATUS_OK, and read from output FMQ
status_t EffectHalAidl::process() {
+ const std::string effectName = mConversion->getDescriptor().common.name;
State state = State::INIT;
if (mConversion->isBypassing() || !mEffect->getState(&state).isOk() ||
state != State::PROCESSING) {
- ALOGI("%s skipping %s process because it's %s", __func__,
- mConversion->getDescriptor().common.name.c_str(),
+ ALOGI("%s skipping %s process because it's %s", __func__, effectName.c_str(),
mConversion->isBypassing()
? "bypassing"
: aidl::android::hardware::audio::effect::toString(state).c_str());
return -ENODATA;
}
+ // check if the DataMq needs any update, timeout at 1ns to avoid being blocked
+ auto efGroup = mConversion->getEventFlagGroup();
+ if (!efGroup) {
+ ALOGE("%s invalid efGroup", __func__);
+ return INVALID_OPERATION;
+ }
+
+ if (uint32_t efState = 0;
+ ::android::OK == efGroup->wait(kEventFlagDataMqUpdate, &efState, 1 /* ns */,
+ true /* retry */)) {
+ ALOGI("%s %s receive dataMQUpdate eventFlag from HAL", __func__, effectName.c_str());
+ mConversion->reopen();
+ }
auto statusQ = mConversion->getStatusMQ();
auto inputQ = mConversion->getInputMQ();
auto outputQ = mConversion->getOutputMQ();
- auto efGroup = mConversion->getEventFlagGroup();
if (!statusQ || !statusQ->isValid() || !inputQ || !inputQ->isValid() || !outputQ ||
- !outputQ->isValid() || !efGroup) {
- ALOGE("%s invalid FMQ [Status %d I %d O %d] efGroup %p", __func__,
- statusQ ? statusQ->isValid() : 0, inputQ ? inputQ->isValid() : 0,
- outputQ ? outputQ->isValid() : 0, efGroup.get());
+ !outputQ->isValid()) {
+ ALOGE("%s invalid FMQ [Status %d I %d O %d]", __func__, statusQ ? statusQ->isValid() : 0,
+ inputQ ? inputQ->isValid() : 0, outputQ ? outputQ->isValid() : 0);
return INVALID_OPERATION;
}
@@ -225,8 +237,8 @@
return INVALID_OPERATION;
}
- ALOGD("%s %s consumed %zu produced %zu", __func__,
- mConversion->getDescriptor().common.name.c_str(), floatsToWrite, floatsToRead);
+ ALOGD("%s %s consumed %zu produced %zu", __func__, effectName.c_str(), floatsToWrite,
+ floatsToRead);
return OK;
}
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index aee42a9..d73a36c 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -106,8 +106,8 @@
ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
const std::optional<Parameter::Specific>& specific,
IEffect::OpenEffectReturn* ret __unused) {
- ndk::ScopedAStatus status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
- EX_ILLEGAL_ARGUMENT, "nullEffectHandle");
+ ndk::ScopedAStatus status =
+ ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "nullEffectHandle");
for (auto& sub : mSubEffects) {
IEffect::OpenEffectReturn openReturn;
if (!sub.handle || !(status = sub.handle->open(common, specific, &openReturn)).isOk()) {
@@ -130,6 +130,31 @@
return status;
}
+ndk::ScopedAStatus EffectProxy::reopen(OpenEffectReturn* ret __unused) {
+ ndk::ScopedAStatus status =
+ ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "nullEffectHandle");
+ for (auto& sub : mSubEffects) {
+ IEffect::OpenEffectReturn openReturn;
+ if (!sub.handle || !(status = sub.handle->reopen(&openReturn)).isOk()) {
+ ALOGE("%s: failed to open %p UUID %s", __func__, sub.handle.get(),
+ ::android::audio::utils::toString(sub.descriptor.common.id.uuid).c_str());
+ break;
+ }
+ sub.effectMq.statusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
+ sub.effectMq.inputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
+ sub.effectMq.outputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
+ }
+
+ // close all opened effects if failure
+ if (!status.isOk()) {
+ ALOGE("%s: closing all sub-effects with error %s", __func__,
+ status.getDescription().c_str());
+ close();
+ }
+
+ return status;
+}
+
ndk::ScopedAStatus EffectProxy::close() {
return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
return effect->close();
diff --git a/media/libaudiohal/impl/EffectProxy.h b/media/libaudiohal/impl/EffectProxy.h
index 0d62642..9b9e8f1 100644
--- a/media/libaudiohal/impl/EffectProxy.h
+++ b/media/libaudiohal/impl/EffectProxy.h
@@ -62,6 +62,8 @@
specific,
::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn* ret) override;
ndk::ScopedAStatus close() override;
+ ndk::ScopedAStatus reopen(
+ ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn* ret) override;
ndk::ScopedAStatus getDescriptor(
::aidl::android::hardware::audio::effect::Descriptor* desc) override;
ndk::ScopedAStatus command(::aidl::android::hardware::audio::effect::CommandId id) override;
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.cpp b/media/libaudiohal/impl/Hal2AidlMapper.cpp
index 47fcd27..2b7f298 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.cpp
+++ b/media/libaudiohal/impl/Hal2AidlMapper.cpp
@@ -30,11 +30,13 @@
using aidl::android::aidl_utils::statusTFromBinderStatus;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioConfig;
+using aidl::android::media::audio::common::AudioConfigBase;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOutputFlags;
@@ -64,10 +66,11 @@
portConfig.format.value() == config.base.format;
}
-void setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
+AudioConfig* setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
config->base.sampleRate = portConfig.sampleRate.value().value;
config->base.channelMask = portConfig.channelMask.value();
config->base.format = portConfig.format.value();
+ return config;
}
void setPortConfigFromConfig(AudioPortConfig* portConfig, const AudioConfig& config) {
@@ -142,8 +145,33 @@
std::vector<int32_t>* ids, std::set<int32_t>* portIds) -> status_t {
for (const auto& s : configs) {
AudioPortConfig portConfig;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
- s, destinationPortIds, &portConfig, cleanups));
+ if (status_t status = setPortConfig(
+ s, destinationPortIds, &portConfig, cleanups); status != OK) {
+ if (s.ext.getTag() == AudioPortExt::mix) {
+ // See b/315528763. Despite that the framework knows the actual format of
+ // the mix port, it still uses the original format. Luckily, there is
+ // the I/O handle which can be used to find the mix port.
+ ALOGI("fillPortConfigs: retrying to find a mix port config with default "
+ "configuration");
+ if (auto it = findPortConfig(std::nullopt, s.flags,
+ s.ext.get<AudioPortExt::mix>().handle);
+ it != mPortConfigs.end()) {
+ portConfig = it->second;
+ } else {
+ const std::string flags = s.flags.has_value() ?
+ s.flags->toString() : "<unspecified>";
+ ALOGE("fillPortConfigs: existing port config for flags %s, handle %d "
+ "not found in module %s", flags.c_str(),
+ s.ext.get<AudioPortExt::mix>().handle, mInstance.c_str());
+ return BAD_VALUE;
+ }
+ } else {
+ return status;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(portConfig.id == 0,
+ "fillPortConfigs: initial config: %s, port config: %s",
+ s.toString().c_str(), portConfig.toString().c_str());
ids->push_back(portConfig.id);
if (portIds != nullptr) {
portIds->insert(portConfig.portId);
@@ -189,34 +217,50 @@
}
status_t Hal2AidlMapper::createOrUpdatePortConfig(
- const AudioPortConfig& requestedPortConfig, PortConfigs::iterator* result, bool* created) {
- AudioPortConfig appliedPortConfig;
+ const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
bool applied = false;
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
- requestedPortConfig, &appliedPortConfig, &applied)));
+ requestedPortConfig, result, &applied)));
if (!applied) {
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
- appliedPortConfig, &appliedPortConfig, &applied)));
- if (!applied) {
- ALOGE("%s: module %s did not apply suggested config %s",
- __func__, mInstance.c_str(), appliedPortConfig.toString().c_str());
- return NO_INIT;
- }
+ result->id = 0;
+ *created = false;
+ return OK;
}
- int32_t id = appliedPortConfig.id;
+ int32_t id = result->id;
if (requestedPortConfig.id != 0 && requestedPortConfig.id != id) {
LOG_ALWAYS_FATAL("%s: requested port config id %d changed to %d", __func__,
requestedPortConfig.id, id);
}
- auto [it, inserted] = mPortConfigs.insert_or_assign(std::move(id),
- std::move(appliedPortConfig));
- *result = it;
+ auto [_, inserted] = mPortConfigs.insert_or_assign(id, *result);
*created = inserted;
return OK;
}
+status_t Hal2AidlMapper::createOrUpdatePortConfigRetry(
+ const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
+ AudioPortConfig suggestedOrAppliedPortConfig;
+ RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig,
+ &suggestedOrAppliedPortConfig, created));
+ if (suggestedOrAppliedPortConfig.id == 0) {
+ // Try again with the suggested config
+ suggestedOrAppliedPortConfig.id = requestedPortConfig.id;
+ AudioPortConfig appliedPortConfig;
+ RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(suggestedOrAppliedPortConfig,
+ &appliedPortConfig, created));
+ if (appliedPortConfig.id == 0) {
+ ALOGE("%s: module %s did not apply suggested config %s", __func__,
+ mInstance.c_str(), suggestedOrAppliedPortConfig.toString().c_str());
+ return NO_INIT;
+ }
+ *result = appliedPortConfig;
+ } else {
+ *result = suggestedOrAppliedPortConfig;
+ }
+ return OK;
+}
+
void Hal2AidlMapper::eraseConnectedPort(int32_t portId) {
mPorts.erase(portId);
mConnectedPorts.erase(portId);
@@ -226,6 +270,7 @@
ALOGD("%s: disconnected port replacement: %s", __func__, port.toString().c_str());
mDisconnectedPortReplacement = std::pair<int32_t, AudioPort>();
}
+ updateDynamicMixPorts();
}
status_t Hal2AidlMapper::findOrCreatePatch(
@@ -258,11 +303,10 @@
return OK;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateDevicePortConfig(
const AudioDevice& device, const AudioConfig* config, AudioPortConfig* portConfig,
bool* created) {
- auto portConfigIt = findPortConfig(device);
- if (portConfigIt == mPortConfigs.end()) {
+ if (auto portConfigIt = findPortConfig(device); portConfigIt == mPortConfigs.end()) {
auto portsIt = findPort(device);
if (portsIt == mPorts.end()) {
ALOGE("%s: device port for device %s is not found in the module %s",
@@ -274,24 +318,32 @@
if (config != nullptr) {
setPortConfigFromConfig(&requestedPortConfig, *config);
}
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ return createOrUpdatePortConfigRetry(requestedPortConfig, portConfig, created);
} else {
- *created = false;
+ AudioPortConfig requestedPortConfig = portConfigIt->second;
+ if (config != nullptr) {
+ setPortConfigFromConfig(&requestedPortConfig, *config);
+ }
+
+ if (requestedPortConfig != portConfigIt->second) {
+ return createOrUpdatePortConfigRetry(requestedPortConfig, portConfig, created);
+ } else {
+ *portConfig = portConfigIt->second;
+ *created = false;
+ }
}
- *portConfig = portConfigIt->second;
return OK;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateMixPortConfig(
const AudioConfig& config, const std::optional<AudioIoFlags>& flags, int32_t ioHandle,
AudioSource source, const std::set<int32_t>& destinationPortIds,
AudioPortConfig* portConfig, bool* created) {
// These flags get removed one by one in this order when retrying port finding.
static const std::vector<AudioInputFlags> kOptionalInputFlags{
AudioInputFlags::FAST, AudioInputFlags::RAW, AudioInputFlags::VOIP_TX };
- auto portConfigIt = findPortConfig(config, flags, ioHandle);
- if (portConfigIt == mPortConfigs.end() && flags.has_value()) {
+ if (auto portConfigIt = findPortConfig(config, flags, ioHandle);
+ portConfigIt == mPortConfigs.end() && flags.has_value()) {
auto optionalInputFlagsIt = kOptionalInputFlags.begin();
AudioIoFlags matchFlags = flags.value();
auto portsIt = findPort(config, matchFlags, destinationPortIds);
@@ -319,14 +371,14 @@
AudioPortConfig requestedPortConfig;
requestedPortConfig.portId = portsIt->first;
setPortConfigFromConfig(&requestedPortConfig, config);
+ requestedPortConfig.flags = portsIt->second.flags;
requestedPortConfig.ext = AudioPortMixExt{ .handle = ioHandle };
if (matchFlags.getTag() == AudioIoFlags::Tag::input
&& source != AudioSource::SYS_RESERVED_INVALID) {
requestedPortConfig.ext.get<AudioPortExt::Tag::mix>().usecase =
AudioPortMixExtUseCase::make<AudioPortMixExtUseCase::Tag::source>(source);
}
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
} else if (portConfigIt == mPortConfigs.end() && !flags.has_value()) {
ALOGW("%s: mix port config for %s, handle %d not found in the module %s, "
"and was not created as flags are not specified",
@@ -334,22 +386,21 @@
return BAD_VALUE;
} else {
AudioPortConfig requestedPortConfig = portConfigIt->second;
- if (requestedPortConfig.ext.getTag() == AudioPortExt::Tag::mix) {
- AudioPortMixExt& mixExt = requestedPortConfig.ext.get<AudioPortExt::Tag::mix>();
- if (mixExt.usecase.getTag() == AudioPortMixExtUseCase::Tag::source &&
- source != AudioSource::SYS_RESERVED_INVALID) {
- mixExt.usecase.get<AudioPortMixExtUseCase::Tag::source>() = source;
- }
+ setPortConfigFromConfig(&requestedPortConfig, config);
+
+ AudioPortMixExt& mixExt = requestedPortConfig.ext.get<AudioPortExt::Tag::mix>();
+ if (mixExt.usecase.getTag() == AudioPortMixExtUseCase::Tag::source &&
+ source != AudioSource::SYS_RESERVED_INVALID) {
+ mixExt.usecase.get<AudioPortMixExtUseCase::Tag::source>() = source;
}
if (requestedPortConfig != portConfigIt->second) {
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
} else {
+ *portConfig = portConfigIt->second;
*created = false;
}
}
- *portConfig = portConfigIt->second;
return OK;
}
@@ -371,31 +422,29 @@
AudioPortMixExtUseCase::Tag::source ?
requestedPortConfig.ext.get<Tag::mix>().usecase.
get<AudioPortMixExtUseCase::Tag::source>() : AudioSource::SYS_RESERVED_INVALID;
- return findOrCreatePortConfig(config, requestedPortConfig.flags,
+ return findOrCreateMixPortConfig(config, requestedPortConfig.flags,
requestedPortConfig.ext.get<Tag::mix>().handle, source, destinationPortIds,
portConfig, created);
} else if (requestedPortConfig.ext.getTag() == Tag::device) {
- return findOrCreatePortConfig(
- requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
- portConfig, created);
+ if (const auto& p = requestedPortConfig;
+ p.sampleRate.has_value() && p.channelMask.has_value() &&
+ p.format.has_value()) {
+ AudioConfig config;
+ setConfigFromPortConfig(&config, requestedPortConfig);
+ return findOrCreateDevicePortConfig(
+ requestedPortConfig.ext.get<Tag::device>().device, &config,
+ portConfig, created);
+ } else {
+ return findOrCreateDevicePortConfig(
+ requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
+ portConfig, created);
+ }
}
ALOGW("%s: unsupported audio port config: %s",
__func__, requestedPortConfig.toString().c_str());
return BAD_VALUE;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
- const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
- AudioPortConfig* portConfig, Cleanups* cleanups) {
- bool created = false;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
- requestedPortConfig, destinationPortIds, portConfig, &created));
- if (created && cleanups != nullptr) {
- cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
- }
- return OK;
-}
-
status_t Hal2AidlMapper::findPortConfig(const AudioDevice& device, AudioPortConfig* portConfig) {
if (auto it = findPortConfig(device); it != mPortConfigs.end()) {
*portConfig = it->second;
@@ -665,6 +714,14 @@
return false;
}
+status_t Hal2AidlMapper::prepareToDisconnectExternalDevice(const AudioPort& devicePort) {
+ auto portsIt = findPort(devicePort.ext.get<AudioPortExt::device>().device);
+ if (portsIt == mPorts.end()) {
+ return BAD_VALUE;
+ }
+ return statusTFromBinderStatus(mModule->prepareToDisconnectExternalDevice(portsIt->second.id));
+}
+
status_t Hal2AidlMapper::prepareToOpenStream(
int32_t ioHandle, const AudioDevice& device, const AudioIoFlags& flags,
AudioSource source, Cleanups* cleanups, AudioConfig* config,
@@ -673,29 +730,93 @@
this, __func__, ioHandle, device.toString().c_str(),
flags.toString().c_str(), toString(source).c_str(),
config->toString().c_str(), mixPortConfig->toString().c_str());
- resetUnusedPatchesAndPortConfigs();
- const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
+ resetUnusedPatchesPortConfigsAndPorts();
+ const AudioConfig initialConfig = *config;
// Find / create AudioPortConfigs for the device port and the mix port,
// then find / create a patch between them, and open a stream on the mix port.
AudioPortConfig devicePortConfig;
bool created = false;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(device, config,
- &devicePortConfig, &created));
+ RETURN_STATUS_IF_ERROR(findOrCreateDevicePortConfig(device, config,
+ &devicePortConfig, &created));
+ LOG_ALWAYS_FATAL_IF(devicePortConfig.id == 0);
if (created) {
cleanups->add(&Hal2AidlMapper::resetPortConfig, devicePortConfig.id);
}
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(*config, flags, ioHandle, source,
- std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
+ status_t status = prepareToOpenStreamHelper(ioHandle, devicePortConfig.portId,
+ devicePortConfig.id, flags, source, initialConfig, cleanups, config,
+ mixPortConfig, patch);
+ if (status != OK) {
+ // If using the client-provided config did not work out for establishing a mix port config
+ // or patching, try with the device port config. Note that in general device port config and
+ // mix port config are not required to be the same, however they must match if the HAL
+ // module can't perform audio stream conversions.
+ AudioConfig deviceConfig = initialConfig;
+ if (setConfigFromPortConfig(&deviceConfig, devicePortConfig)->base != initialConfig.base) {
+ ALOGD("%s: retrying with device port config: %s", __func__,
+ devicePortConfig.toString().c_str());
+ status = prepareToOpenStreamHelper(ioHandle, devicePortConfig.portId,
+ devicePortConfig.id, flags, source, initialConfig, cleanups,
+ &deviceConfig, mixPortConfig, patch);
+ if (status == OK) {
+ *config = deviceConfig;
+ }
+ }
+ }
+ return status;
+}
+
+status_t Hal2AidlMapper::prepareToOpenStreamHelper(
+ int32_t ioHandle, int32_t devicePortId, int32_t devicePortConfigId,
+ const AudioIoFlags& flags, AudioSource source, const AudioConfig& initialConfig,
+ Cleanups* cleanups, AudioConfig* config, AudioPortConfig* mixPortConfig,
+ AudioPatch* patch) {
+ const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
+ bool created = false;
+ RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
+ std::set<int32_t>{devicePortId}, mixPortConfig, &created));
if (created) {
cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
}
setConfigFromPortConfig(config, *mixPortConfig);
+ bool retryWithSuggestedConfig = false; // By default, let the framework to retry.
+ if (mixPortConfig->id == 0 && config->base == AudioConfigBase{}) {
+ // The HAL proposes a default config, can retry here.
+ retryWithSuggestedConfig = true;
+ } else if (isInput && config->base != initialConfig.base) {
+ // If the resulting config is different, we must stop and provide the config to the
+ // framework so that it can retry.
+ mixPortConfig->id = 0;
+ } else if (!isInput && mixPortConfig->id == 0 &&
+ (initialConfig.base.format.type == AudioFormatType::PCM ||
+ !isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+ AudioOutputFlags::DIRECT) ||
+ isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD))) {
+ // The framework does not retry opening non-direct PCM and IEC61937 outputs, need to retry
+ // here (see 'AudioHwDevice::openOutputStream').
+ retryWithSuggestedConfig = true;
+ }
+ if (mixPortConfig->id == 0 && retryWithSuggestedConfig) {
+ ALOGD("%s: retrying to find/create a mix port config using config %s", __func__,
+ config->toString().c_str());
+ RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
+ std::set<int32_t>{devicePortId}, mixPortConfig, &created));
+ if (created) {
+ cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
+ }
+ setConfigFromPortConfig(config, *mixPortConfig);
+ }
+ if (mixPortConfig->id == 0) {
+ ALOGD("%p %s: returning suggested config for the stream: %s", this, __func__,
+ config->toString().c_str());
+ return OK;
+ }
if (isInput) {
RETURN_STATUS_IF_ERROR(findOrCreatePatch(
- {devicePortConfig.id}, {mixPortConfig->id}, patch, &created));
+ {devicePortConfigId}, {mixPortConfig->id}, patch, &created));
} else {
RETURN_STATUS_IF_ERROR(findOrCreatePatch(
- {mixPortConfig->id}, {devicePortConfig.id}, patch, &created));
+ {mixPortConfig->id}, {devicePortConfigId}, patch, &created));
}
if (created) {
cleanups->add(&Hal2AidlMapper::resetPatch, patch->id);
@@ -706,6 +827,18 @@
return OK;
}
+status_t Hal2AidlMapper::setPortConfig(
+ const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
+ AudioPortConfig* portConfig, Cleanups* cleanups) {
+ bool created = false;
+ RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
+ requestedPortConfig, destinationPortIds, portConfig, &created));
+ if (created && cleanups != nullptr) {
+ cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
+ }
+ return OK;
+}
+
status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
return releaseAudioPatches({patchId});
}
@@ -725,7 +858,7 @@
result = BAD_VALUE;
}
}
- resetUnusedPortConfigs();
+ resetUnusedPortConfigsAndPorts();
return result;
}
@@ -742,7 +875,7 @@
ALOGE("%s: port config id %d not found", __func__, portConfigId);
}
-void Hal2AidlMapper::resetUnusedPatchesAndPortConfigs() {
+void Hal2AidlMapper::resetUnusedPatchesPortConfigsAndPorts() {
// Since patches can be created independently of streams via 'createOrUpdatePatch',
// here we only clean up patches for released streams.
std::set<int32_t> patchesToRelease;
@@ -756,11 +889,11 @@
it = mStreams.erase(it);
}
}
- // 'releaseAudioPatches' also resets unused port configs.
+ // 'releaseAudioPatches' also resets unused port configs and ports.
releaseAudioPatches(patchesToRelease);
}
-void Hal2AidlMapper::resetUnusedPortConfigs() {
+void Hal2AidlMapper::resetUnusedPortConfigsAndPorts() {
// The assumption is that port configs are used to create patches
// (or to open streams, but that involves creation of patches, too). Thus,
// orphaned port configs can and should be reset.
@@ -801,6 +934,7 @@
}
status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort, bool connected) {
+ resetUnusedPatchesPortConfigsAndPorts();
if (connected) {
AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
std::optional<AudioPort> templatePort;
@@ -835,7 +969,6 @@
}
templatePort = portsIt->second;
}
- resetUnusedPatchesAndPortConfigs();
// Use the ID of the "template" port, use all the information from the provided port.
AudioPort connectedPort = devicePort;
@@ -862,7 +995,6 @@
ALOGD("%s: device port for device %s found in the module %s",
__func__, matchDevice.toString().c_str(), mInstance.c_str());
}
- resetUnusedPatchesAndPortConfigs();
// Disconnection of remote submix out with address "0" is a special case. We need to replace
// the connected port entry with the "augmented template".
@@ -895,6 +1027,9 @@
if (status == OK) {
auto portIt = mPorts.find(portId);
if (portIt != mPorts.end()) {
+ if (port->ext.getTag() == AudioPortExt::Tag::mix && portIt->second != *port) {
+ mDynamicMixPortIds.insert(portId);
+ }
portIt->second = *port;
} else {
ALOGW("%s, port(%d) returned successfully from the HAL but not it is not cached",
@@ -943,4 +1078,15 @@
return OK;
}
+void Hal2AidlMapper::updateDynamicMixPorts() {
+ for (int32_t portId : mDynamicMixPortIds) {
+ if (auto it = mPorts.find(portId); it != mPorts.end()) {
+ updateAudioPort(portId, &it->second);
+ } else {
+ // This must not happen
+ ALOGE("%s, cannot find port for id=%d", __func__, portId);
+ }
+ }
+}
+
} // namespace android
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.h b/media/libaudiohal/impl/Hal2AidlMapper.h
index 70a2bd7..f937173 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.h
+++ b/media/libaudiohal/impl/Hal2AidlMapper.h
@@ -36,6 +36,11 @@
// structures directly. Mapper does the job of translating the "legacy" way of identifying ports
// and port configs (by device addresses and I/O handles) into AIDL IDs. Once the framework will
// be updated to provide these IDs directly to libaudiohal, the need for the mapper will cease.
+//
+// Note that unlike DeviceHalInterface, which sometimes allows a method to return an error,
+// but still consider some of the outputs to be valid (for example, in 'open{Input|Output}Stream'),
+// 'Hal2AidlMapper' follows the Binder convention. It means that if a method returns an error,
+// the outputs may not be initialized at all and should not be considered by the caller.
class Hal2AidlMapper {
public:
using Cleanups = Cleanups<Hal2AidlMapper>;
@@ -49,27 +54,6 @@
const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sinks,
int32_t* patchId, Cleanups* cleanups);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioDevice& device,
- const ::aidl::android::media::audio::common::AudioConfig* config,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
- bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioConfig& config,
- const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
- int32_t ioHandle,
- ::aidl::android::media::audio::common::AudioSource source,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
- Cleanups* cleanups = nullptr);
status_t findPortConfig(
const ::aidl::android::media::audio::common::AudioDevice& device,
::aidl::android::media::audio::common::AudioPortConfig* portConfig);
@@ -88,6 +72,10 @@
return ::aidl::android::convertContainer(mRoutes, routes, converter);
}
status_t initialize();
+ status_t prepareToDisconnectExternalDevice(
+ const ::aidl::android::media::audio::common::AudioPort& devicePort);
+ // If the resulting 'mixPortConfig->id' is 0, that means the stream was not created,
+ // and 'config' is a suggested config.
status_t prepareToOpenStream(
int32_t ioHandle,
const ::aidl::android::media::audio::common::AudioDevice& device,
@@ -97,8 +85,13 @@
::aidl::android::media::audio::common::AudioConfig* config,
::aidl::android::media::audio::common::AudioPortConfig* mixPortConfig,
::aidl::android::hardware::audio::core::AudioPatch* patch);
+ status_t setPortConfig(
+ const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+ Cleanups* cleanups = nullptr);
status_t releaseAudioPatch(int32_t patchId);
- void resetUnusedPatchesAndPortConfigs();
+ void resetUnusedPatchesPortConfigsAndPorts();
status_t setDevicePortConnectedState(
const ::aidl::android::media::audio::common::AudioPort& devicePort, bool connected);
@@ -128,9 +121,14 @@
const ::aidl::android::media::audio::common::AudioPort& p);
bool audioDeviceMatches(const ::aidl::android::media::audio::common::AudioDevice& device,
const ::aidl::android::media::audio::common::AudioPortConfig& p);
+ // If the 'result->id' is 0, that means, the config was not created/updated,
+ // and the 'result' is a suggestion from the HAL.
status_t createOrUpdatePortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- PortConfigs::iterator* result, bool *created);
+ ::aidl::android::media::audio::common::AudioPortConfig* result, bool *created);
+ status_t createOrUpdatePortConfigRetry(
+ const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+ ::aidl::android::media::audio::common::AudioPortConfig* result, bool *created);
void eraseConnectedPort(int32_t portId);
status_t findOrCreatePatch(
const std::set<int32_t>& sourcePortConfigIds,
@@ -139,6 +137,24 @@
status_t findOrCreatePatch(
const ::aidl::android::hardware::audio::core::AudioPatch& requestedPatch,
::aidl::android::hardware::audio::core::AudioPatch* patch, bool* created);
+ status_t findOrCreateDevicePortConfig(
+ const ::aidl::android::media::audio::common::AudioDevice& device,
+ const ::aidl::android::media::audio::common::AudioConfig* config,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+ bool* created);
+ // If the resulting 'portConfig->id' is 0, that means the config was not created,
+ // and 'portConfig' is a suggested config.
+ status_t findOrCreateMixPortConfig(
+ const ::aidl::android::media::audio::common::AudioConfig& config,
+ const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
+ int32_t ioHandle,
+ ::aidl::android::media::audio::common::AudioSource source,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
+ status_t findOrCreatePortConfig(
+ const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
Patches::iterator findPatch(const std::set<int32_t>& sourcePortConfigIds,
const std::set<int32_t>& sinkPortConfigIds);
Ports::iterator findPort(const ::aidl::android::media::audio::common::AudioDevice& device);
@@ -153,6 +169,14 @@
const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
int32_t ioHandle);
bool isPortBeingHeld(int32_t portId);
+ status_t prepareToOpenStreamHelper(
+ int32_t ioHandle, int32_t devicePortId, int32_t devicePortConfigId,
+ const ::aidl::android::media::audio::common::AudioIoFlags& flags,
+ ::aidl::android::media::audio::common::AudioSource source,
+ const ::aidl::android::media::audio::common::AudioConfig& initialConfig,
+ Cleanups* cleanups, ::aidl::android::media::audio::common::AudioConfig* config,
+ ::aidl::android::media::audio::common::AudioPortConfig* mixPortConfig,
+ ::aidl::android::hardware::audio::core::AudioPatch* patch);
bool portConfigBelongsToPort(int32_t portConfigId, int32_t portId) {
auto it = mPortConfigs.find(portConfigId);
return it != mPortConfigs.end() && it->second.portId == portId;
@@ -160,10 +184,11 @@
status_t releaseAudioPatches(const std::set<int32_t>& patchIds);
void resetPatch(int32_t patchId) { (void)releaseAudioPatch(patchId); }
void resetPortConfig(int32_t portConfigId);
- void resetUnusedPortConfigs();
+ void resetUnusedPortConfigsAndPorts();
status_t updateAudioPort(
int32_t portId, ::aidl::android::media::audio::common::AudioPort* port);
status_t updateRoutes();
+ void updateDynamicMixPorts();
Ports mPorts;
// Remote submix "template" ports (no address specified, no profiles).
@@ -181,6 +206,7 @@
ConnectedPorts mConnectedPorts;
std::pair<int32_t, ::aidl::android::media::audio::common::AudioPort>
mDisconnectedPortReplacement;
+ std::set<int32_t> mDynamicMixPortIds;
};
} // namespace android
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 378d919..5f525d7 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -234,7 +234,9 @@
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
- return mStream->dump(fd, Args(args).args(), args.size());
+ status_t status = mStream->dump(fd, Args(args).args(), args.size());
+ mStreamPowerLog.dump(fd);
+ return status;
}
status_t StreamHalAidl::start() {
@@ -808,6 +810,7 @@
mOffloadMetadata.channelMask = VALUE_OR_RETURN_STATUS(
::aidl::android::legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
channel_mask, false /*isInput*/));
+ return OK;
}
return BAD_VALUE;
}))) {
diff --git a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
index 8397e9b..c34a671 100644
--- a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
@@ -42,8 +42,6 @@
// necessary to release references to the returned object.
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device) = 0;
- virtual status_t getHalPids(std::vector<pid_t> *pids) = 0;
-
// Sets a DevicesFactoryHalCallback to notify the client.
// The callback can be only set once.
virtual status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) = 0;
diff --git a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
index adff110..1204a3b 100644
--- a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
+++ b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
@@ -201,6 +201,9 @@
ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t*) override {
return ndk::ScopedAStatus::ok();
}
+ ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t) override {
+ return ndk::ScopedAStatus::ok();
+ }
bool mIsScreenTurnedOn = false;
ScreenRotation mScreenRotation = ScreenRotation::DEG_0;
diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp
index a5259aa..b56872c 100644
--- a/media/libeffects/downmix/Android.bp
+++ b/media/libeffects/downmix/Android.bp
@@ -56,9 +56,7 @@
":effectCommonFile",
],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
],
header_libs: [
"libaudioeffects",
@@ -71,6 +69,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/downmix/aidl/DownmixContext.cpp b/media/libeffects/downmix/aidl/DownmixContext.cpp
index 0e76d1d..5fb44b5 100644
--- a/media/libeffects/downmix/aidl/DownmixContext.cpp
+++ b/media/libeffects/downmix/aidl/DownmixContext.cpp
@@ -20,12 +20,60 @@
#include "DownmixContext.h"
-using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::common::getChannelCount;
+using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioConfig;
namespace aidl::android::hardware::audio::effect {
+namespace {
+
+inline bool isChannelMaskValid(const AudioChannelLayout& channelMask) {
+ if (channelMask.getTag() != AudioChannelLayout::layoutMask) return false;
+ int chMask = channelMask.get<AudioChannelLayout::layoutMask>();
+ // check against unsupported channels (up to FCC_26)
+ constexpr uint32_t MAXIMUM_CHANNEL_MASK = AudioChannelLayout::LAYOUT_22POINT2 |
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT |
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT;
+ if (chMask & ~MAXIMUM_CHANNEL_MASK) {
+ LOG(ERROR) << "Unsupported channels in " << (chMask & ~MAXIMUM_CHANNEL_MASK);
+ return false;
+ }
+ return true;
+}
+
+inline bool isStereoChannelMask(const AudioChannelLayout& channelMask) {
+ if (channelMask.getTag() != AudioChannelLayout::layoutMask) return false;
+
+ return channelMask.get<AudioChannelLayout::layoutMask>() == AudioChannelLayout::LAYOUT_STEREO;
+}
+
+} // namespace
+
+bool DownmixContext::validateCommonConfig(const Parameter::Common& common) {
+ const AudioConfig& input = common.input;
+ const AudioConfig& output = common.output;
+ if (input.base.sampleRate != output.base.sampleRate) {
+ LOG(ERROR) << __func__ << ": SRC not supported, input: " << input.toString()
+ << " output: " << output.toString();
+ return false;
+ }
+
+ if (!isStereoChannelMask(output.base.channelMask)) {
+ LOG(ERROR) << __func__ << ": output should be stereo, not "
+ << output.base.channelMask.toString();
+ return false;
+ }
+
+ if (!isChannelMaskValid(input.base.channelMask)) {
+ LOG(ERROR) << __func__ << ": invalid input channel, " << input.base.channelMask.toString();
+ return false;
+ }
+
+ return true;
+}
+
DownmixContext::DownmixContext(int statusDepth, const Parameter::Common& common)
: EffectContext(statusDepth, common) {
LOG(DEBUG) << __func__;
@@ -62,8 +110,7 @@
resetBuffer();
}
-IEffect::Status DownmixContext::lvmProcess(float* in, float* out, int samples) {
- LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
+IEffect::Status DownmixContext::downmixProcess(float* in, float* out, int samples) {
IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0};
if (in == nullptr || out == nullptr ||
@@ -84,7 +131,6 @@
bool accumulate = false;
int frames = samples * sizeof(float) / getInputFrameSize();
if (mType == Downmix::Type::STRIP) {
- int inputChannelCount = getChannelCount(mChMask);
while (frames) {
if (accumulate) {
out[0] = std::clamp(out[0] + in[0], -1.f, 1.f);
@@ -93,7 +139,7 @@
out[0] = in[0];
out[1] = in[1];
}
- in += inputChannelCount;
+ in += mInputChannelCount;
out += 2;
frames--;
}
@@ -105,8 +151,11 @@
return status;
}
}
- LOG(DEBUG) << __func__ << " done processing";
- return {STATUS_OK, samples, samples};
+ int producedSamples = (samples / mInputChannelCount) << 1;
+ LOG(DEBUG) << __func__ << " done processing " << samples << " samples, generated "
+ << producedSamples << " frameSize: " << getInputFrameSize() << " - "
+ << getOutputFrameSize();
+ return {STATUS_OK, samples, producedSamples};
}
void DownmixContext::init_params(const Parameter::Common& common) {
@@ -122,18 +171,4 @@
}
}
-bool DownmixContext::isChannelMaskValid(AudioChannelLayout channelMask) {
- if (channelMask.getTag() != AudioChannelLayout::layoutMask) return false;
- int chMask = channelMask.get<AudioChannelLayout::layoutMask>();
- // check against unsupported channels (up to FCC_26)
- constexpr uint32_t MAXIMUM_CHANNEL_MASK = AudioChannelLayout::LAYOUT_22POINT2 |
- AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT |
- AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT;
- if (chMask & ~MAXIMUM_CHANNEL_MASK) {
- LOG(ERROR) << "Unsupported channels in " << (chMask & ~MAXIMUM_CHANNEL_MASK);
- return false;
- }
- return true;
-}
-
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/downmix/aidl/DownmixContext.h b/media/libeffects/downmix/aidl/DownmixContext.h
index 1571c38..a381d7f 100644
--- a/media/libeffects/downmix/aidl/DownmixContext.h
+++ b/media/libeffects/downmix/aidl/DownmixContext.h
@@ -50,7 +50,9 @@
return RetCode::SUCCESS;
}
- IEffect::Status lvmProcess(float* in, float* out, int samples);
+ IEffect::Status downmixProcess(float* in, float* out, int samples);
+
+ static bool validateCommonConfig(const Parameter::Common& common);
private:
DownmixState mState;
@@ -60,7 +62,6 @@
// Common Params
void init_params(const Parameter::Common& common);
- bool isChannelMaskValid(::aidl::android::media::audio::common::AudioChannelLayout channelMask);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.cpp b/media/libeffects/downmix/aidl/EffectDownmix.cpp
index 7068c5c..46156ce 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.cpp
+++ b/media/libeffects/downmix/aidl/EffectDownmix.cpp
@@ -71,42 +71,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus DownmixImpl::setParameterCommon(const Parameter& param) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
-
- auto tag = param.getTag();
- switch (tag) {
- case Parameter::common:
- RETURN_IF(mContext->setCommon(param.get<Parameter::common>()) != RetCode::SUCCESS,
- EX_ILLEGAL_ARGUMENT, "setCommFailed");
- break;
- case Parameter::deviceDescription:
- RETURN_IF(mContext->setOutputDevice(param.get<Parameter::deviceDescription>()) !=
- RetCode::SUCCESS,
- EX_ILLEGAL_ARGUMENT, "setDeviceFailed");
- break;
- case Parameter::mode:
- RETURN_IF(mContext->setAudioMode(param.get<Parameter::mode>()) != RetCode::SUCCESS,
- EX_ILLEGAL_ARGUMENT, "setModeFailed");
- break;
- case Parameter::source:
- RETURN_IF(mContext->setAudioSource(param.get<Parameter::source>()) != RetCode::SUCCESS,
- EX_ILLEGAL_ARGUMENT, "setSourceFailed");
- break;
- case Parameter::volumeStereo:
- RETURN_IF(mContext->setVolumeStereo(param.get<Parameter::volumeStereo>()) !=
- RetCode::SUCCESS,
- EX_ILLEGAL_ARGUMENT, "setVolumeStereoFailed");
- break;
- default: {
- LOG(ERROR) << __func__ << " unsupportedParameterTag " << toString(tag);
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commonParamNotSupported");
- }
- }
- return ndk::ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus DownmixImpl::commandImpl(CommandId command) {
RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
switch (command) {
@@ -193,6 +157,8 @@
return mContext;
}
+ if (!DownmixContext::validateCommonConfig(common)) return nullptr;
+
mContext = std::make_shared<DownmixContext>(1 /* statusFmqDepth */, common);
return mContext;
}
@@ -204,13 +170,52 @@
return RetCode::SUCCESS;
}
+void DownmixImpl::process() {
+ /**
+ * wait for the EventFlag without lock, it's ok because the mEfGroup pointer will not change
+ * in the life cycle of workerThread (threadLoop).
+ */
+ uint32_t efState = 0;
+ if (!mEventFlag || ::android::OK != mEventFlag->wait(kEventFlagNotEmpty, &efState)) {
+ LOG(ERROR) << getEffectName() << __func__ << ": StatusEventFlag invalid";
+ }
+
+ {
+ std::lock_guard lg(mImplMutex);
+ RETURN_VALUE_IF(!mImplContext, void(), "nullContext");
+ auto statusMQ = mImplContext->getStatusFmq();
+ auto inputMQ = mImplContext->getInputDataFmq();
+ auto outputMQ = mImplContext->getOutputDataFmq();
+ auto buffer = mImplContext->getWorkBuffer();
+ if (!inputMQ || !outputMQ) {
+ return;
+ }
+
+ const auto availableToRead = inputMQ->availableToRead();
+ const auto availableToWrite = outputMQ->availableToWrite() *
+ mImplContext->getInputFrameSize() /
+ mImplContext->getOutputFrameSize();
+ assert(mImplContext->getWorkBufferSize() >=
+ std::max(availableToRead(), availableToWrite));
+ auto processSamples = std::min(availableToRead, availableToWrite);
+ if (processSamples) {
+ inputMQ->read(buffer, processSamples);
+ IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
+ outputMQ->write(buffer, status.fmqProduced);
+ statusMQ->writeBlocking(&status, 1);
+ LOG(VERBOSE) << getEffectName() << __func__ << ": done processing, effect consumed "
+ << status.fmqConsumed << " produced " << status.fmqProduced;
+ }
+ }
+}
+
// Processing method running in EffectWorker thread.
IEffect::Status DownmixImpl::effectProcessImpl(float* in, float* out, int sampleToProcess) {
if (!mContext) {
LOG(ERROR) << __func__ << " nullContext";
return {EX_NULL_POINTER, 0, 0};
}
- return mContext->lvmProcess(in, out, sampleToProcess);
+ return mContext->downmixProcess(in, out, sampleToProcess);
}
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.h b/media/libeffects/downmix/aidl/EffectDownmix.h
index 812d26b..54557dc 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.h
+++ b/media/libeffects/downmix/aidl/EffectDownmix.h
@@ -34,21 +34,26 @@
LOG(DEBUG) << __func__;
}
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterCommon(const Parameter& param) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- RetCode releaseContext() override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process)
+ REQUIRES(mImplMutex) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> getContext() override { return mContext; }
std::string getEffectName() override { return kEffectName; }
+ // downmix override the process because of different input/output sample size requirement
+ void process() override;
+
private:
- std::shared_ptr<DownmixContext> mContext;
- ndk::ScopedAStatus getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific);
+ std::shared_ptr<DownmixContext> mContext GUARDED_BY(mImplMutex);
+ ndk::ScopedAStatus getParameterDownmix(const Downmix::Tag& tag, Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/Android.bp b/media/libeffects/dynamicsproc/Android.bp
index 7838117..e93a4e6 100644
--- a/media/libeffects/dynamicsproc/Android.bp
+++ b/media/libeffects/dynamicsproc/Android.bp
@@ -86,9 +86,7 @@
],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
"dynamicsprocessingdefaults",
],
@@ -97,6 +95,6 @@
],
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
index 85ea53a..1fedea4 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
@@ -211,11 +211,12 @@
RETURN_IF(common.input.base.format.pcm != common.output.base.format.pcm ||
common.input.base.format.pcm != PcmType::FLOAT_32_BIT,
EX_ILLEGAL_ARGUMENT, "dataMustBe32BitsFloat");
+ std::lock_guard lg(mImplMutex);
RETURN_OK_IF(mState != State::INIT);
- auto context = createContext(common);
- RETURN_IF(!context, EX_NULL_POINTER, "createContextFailed");
+ mImplContext = createContext(common);
+ RETURN_IF(!mContext || !mImplContext, EX_NULL_POINTER, "createContextFailed");
+ mEventFlag = mImplContext->getStatusEventFlag();
- RETURN_IF_ASTATUS_NOT_OK(setParameterCommon(common), "setCommParamErr");
if (specific.has_value()) {
RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr");
} else {
@@ -227,8 +228,8 @@
}
mState = State::IDLE;
- context->dupeFmq(ret);
- RETURN_IF(createThread(context, getEffectName()) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+ mContext->dupeFmq(ret);
+ RETURN_IF(createThread(getEffectName()) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
"FailedToCreateWorker");
return ndk::ScopedAStatus::ok();
}
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
index 1e1e72e..4897888 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
@@ -39,22 +39,25 @@
ndk::ScopedAStatus open(const Parameter::Common& common,
const std::optional<Parameter::Specific>& specific,
OpenEffectReturn* ret) override;
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- RetCode releaseContext() override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process)
+ REQUIRES(mImplMutex) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> getContext() override { return mContext; }
std::string getEffectName() override { return kEffectName; }
private:
- std::shared_ptr<DynamicsProcessingContext> mContext;
+ std::shared_ptr<DynamicsProcessingContext> mContext GUARDED_BY(mImplMutex);
ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
bool isParamInRange(const Parameter::Specific& specific);
};
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
index e5e5368..042b063 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
@@ -63,6 +63,9 @@
}
RetCode DynamicsProcessingContext::setCommon(const Parameter::Common& common) {
+ if(auto ret = updateIOFrameSize(common); ret != RetCode::SUCCESS) {
+ return ret;
+ }
mCommon = common;
init();
LOG(INFO) << __func__ << common.toString();
@@ -312,9 +315,11 @@
void DynamicsProcessingContext::init() {
std::lock_guard lg(mMutex);
- mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
- mChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
- mCommon.input.base.channelMask);
+ if (mState == DYNAMICS_PROCESSING_STATE_UNINITIALIZED) {
+ mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
+ }
+ mChannelCount = static_cast<int>(::aidl::android::hardware::audio::common::getChannelCount(
+ mCommon.input.base.channelMask));
}
dp_fx::DPChannel* DynamicsProcessingContext::getChannel_l(int channel) {
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
index ced7f19..839c6dd 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
@@ -74,7 +74,7 @@
static constexpr float kPreferredProcessingDurationMs = 10.0f;
static constexpr int kBandCount = 5;
std::mutex mMutex;
- size_t mChannelCount GUARDED_BY(mMutex) = 0;
+ int mChannelCount GUARDED_BY(mMutex) = 0;
DynamicsProcessingState mState GUARDED_BY(mMutex) = DYNAMICS_PROCESSING_STATE_UNINITIALIZED;
std::unique_ptr<dp_fx::DPFrequency> mDpFreq GUARDED_BY(mMutex) = nullptr;
bool mEngineInited GUARDED_BY(mMutex) = false;
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index fc80211..7d96b53 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -75,9 +75,7 @@
],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
"hapticgeneratordefaults",
],
@@ -86,6 +84,6 @@
],
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
index fe9616a..53dcd49 100644
--- a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
@@ -33,16 +33,18 @@
LOG(DEBUG) << __func__;
}
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- RetCode releaseContext() override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process)
+ REQUIRES(mImplMutex) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> getContext() override { return mContext; }
std::string getEffectName() override { return kEffectName; }
private:
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
index de44e05..354ee00 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <cstddef>
#define LOG_TAG "AHAL_HapticGeneratorContext"
#include <Utils.h>
@@ -162,8 +163,8 @@
}
// Construct input buffer according to haptic channel source
- for (size_t i = 0; i < mFrameCount; ++i) {
- for (size_t j = 0; j < mParams.mHapticChannelCount; ++j) {
+ for (int64_t i = 0; i < mFrameCount; ++i) {
+ for (int j = 0; j < mParams.mHapticChannelCount; ++j) {
mInputBuffer[i * mParams.mHapticChannelCount + j] =
in[i * mParams.mAudioChannelCount + mParams.mHapticChannelSource[j]];
}
@@ -180,8 +181,7 @@
// buffer, which contains haptic data at the end of the buffer, directly to sink buffer.
// In that case, copy haptic data to input buffer instead of output buffer.
// Note: this may not work with rpc/binder calls
- int offset = samples;
- for (int i = 0; i < hapticSampleCount; ++i) {
+ for (size_t i = 0; i < hapticSampleCount; ++i) {
in[samples + i] = hapticOutBuffer[i];
}
return {STATUS_OK, samples, static_cast<int32_t>(samples + hapticSampleCount)};
@@ -199,7 +199,7 @@
mParams.mHapticChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
outputChMask, media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
LOG_ALWAYS_FATAL_IF(mParams.mHapticChannelCount > 2, "haptic channel count is too large");
- for (size_t i = 0; i < mParams.mHapticChannelCount; ++i) {
+ for (int i = 0; i < mParams.mHapticChannelCount; ++i) {
// By default, use the first audio channel to generate haptic channels.
mParams.mHapticChannelSource[i] = 0;
}
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
index a0a0a4c..26e69e4 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
@@ -92,7 +92,7 @@
HapticGeneratorState mState;
HapticGeneratorParam mParams GUARDED_BY(mMutex);
int mSampleRate;
- int mFrameCount = 0;
+ int64_t mFrameCount = 0;
// A cache for all shared pointers of the HapticGenerator
struct HapticGeneratorProcessorsRecord mProcessorsRecord;
diff --git a/media/libeffects/loudness/Android.bp b/media/libeffects/loudness/Android.bp
index 7acba11..46e4669 100644
--- a/media/libeffects/loudness/Android.bp
+++ b/media/libeffects/loudness/Android.bp
@@ -54,9 +54,7 @@
":effectCommonFile",
],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
],
header_libs: [
"libaudioeffects",
@@ -71,6 +69,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
index 5b9e924..e2e716c 100644
--- a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
+++ b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
@@ -33,22 +33,25 @@
LOG(DEBUG) << __func__;
}
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- RetCode releaseContext() override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process)
+ REQUIRES(mImplMutex) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> getContext() override { return mContext; }
std::string getEffectName() override { return kEffectName; }
private:
- std::shared_ptr<LoudnessEnhancerContext> mContext;
+ std::shared_ptr<LoudnessEnhancerContext> mContext GUARDED_BY(mImplMutex);
ndk::ScopedAStatus getParameterLoudnessEnhancer(const LoudnessEnhancer::Tag& tag,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index a163f4b..bb7e4c6 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -20,6 +20,7 @@
#define LOG_TAG "BundleContext"
#include <android-base/logging.h>
#include <audio_utils/power.h>
+#include <media/AidlConversionCppNdk.h>
#include <Utils.h>
#include "BundleContext.h"
@@ -36,9 +37,10 @@
const lvm::BundleEffectType& type)
: EffectContext(statusDepth, common), mType(type) {
LOG(DEBUG) << __func__ << type;
- int channelCount = ::aidl::android::hardware::audio::common::getChannelCount(
+
+ int inputChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
common.input.base.channelMask);
- mSamplesPerSecond = common.input.base.sampleRate * channelCount;
+ mSamplesPerSecond = common.input.base.sampleRate * inputChannelCount;
}
BundleContext::~BundleContext() {
@@ -50,9 +52,15 @@
std::lock_guard lg(mMutex);
// init with pre-defined preset NORMAL
for (std::size_t i = 0; i < lvm::MAX_NUM_BANDS; i++) {
- mBandGainMdB[i] = lvm::kSoftPresets[0 /* normal */][i] * 100;
+ mBandGainmB[i] = lvm::kSoftPresets[0 /* normal */][i] * 100;
}
+ // Initialise control params
+ LVM_ControlParams_t controlParams;
+ RetCode retStatus = initControlParameter(controlParams);
+ RETURN_VALUE_IF(retStatus != RetCode::SUCCESS, RetCode::ERROR_ILLEGAL_PARAMETER,
+ " UnsupportedParams");
+
// allocate lvm instance
LVM_ReturnStatus_en status;
LVM_InstParams_t params = {.BufferMode = LVM_UNMANAGED_BUFFERS,
@@ -63,8 +71,6 @@
GOTO_IF_LVM_ERROR(status, deinit, "LVM_GetInstanceHandleFailed");
// set control
- LVM_ControlParams_t controlParams;
- initControlParameter(controlParams);
status = LVM_SetControlParameters(mInstance, &controlParams);
GOTO_IF_LVM_ERROR(status, deinit, "LVM_SetControlParametersFailed");
@@ -227,8 +233,8 @@
bool viEnabled = params.VirtualizerOperatingMode == LVM_MODE_ON;
if (eqEnabled) {
- for (int i = 0; i < lvm::MAX_NUM_BANDS; i++) {
- float bandFactor = mBandGainMdB[i] / 1500.0;
+ for (unsigned int i = 0; i < lvm::MAX_NUM_BANDS; i++) {
+ float bandFactor = mBandGainmB[i] / 1500.0;
float bandCoefficient = lvm::kBandEnergyCoefficient[i];
float bandEnergy = bandFactor * bandCoefficient * bandCoefficient;
if (bandEnergy > 0) energyContribution += bandEnergy;
@@ -236,9 +242,9 @@
// cross EQ coefficients
float bandFactorSum = 0;
- for (int i = 0; i < lvm::MAX_NUM_BANDS - 1; i++) {
- float bandFactor1 = mBandGainMdB[i] / 1500.0;
- float bandFactor2 = mBandGainMdB[i + 1] / 1500.0;
+ for (unsigned int i = 0; i < lvm::MAX_NUM_BANDS - 1; i++) {
+ float bandFactor1 = mBandGainmB[i] / 1500.0;
+ float bandFactor2 = mBandGainmB[i + 1] / 1500.0;
if (bandFactor1 > 0 && bandFactor2 > 0) {
float crossEnergy =
@@ -259,8 +265,8 @@
energyContribution += boostFactor * boostCoefficient * boostCoefficient;
if (eqEnabled) {
- for (int i = 0; i < lvm::MAX_NUM_BANDS; i++) {
- float bandFactor = mBandGainMdB[i] / 1500.0;
+ for (unsigned int i = 0; i < lvm::MAX_NUM_BANDS; i++) {
+ float bandFactor = mBandGainmB[i] / 1500.0;
float bandCrossCoefficient = lvm::kBassBoostEnergyCrossCoefficient[i];
float bandEnergy = boostFactor * bandFactor * bandCrossCoefficient;
if (bandEnergy > 0) energyBassBoost += bandEnergy;
@@ -312,7 +318,9 @@
device != AudioDeviceDescription{AudioDeviceType::OUT_CARKIT,
AudioDeviceDescription::CONNECTION_BT_SCO} &&
device != AudioDeviceDescription{AudioDeviceType::OUT_SPEAKER,
- AudioDeviceDescription::CONNECTION_BT_A2DP}) {
+ AudioDeviceDescription::CONNECTION_BT_A2DP} &&
+ device != AudioDeviceDescription{AudioDeviceType::OUT_SUBMIX,
+ AudioDeviceDescription::CONNECTION_VIRTUAL}) {
return false;
}
}
@@ -329,7 +337,9 @@
device != AudioDeviceDescription{AudioDeviceType::OUT_HEADPHONE,
AudioDeviceDescription::CONNECTION_BT_A2DP} &&
device != AudioDeviceDescription{AudioDeviceType::OUT_HEADSET,
- AudioDeviceDescription::CONNECTION_USB}) {
+ AudioDeviceDescription::CONNECTION_USB} &&
+ device != AudioDeviceDescription{AudioDeviceType::OUT_SUBMIX,
+ AudioDeviceDescription::CONNECTION_VIRTUAL}) {
return false;
}
}
@@ -421,7 +431,6 @@
RetCode BundleContext::setVolumeStereo(const Parameter::VolumeStereo& volume) {
LVM_ControlParams_t params;
- LVM_ReturnStatus_en status = LVM_SUCCESS;
// Convert volume to dB
float leftdB = VolToDb(volume.left);
@@ -470,6 +479,7 @@
RetCode BundleContext::setEqualizerBandLevels(const std::vector<Equalizer::BandLevel>& bandLevels) {
RETURN_VALUE_IF(bandLevels.size() > lvm::MAX_NUM_BANDS || bandLevels.empty(),
RetCode::ERROR_ILLEGAL_PARAMETER, "sizeExceedMax");
+
RetCode ret = updateControlParameter(bandLevels);
if (RetCode::SUCCESS == ret) {
mCurPresetIdx = lvm::PRESET_CUSTOM;
@@ -484,15 +494,13 @@
std::vector<Equalizer::BandLevel> bandLevels;
bandLevels.reserve(lvm::MAX_NUM_BANDS);
for (std::size_t i = 0; i < lvm::MAX_NUM_BANDS; i++) {
- bandLevels.emplace_back(
- Equalizer::BandLevel{static_cast<int32_t>(i), mBandGainMdB[i]});
+ bandLevels.emplace_back(Equalizer::BandLevel{static_cast<int32_t>(i), mBandGainmB[i]});
}
return bandLevels;
}
std::vector<int32_t> BundleContext::getEqualizerCenterFreqs() {
std::vector<int32_t> freqs;
-
LVM_ControlParams_t params;
{
std::lock_guard lg(mMutex);
@@ -512,14 +520,14 @@
const auto [min, max] =
std::minmax_element(bandLevels.begin(), bandLevels.end(),
[](const auto& a, const auto& b) { return a.index < b.index; });
- return min->index >= 0 && max->index < lvm::MAX_NUM_BANDS;
+ return min->index >= 0 && static_cast<size_t>(max->index) < lvm::MAX_NUM_BANDS;
}
RetCode BundleContext::updateControlParameter(const std::vector<Equalizer::BandLevel>& bandLevels) {
RETURN_VALUE_IF(!isBandLevelIndexInRange(bandLevels), RetCode::ERROR_ILLEGAL_PARAMETER,
"indexOutOfRange");
- std::array<int, lvm::MAX_NUM_BANDS> tempLevel(mBandGainMdB);
+ std::array<int, lvm::MAX_NUM_BANDS> tempLevel(mBandGainmB);
for (const auto& it : bandLevels) {
tempLevel[it.index] = it.levelMb;
}
@@ -540,8 +548,8 @@
RETURN_VALUE_IF(LVM_SUCCESS != LVM_SetControlParameters(mInstance, ¶ms),
RetCode::ERROR_EFFECT_LIB_ERROR, " setControlParamFailed");
}
- mBandGainMdB = tempLevel;
- LOG(DEBUG) << __func__ << " update bandGain to " << ::android::internal::ToString(mBandGainMdB)
+ mBandGainmB = tempLevel;
+ LOG(DEBUG) << __func__ << " update bandGain to " << ::android::internal::ToString(mBandGainmB)
<< "mdB";
return RetCode::SUCCESS;
@@ -619,11 +627,30 @@
return RetCode::SUCCESS;
}
-void BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+RetCode BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+ int outputChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
+ mCommon.output.base.channelMask);
+ auto outputChannelMaskConv = aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ mCommon.output.base.channelMask, /*isInput*/ false);
+ RETURN_VALUE_IF(!outputChannelMaskConv.ok(), RetCode::ERROR_ILLEGAL_PARAMETER,
+ " outputChannelMaskNotValid");
+
+ params.NrChannels = outputChannelCount;
+ params.ChMask = outputChannelMaskConv.value();
+ params.SampleRate = lvmFsForSampleRate(mCommon.input.base.sampleRate);
+
+ int inputChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
+ mCommon.input.base.channelMask);
+ if (inputChannelCount == 1) {
+ params.SourceFormat = LVM_MONO;
+ } else if (inputChannelCount == 2) {
+ params.SourceFormat = LVM_STEREO;
+ } else if (inputChannelCount > 2 && inputChannelCount <= LVM_MAX_CHANNELS) {
+ params.SourceFormat = LVM_MULTICHANNEL;
+ }
+
/* General parameters */
params.OperatingMode = LVM_MODE_ON;
- params.SampleRate = LVM_FS_44100;
- params.SourceFormat = LVM_STEREO;
params.SpeakerType = LVM_HEADPHONES;
/* Concert Sound parameters */
@@ -658,13 +685,7 @@
params.PSA_Enable = LVM_PSA_OFF;
params.PSA_PeakDecayRate = LVM_PSA_SPEED_MEDIUM;
- /* TE Control parameters */
- params.TE_OperatingMode = LVM_TE_OFF;
- params.TE_EffectLevel = 0;
-
- params.NrChannels = audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
- params.ChMask = AUDIO_CHANNEL_OUT_STEREO;
- params.SourceFormat = LVM_STEREO;
+ return RetCode::SUCCESS;
}
void BundleContext::initHeadroomParameter(LVM_HeadroomParams_t& params) const {
@@ -833,13 +854,13 @@
LOG(DEBUG) << "Effect_process() processing last frame";
}
mNumberEffectsCalled = 0;
- LVM_UINT16 frames = samples * sizeof(float) / frameSize;
float* outTmp = (accumulate ? getWorkBuffer() : out);
/* Process the samples */
LVM_ReturnStatus_en lvmStatus;
{
std::lock_guard lg(mMutex);
- lvmStatus = LVM_Process(mInstance, in, outTmp, frames, 0);
+
+ lvmStatus = LVM_Process(mInstance, in, outTmp, inputFrameCount, 0);
if (lvmStatus != LVM_SUCCESS) {
LOG(ERROR) << __func__ << lvmStatus;
return {EX_UNSUPPORTED_OPERATION, 0, 0};
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index 779d53a..809f402 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -41,15 +41,6 @@
RetCode disable();
RetCode disableOperatingMode();
- void setSampleRate(const int sampleRate) { mSampleRate = sampleRate; }
- int getSampleRate() const { return mSampleRate; }
-
- void setChannelMask(const aidl::android::media::audio::common::AudioChannelLayout& chMask) {
- mChMask = chMask;
- }
- aidl::android::media::audio::common::AudioChannelLayout getChannelMask() const {
- return mChMask;
- }
bool isDeviceSupportedBassBoost(
const std::vector<aidl::android::media::audio::common::AudioDeviceDescription>&
devices);
@@ -105,9 +96,7 @@
LVM_Handle_t mInstance GUARDED_BY(mMutex);
aidl::android::media::audio::common::AudioDeviceDescription mVirtualizerForcedDevice;
- aidl::android::media::audio::common::AudioChannelLayout mChMask;
- int mSampleRate = LVM_FS_44100;
int mSamplesPerSecond = 0;
int mSamplesToExitCountEq = 0;
int mSamplesToExitCountBb = 0;
@@ -129,7 +118,7 @@
int mBassStrengthSaved = 0;
// Equalizer
int mCurPresetIdx = lvm::PRESET_CUSTOM; /* Current preset being used */
- std::array<int, lvm::MAX_NUM_BANDS> mBandGainMdB; /* band gain in millibels */
+ std::array<int, lvm::MAX_NUM_BANDS> mBandGainmB; /* band gain in millibels */
// Virtualizer
int mVirtStrengthSaved = 0; /* Conversion between Get/Set */
bool mVirtualizerTempDisabled = false;
@@ -139,7 +128,7 @@
float mVolume = 0;
bool mMuteEnabled = false; /* Must store as mute = -96dB level */
- void initControlParameter(LVM_ControlParams_t& params) const;
+ RetCode initControlParameter(LVM_ControlParams_t& params) const;
void initHeadroomParameter(LVM_HeadroomParams_t& params) const;
RetCode limitLevel();
static float VolToDb(float vol);
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
index 3148d36..257e972 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
@@ -425,10 +425,6 @@
return mContext;
}
-std::shared_ptr<EffectContext> EffectBundleAidl::getContext() {
- return mContext;
-}
-
RetCode EffectBundleAidl::releaseContext() {
if (mContext) {
GlobalSession::getGlobalSession().releaseSession(mType, mContext->getSessionId());
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
index ec1abe8..429e941 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
@@ -36,41 +36,47 @@
~EffectBundleAidl() override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterCommon(const Parameter& param) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
+ ndk::ScopedAStatus setParameterCommon(const Parameter& param) REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- std::shared_ptr<EffectContext> getContext() override;
- RetCode releaseContext() override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int samples)
+ REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
std::string getEffectName() override { return *mEffectName; }
private:
- std::shared_ptr<BundleContext> mContext;
+ std::shared_ptr<BundleContext> mContext GUARDED_BY(mImplMutex);
const Descriptor* mDescriptor;
const std::string* mEffectName;
lvm::BundleEffectType mType = lvm::BundleEffectType::EQUALIZER;
IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
- ndk::ScopedAStatus setParameterBassBoost(const Parameter::Specific& specific);
- ndk::ScopedAStatus getParameterBassBoost(const BassBoost::Id& id,
- Parameter::Specific* specific);
+ ndk::ScopedAStatus setParameterBassBoost(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
+ ndk::ScopedAStatus getParameterBassBoost(const BassBoost::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
- ndk::ScopedAStatus setParameterEqualizer(const Parameter::Specific& specific);
- ndk::ScopedAStatus getParameterEqualizer(const Equalizer::Id& id,
- Parameter::Specific* specific);
- ndk::ScopedAStatus setParameterVolume(const Parameter::Specific& specific);
- ndk::ScopedAStatus getParameterVolume(const Volume::Id& id, Parameter::Specific* specific);
- ndk::ScopedAStatus setParameterVirtualizer(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterEqualizer(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
+ ndk::ScopedAStatus getParameterEqualizer(const Equalizer::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
+ ndk::ScopedAStatus setParameterVolume(const Parameter::Specific& specific) REQUIRES(mImplMutex);
+ ndk::ScopedAStatus getParameterVolume(const Volume::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
+ ndk::ScopedAStatus setParameterVirtualizer(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterVirtualizer(const Virtualizer::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific) REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index fa300d2..62837b9 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -110,9 +110,7 @@
],
static_libs: ["libmusicbundle"],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
],
local_include_dirs: ["Aidl"],
header_libs: [
@@ -120,14 +118,18 @@
"libhardware_headers",
],
shared_libs: [
+ "libaudio_aidl_conversion_common_ndk",
+ "libaudioutils",
"liblog",
+ "libstagefright_foundation",
],
cflags: [
"-Wthread-safety",
+ "-DBACKEND_NDK",
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
@@ -140,9 +142,7 @@
],
static_libs: ["libreverb"],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
],
local_include_dirs: ["Reverb/aidl"],
header_libs: [
@@ -160,6 +160,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
index b49d109..f9afe69 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
@@ -358,10 +358,6 @@
return mContext;
}
-std::shared_ptr<EffectContext> EffectReverb::getContext() {
- return mContext;
-}
-
RetCode EffectReverb::releaseContext() {
if (mContext) {
mContext.reset();
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
index d7d2bbd..e0771a1 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
@@ -30,35 +30,41 @@
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- std::shared_ptr<EffectContext> getContext() override;
- RetCode releaseContext() override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int samples)
+ REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
std::string getEffectName() override { return *mEffectName; }
private:
- std::shared_ptr<ReverbContext> mContext;
+ std::shared_ptr<ReverbContext> mContext GUARDED_BY(mImplMutex);
const Descriptor* mDescriptor;
const std::string* mEffectName;
lvm::ReverbEffectType mType;
IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
- ndk::ScopedAStatus setParameterPresetReverb(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterPresetReverb(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterPresetReverb(const PresetReverb::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
- ndk::ScopedAStatus setParameterEnvironmentalReverb(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterEnvironmentalReverb(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterEnvironmentalReverb(const EnvironmentalReverb::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
index 79e67f2..468b268 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
@@ -329,7 +329,7 @@
*/
int ReverbContext::convertLevel(int level) {
- for (int i = 0; i < kLevelMapping.size(); i++) {
+ for (std::size_t i = 0; i < kLevelMapping.size(); i++) {
if (level <= kLevelMapping[i]) {
return i;
}
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index d018c47..994b061 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -67,9 +67,7 @@
":effectCommonFile",
],
defaults: [
- "aidlaudioservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
+ "aidlaudioeffectservice_defaults",
],
local_include_dirs: ["aidl"],
shared_libs: [
@@ -91,6 +89,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp b/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
index e8ae8b3..7552804 100644
--- a/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
+++ b/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
@@ -412,10 +412,6 @@
return mContext;
}
-std::shared_ptr<EffectContext> EffectPreProcessing::getContext() {
- return mContext;
-}
-
RetCode EffectPreProcessing::releaseContext() {
if (mContext) {
PreProcessingSession::getPreProcessingSession().releaseSession(mType,
diff --git a/media/libeffects/preprocessing/aidl/EffectPreProcessing.h b/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
index fad848a..9ce5597 100644
--- a/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
+++ b/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
@@ -31,41 +31,51 @@
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- std::shared_ptr<EffectContext> getContext() override;
- RetCode releaseContext() override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int samples)
+ REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
std::string getEffectName() override { return *mEffectName; }
private:
- std::shared_ptr<PreProcessingContext> mContext;
+ std::shared_ptr<PreProcessingContext> mContext GUARDED_BY(mImplMutex);
const Descriptor* mDescriptor;
const std::string* mEffectName;
PreProcessingEffectType mType;
- ndk::ScopedAStatus setParameterAcousticEchoCanceler(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterAcousticEchoCanceler(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterAcousticEchoCanceler(const AcousticEchoCanceler::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
- ndk::ScopedAStatus setParameterAutomaticGainControlV1(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterAutomaticGainControlV1(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterAutomaticGainControlV1(const AutomaticGainControlV1::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
- ndk::ScopedAStatus setParameterAutomaticGainControlV2(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterAutomaticGainControlV2(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterAutomaticGainControlV2(const AutomaticGainControlV2::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
- ndk::ScopedAStatus setParameterNoiseSuppression(const Parameter::Specific& specific);
+ ndk::ScopedAStatus setParameterNoiseSuppression(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex);
ndk::ScopedAStatus getParameterNoiseSuppression(const NoiseSuppression::Id& id,
- Parameter::Specific* specific);
+ Parameter::Specific* specific)
+ REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/preprocessing/aidl/PreProcessingContext.cpp b/media/libeffects/preprocessing/aidl/PreProcessingContext.cpp
index c1e4eda..2c44e5c 100644
--- a/media/libeffects/preprocessing/aidl/PreProcessingContext.cpp
+++ b/media/libeffects/preprocessing/aidl/PreProcessingContext.cpp
@@ -141,6 +141,9 @@
}
RetCode PreProcessingContext::setCommon(const Parameter::Common& common) {
+ if(auto ret = updateIOFrameSize(common); ret != RetCode::SUCCESS) {
+ return ret;
+ }
mCommon = common;
updateConfigs(common);
return RetCode::SUCCESS;
diff --git a/media/libeffects/visualizer/Android.bp b/media/libeffects/visualizer/Android.bp
index cf782f7..66ceadf 100644
--- a/media/libeffects/visualizer/Android.bp
+++ b/media/libeffects/visualizer/Android.bp
@@ -60,8 +60,6 @@
],
defaults: [
"aidlaudioeffectservice_defaults",
- "latest_android_hardware_audio_effect_ndk_shared",
- "latest_android_media_audio_common_types_ndk_shared",
"visualizer_defaults",
],
cflags: [
@@ -72,6 +70,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/media/libeffects/visualizer/aidl/Visualizer.h b/media/libeffects/visualizer/aidl/Visualizer.h
index ec725db..b48c85e 100644
--- a/media/libeffects/visualizer/aidl/Visualizer.h
+++ b/media/libeffects/visualizer/aidl/Visualizer.h
@@ -35,23 +35,25 @@
LOG(DEBUG) << __func__;
}
- ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
- ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
- ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
- Parameter::Specific* specific) override;
- IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
- std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
- RetCode releaseContext() override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
+ REQUIRES(mImplMutex) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific)
+ REQUIRES(mImplMutex) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process)
+ REQUIRES(mImplMutex) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
+ REQUIRES(mImplMutex) override;
+ RetCode releaseContext() REQUIRES(mImplMutex) override;
- std::shared_ptr<EffectContext> getContext() override { return mContext; }
std::string getEffectName() override { return kEffectName; }
private:
static const std::vector<Range::VisualizerRange> kRanges;
- std::shared_ptr<VisualizerContext> mContext;
+ std::shared_ptr<VisualizerContext> mContext GUARDED_BY(mImplMutex);
ndk::ScopedAStatus getParameterVisualizer(const Visualizer::Tag& tag,
- Parameter::Specific* specific);
+ Parameter::Specific* specific) REQUIRES(mImplMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
index e33cc0f..4436fb9 100644
--- a/media/libmedia/CharacterEncodingDetector.cpp
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -198,7 +198,9 @@
ALOGV("@@@ checking %s", name);
const char *s = mValues.getEntry(i);
int32_t inputLength = strlen(s);
- const char *enc;
+ // Use encoding determined from the combination of artist/album/title etc.
+ // as default if there is no better match found.
+ const char *enc = combinedenc;
if (!allprintable && (!strcmp(name, "artist") ||
!strcmp(name, "albumartist") ||
@@ -216,13 +218,12 @@
const UCharsetMatch** ucma = ucsdet_detectAll(csd, &matches, &status);
const UCharsetMatch* bestSingleMatch = getPreferred(s, inputLength,
ucma, matches, &goodmatchSingle, &highestSingle);
- if (goodmatchSingle || highestSingle > highest)
- enc = ucsdet_getName(bestSingleMatch, &status);
- else
- enc = combinedenc;
- } else {
- // use encoding determined from the combination of artist/album/title etc.
- enc = combinedenc;
+ // getPreferred could return a null. Check for null before calling
+ // ucsdet_getName.
+ if (bestSingleMatch != NULL) {
+ if (goodmatchSingle || highestSingle > highest)
+ enc = ucsdet_getName(bestSingleMatch, &status);
+ }
}
} else {
if (isPrintableAscii(s, inputLength)) {
diff --git a/media/libmediaplayerservice/fuzzer/Android.bp b/media/libmediaplayerservice/fuzzer/Android.bp
index b511372..507da29 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -124,14 +124,19 @@
],
defaults: [
"libmediaplayerserviceFuzzer_defaults",
+ "libmediaplayerservice_defaults",
],
static_libs: [
"libplayerservice_datasource",
],
shared_libs: [
+ "libmediaplayerservice",
"libdatasource",
"libdrmframework",
+ "libstagefright_httplive",
+ "libmediaextractorservice",
],
+ include_dirs: ["frameworks/av/services/mediaextractor"],
}
cc_fuzz {
diff --git a/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
index a7cb689..857223d 100644
--- a/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
@@ -15,6 +15,8 @@
*
*/
+#include <MediaExtractorService.h>
+#include <MediaPlayerService.h>
#include <StagefrightMetadataRetriever.h>
#include <binder/ProcessState.h>
#include <datasource/FileSource.h>
@@ -54,58 +56,96 @@
MEDIA_MIMETYPE_CONTAINER_MPEG2PS, MEDIA_MIMETYPE_CONTAINER_HEIF,
MEDIA_MIMETYPE_TEXT_3GPP, MEDIA_MIMETYPE_TEXT_SUBRIP,
MEDIA_MIMETYPE_TEXT_VTT, MEDIA_MIMETYPE_TEXT_CEA_608,
- MEDIA_MIMETYPE_TEXT_CEA_708, MEDIA_MIMETYPE_DATA_TIMED_ID3};
+ MEDIA_MIMETYPE_TEXT_CEA_708, MEDIA_MIMETYPE_DATA_TIMED_ID3,
+ MEDIA_MIMETYPE_IMAGE_AVIF, MEDIA_MIMETYPE_AUDIO_MPEGH_MHA1,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_MHM1, MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L3,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L4, MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L3,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L4, MEDIA_MIMETYPE_AUDIO_DTS,
+ MEDIA_MIMETYPE_AUDIO_DTS_HD, MEDIA_MIMETYPE_AUDIO_DTS_HD_MA,
+ MEDIA_MIMETYPE_AUDIO_DTS_UHD, MEDIA_MIMETYPE_AUDIO_DTS_UHD_P1,
+ MEDIA_MIMETYPE_AUDIO_DTS_UHD_P2, MEDIA_MIMETYPE_AUDIO_EVRC,
+ MEDIA_MIMETYPE_AUDIO_EVRCB, MEDIA_MIMETYPE_AUDIO_EVRCWB,
+ MEDIA_MIMETYPE_AUDIO_EVRCNW, MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS,
+ MEDIA_MIMETYPE_AUDIO_APTX, MEDIA_MIMETYPE_AUDIO_DRA,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT, MEDIA_MIMETYPE_AUDIO_DOLBY_TRUEHD,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_1_0,MEDIA_MIMETYPE_AUDIO_AAC_MP4,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_2_0,MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_2_1,
+ MEDIA_MIMETYPE_AUDIO_AAC_MAIN, MEDIA_MIMETYPE_AUDIO_AAC_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_SSR, MEDIA_MIMETYPE_AUDIO_AAC_LTP,
+ MEDIA_MIMETYPE_AUDIO_AAC_HE_V1, MEDIA_MIMETYPE_AUDIO_AAC_SCALABLE,
+ MEDIA_MIMETYPE_AUDIO_AAC_ERLC, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_MAIN,
+ MEDIA_MIMETYPE_AUDIO_AAC_HE_V2, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V1,
+ MEDIA_MIMETYPE_AUDIO_AAC_XHE, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V2,
+ MEDIA_MIMETYPE_AUDIO_AAC_LD, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SSR, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LTP,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADIF, MEDIA_MIMETYPE_AUDIO_IEC60958,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ERLC,MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LD,
+ MEDIA_MIMETYPE_AUDIO_AAC_ELD, MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V1,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_XHE, MEDIA_MIMETYPE_AUDIO_AAC_LATM_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ELD, MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V2,
+ MEDIA_MIMETYPE_AUDIO_IEC61937,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SCALABLE,};
+
+constexpr size_t kMaxSize = 100;
class MetadataRetrieverFuzzer {
public:
MetadataRetrieverFuzzer(const uint8_t *data, size_t size)
- : mFdp(data, size),
- mMdRetriever(new StagefrightMetadataRetriever()),
- mDataSourceFd(memfd_create("InputFile", MFD_ALLOW_SEALING)) {}
- ~MetadataRetrieverFuzzer() { close(mDataSourceFd); }
+ : mFdp(data, size), mMdRetriever(new StagefrightMetadataRetriever()) {}
bool setDataSource(const uint8_t *data, size_t size);
void getData();
private:
FuzzedDataProvider mFdp;
sp<StagefrightMetadataRetriever> mMdRetriever = nullptr;
- const int32_t mDataSourceFd;
+ int32_t mDataSourceFd;
};
void MetadataRetrieverFuzzer::getData() {
- int64_t timeUs = mFdp.ConsumeIntegral<int64_t>();
- int32_t option = mFdp.ConsumeIntegral<int32_t>();
- int32_t colorFormat = mFdp.ConsumeIntegral<int32_t>();
- bool metaOnly = mFdp.ConsumeBool();
- mMdRetriever->getFrameAtTime(timeUs, option, colorFormat, metaOnly);
-
- int32_t index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- metaOnly = mFdp.ConsumeBool();
- bool thumbnail = mFdp.ConsumeBool();
- mMdRetriever->getImageAtIndex(index, colorFormat, metaOnly, thumbnail);
-
- index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- int32_t left = mFdp.ConsumeIntegral<int32_t>();
- int32_t top = mFdp.ConsumeIntegral<int32_t>();
- int32_t right = mFdp.ConsumeIntegral<int32_t>();
- int32_t bottom = mFdp.ConsumeIntegral<int32_t>();
- mMdRetriever->getImageRectAtIndex(index, colorFormat, left, top, right, bottom);
-
- index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- metaOnly = mFdp.ConsumeBool();
- mMdRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
-
- mMdRetriever->extractAlbumArt();
-
- int32_t keyCode = mFdp.ConsumeIntegral<int32_t>();
- mMdRetriever->extractMetadata(keyCode);
+ while (mFdp.remaining_bytes()) {
+ auto invokeMediaApi = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ mMdRetriever->getFrameAtTime(mFdp.ConsumeIntegral<int64_t>() /* timeUs */,
+ mFdp.ConsumeIntegral<int32_t>() /* option */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */);
+ },
+ [&]() {
+ mMdRetriever->getImageAtIndex(mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */,
+ mFdp.ConsumeBool() /* thumbnail */);
+ },
+ [&]() {
+ mMdRetriever->getImageRectAtIndex(
+ mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeIntegral<int32_t>() /* left */,
+ mFdp.ConsumeIntegral<int32_t>() /* top */,
+ mFdp.ConsumeIntegral<int32_t>() /* right */,
+ mFdp.ConsumeIntegral<int32_t>() /* bottom */);
+ },
+ [&]() {
+ mMdRetriever->getFrameAtIndex(mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */);
+ },
+ [&]() { mMdRetriever->extractAlbumArt(); },
+ [&]() {
+ mMdRetriever->extractMetadata(mFdp.ConsumeIntegral<int32_t>() /* keyCode */);
+ },
+ });
+ invokeMediaApi();
+ }
}
bool MetadataRetrieverFuzzer::setDataSource(const uint8_t *data, size_t size) {
status_t status = -1;
+ std::unique_ptr<std::FILE, decltype(&fclose)> fp(tmpfile(), &fclose);
+ mDataSourceFd = fileno(fp.get());
+ if (mDataSourceFd < 0) {
+ return false;
+ }
enum DataSourceChoice {FromHttp, FromFd, FromFileSource, kMaxValue = FromFileSource};
switch (mFdp.ConsumeEnum<DataSourceChoice>()) {
@@ -114,7 +154,7 @@
mHeaders.add(String8(mFdp.ConsumeRandomLengthString().c_str()),
String8(mFdp.ConsumeRandomLengthString().c_str()));
- uint32_t dataBlobSize = mFdp.ConsumeIntegralInRange<uint16_t>(0, size);
+ uint32_t dataBlobSize = mFdp.ConsumeIntegralInRange<uint16_t>(0, min(kMaxSize,size));
vector<uint8_t> uriSuffix = mFdp.ConsumeBytes<uint8_t>(dataBlobSize);
string uri("data:");
@@ -146,6 +186,12 @@
return true;
}
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+ MediaPlayerService::instantiate();
+ MediaExtractorService::instantiate();
+ return 0;
+}
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
MetadataRetrieverFuzzer mrtFuzzer(data, size);
ProcessState::self()->startThreadPool();
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2145dd9..f9ceef2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2188,7 +2188,7 @@
int32_t colorFormat = OMX_COLOR_FormatUnused;
OMX_U32 flexibleEquivalent = OMX_COLOR_FormatUnused;
if (!outputFormat->findInt32("color-format", &colorFormat)) {
- ALOGE("ouptut port did not have a color format (wrong domain?)");
+ ALOGE("output port did not have a color format (wrong domain?)");
return BAD_VALUE;
}
ALOGD("[%s] Requested output format %#x and got %#x.",
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 0af9d12..896e021 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -137,7 +137,7 @@
cflags: [
"-DDISABLE_AUDIO_SYSTEM_OFFLOAD",
],
- }
+ },
},
}
@@ -273,7 +273,7 @@
"VideoFrameSchedulerBase.cpp",
"VideoFrameScheduler.cpp",
"VideoRenderQualityTracker.cpp",
- ],
+ ],
shared_libs: [
"libstagefright_framecapture_utils",
@@ -315,6 +315,7 @@
"libaudioclient_aidl_conversion",
"packagemanager_aidl-cpp",
"server_configurable_flags",
+ "aconfig_mediacodec_flags_c_lib",
],
static_libs: [
@@ -330,7 +331,7 @@
"libmedia_ndkformatpriv",
],
- header_libs:[
+ header_libs: [
"libmediadrm_headers",
"libnativeloader-headers",
"libstagefright_xmlparser_headers",
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a0a2891..bc1a97c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -172,6 +172,7 @@
const char *getTrackType() const;
void resetInternal();
int64_t trackMetaDataSize();
+ bool isTimestampValid(int64_t timeUs);
private:
// A helper class to handle faster write box with table entries
@@ -2430,6 +2431,42 @@
return OK;
}
+bool MPEG4Writer::isSampleMetadataValid(size_t trackIndex, int64_t timeUs) {
+ // Track Index starts from zero, so it should be at least 1 less than size.
+ if (trackIndex >= mTracks.size()) {
+ ALOGE("Incorrect trackIndex %zu, mTracks->size() %zu", trackIndex, mTracks.size());
+ return false;
+ }
+
+ List<Track *>::iterator it = mTracks.begin();
+
+ // (*it) is already pointing to trackIndex 0.
+ for (int i = 1; i <= trackIndex; i++) {
+ it++;
+ }
+
+ return (*it)->isTimestampValid(timeUs);
+}
+
+bool MPEG4Writer::Track::isTimestampValid(int64_t timeUs) {
+ // No timescale if HEIF
+ if (mIsHeif) {
+ return true;
+ }
+
+ // Ensure that the timeUs value does not have extremely low or high values
+ // that would cause an underflow or overflow, like in the calculation -
+ // mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6
+ if (abs(timeUs) >= (INT64_MAX - 5E5) / mTimeScale) {
+ return false;
+ }
+ // Limit check for calculations in ctts box
+ if (abs(timeUs) + kMaxCttsOffsetTimeUs >= INT64_MAX / mTimeScale) {
+ return false;
+ }
+ return true;
+}
+
bool MPEG4Writer::Track::isExifData(
MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const {
if (!mIsHeif) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index c27cfc5..006eb90 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -273,6 +273,38 @@
// XXX suppress until we get our representation right
static bool kEmitHistogram = false;
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
+// Multi access unit helpers
+static status_t generateFlagsFromAccessUnitInfo(
+ sp<AMessage> &msg, const sp<BufferInfosWrapper> &bufferInfos) {
+ msg->setInt64("timeUs", bufferInfos->value[0].mTimestamp);
+ msg->setInt32("flags", bufferInfos->value[0].mFlags);
+ // will prevent any access-unit info copy.
+ if (bufferInfos->value.size() > 1) {
+ uint32_t bufferFlags = 0;
+ uint32_t flagsInAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODEC_CONFIG;
+ uint32_t andFlags = flagsInAllAU;
+ int infoIdx = 0;
+ bool foundEndOfStream = false;
+ for ( ; infoIdx < bufferInfos->value.size() && !foundEndOfStream; ++infoIdx) {
+ bufferFlags |= bufferInfos->value[infoIdx].mFlags;
+ andFlags &= bufferInfos->value[infoIdx].mFlags;
+ if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+ foundEndOfStream = true;
+ }
+ }
+ bufferFlags = bufferFlags & (andFlags | (~flagsInAllAU));
+ if (infoIdx != bufferInfos->value.size()) {
+ ALOGE("Error: incorrect access-units");
+ return -EINVAL;
+ }
+ msg->setInt32("flags", bufferFlags);
+ msg->setObject("accessUnitInfo", bufferInfos);
+ }
+ return OK;
+}
+
static int64_t getId(IResourceManagerClient const * client) {
return (int64_t) client;
}
@@ -320,8 +352,8 @@
return Status::fromStatus(STATUS_INVALID_OPERATION);
}
ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(this)};
+ .uid = static_cast<int32_t>(mUid),
+ .id = getId(this)};
service->removeClient(clientInfo);
*_aidl_return = true;
return Status::ok();
@@ -398,6 +430,10 @@
mCodecName = name;
}
+ inline void setImportance(int importance) {
+ mImportance = importance;
+ }
+
private:
// To get the binder interface to ResourceManagerService.
void getService() {
@@ -437,12 +473,30 @@
mGetServiceFuture = std::async(std::launch::async, [this] { getService(); });
}
+ /**
+ * Get the ClientInfo to communicate with the ResourceManager.
+ *
+ * ClientInfo includes:
+ * - {pid, uid} of the process
+ * - identifier for the client
+ * - name of the client/codec
+ * - importance associated with the client
+ */
+ inline ClientInfoParcel getClientInfo() const {
+ ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
+ .uid = static_cast<int32_t>(mUid),
+ .id = getId(mClient),
+ .name = mCodecName,
+ .importance = mImportance};
+ return std::move(clientInfo);
+ }
private:
- std::mutex mLock;
- pid_t mPid;
- uid_t mUid;
- bool mBinderDied = false;
+ std::mutex mLock;
+ bool mBinderDied = false;
+ pid_t mPid;
+ uid_t mUid;
+ int mImportance = 0;
std::string mCodecName;
/**
* Reconnecting with the ResourceManagerService, after its binder interface dies,
@@ -550,11 +604,7 @@
std::vector<MediaResourceParcel> resources;
std::copy(mMediaResourceParcel.begin(), mMediaResourceParcel.end(),
std::back_inserter(resources));
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- mService->addResource(clientInfo, mClient, resources);
+ mService->addResource(getClientInfo(), mClient, resources);
}
void MediaCodec::ResourceManagerServiceProxy::BinderDiedCallback(void* cookie) {
@@ -587,11 +637,7 @@
}
std::vector<MediaResourceParcel> resources;
resources.push_back(resource);
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- service->addResource(clientInfo, mClient, resources);
+ service->addResource(getClientInfo(), mClient, resources);
mMediaResourceParcel.emplace(resource);
}
@@ -605,11 +651,7 @@
}
std::vector<MediaResourceParcel> resources;
resources.push_back(resource);
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- service->removeResource(clientInfo, resources);
+ service->removeResource(getClientInfo(), resources);
mMediaResourceParcel.erase(resource);
}
@@ -620,11 +662,7 @@
ALOGW("Service isn't available");
return;
}
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- service->removeClient(clientInfo);
+ service->removeClient(getClientInfo());
mMediaResourceParcel.clear();
}
@@ -635,11 +673,7 @@
ALOGW("Service isn't available");
return;
}
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- service->markClientForPendingRemoval(clientInfo);
+ service->markClientForPendingRemoval(getClientInfo());
mMediaResourceParcel.clear();
}
@@ -652,11 +686,7 @@
return false;
}
bool success;
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- Status status = service->reclaimResource(clientInfo, resources, &success);
+ Status status = service->reclaimResource(getClientInfo(), resources, &success);
return status.isOk() && success;
}
@@ -667,11 +697,7 @@
ALOGW("Service isn't available");
return;
}
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
- .uid = static_cast<int32_t>(mUid),
- .id = getId(mClient),
- .name = mCodecName};
- service->notifyClientCreated(clientInfo);
+ service->notifyClientCreated(getClientInfo());
}
void MediaCodec::ResourceManagerServiceProxy::notifyClientStarted(
@@ -682,10 +708,7 @@
ALOGW("Service isn't available");
return;
}
- clientConfig.clientInfo.pid = static_cast<int32_t>(mPid);
- clientConfig.clientInfo.uid = static_cast<int32_t>(mUid);
- clientConfig.clientInfo.id = getId(mClient);
- clientConfig.clientInfo.name = mCodecName;
+ clientConfig.clientInfo = getClientInfo();
service->notifyClientStarted(clientConfig);
}
@@ -697,10 +720,7 @@
ALOGW("Service isn't available");
return;
}
- clientConfig.clientInfo.pid = static_cast<int32_t>(mPid);
- clientConfig.clientInfo.uid = static_cast<int32_t>(mUid);
- clientConfig.clientInfo.id = getId(mClient);
- clientConfig.clientInfo.name = mCodecName;
+ clientConfig.clientInfo = getClientInfo();
service->notifyClientStopped(clientConfig);
}
@@ -712,10 +732,7 @@
ALOGW("Service isn't available");
return;
}
- clientConfig.clientInfo.pid = static_cast<int32_t>(mPid);
- clientConfig.clientInfo.uid = static_cast<int32_t>(mUid);
- clientConfig.clientInfo.id = getId(mClient);
- clientConfig.clientInfo.name = mCodecName;
+ clientConfig.clientInfo = getClientInfo();
service->notifyClientConfigChanged(clientConfig);
}
@@ -1716,6 +1733,21 @@
}
}
+void MediaCodec::updateCodecImportance(const sp<AMessage>& msg) {
+ // Update the codec importance.
+ int32_t importance = 0;
+ if (msg->findInt32(KEY_IMPORTANCE, &importance)) {
+ // Ignoring the negative importance.
+ if (importance >= 0) {
+ // Notify RM about the change in the importance.
+ mResourceManagerProxy->setImportance(importance);
+ ClientConfigParcel clientConfig;
+ initClientConfigParcel(clientConfig);
+ mResourceManagerProxy->notifyClientConfigChanged(clientConfig);
+ }
+ }
+}
+
constexpr const char *MediaCodec::asString(TunnelPeekState state, const char *default_string){
switch(state) {
case TunnelPeekState::kLegacyMode:
@@ -2224,23 +2256,9 @@
static void mapFormat(AString componentName, const sp<AMessage> &format, const char *kind,
bool reverse);
-status_t MediaCodec::configure(
- const sp<AMessage> &format,
- const sp<Surface> &nativeWindow,
- const sp<ICrypto> &crypto,
- uint32_t flags) {
- return configure(format, nativeWindow, crypto, NULL, flags);
-}
-
-status_t MediaCodec::configure(
- const sp<AMessage> &format,
- const sp<Surface> &surface,
- const sp<ICrypto> &crypto,
- const sp<IDescrambler> &descrambler,
- uint32_t flags) {
-
- sp<AMessage> msg = new AMessage(kWhatConfigure, this);
+mediametrics_handle_t MediaCodec::createMediaMetrics(const sp<AMessage>& format, uint32_t flags) {
mediametrics_handle_t nextMetricsHandle = mediametrics_create(kCodecKeyName);
+ bool isEncoder = (flags & CONFIGURE_FLAG_ENCODE);
// TODO: validity check log-session-id: it should be a 32-hex-digit.
format->findString("log-session-id", &mLogSessionId);
@@ -2255,8 +2273,7 @@
if (format->findInt32("level", &level)) {
mediametrics_setInt32(nextMetricsHandle, kCodecLevel, level);
}
- mediametrics_setInt32(nextMetricsHandle, kCodecEncoder,
- (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
+ mediametrics_setInt32(nextMetricsHandle, kCodecEncoder, isEncoder);
if (!mLogSessionId.empty()) {
mediametrics_setCString(nextMetricsHandle, kCodecLogSessionId, mLogSessionId.c_str());
@@ -2335,7 +2352,7 @@
}
}
- if (flags & CONFIGURE_FLAG_ENCODE) {
+ if (isEncoder) {
int8_t enableShaping = property_get_bool(enableMediaFormatShapingProperty,
enableMediaFormatShapingDefault);
if (!enableShaping) {
@@ -2380,6 +2397,31 @@
updateLowLatency(format);
+ return nextMetricsHandle;
+}
+
+status_t MediaCodec::configure(
+ const sp<AMessage> &format,
+ const sp<Surface> &nativeWindow,
+ const sp<ICrypto> &crypto,
+ uint32_t flags) {
+ return configure(format, nativeWindow, crypto, NULL, flags);
+}
+
+status_t MediaCodec::configure(
+ const sp<AMessage> &format,
+ const sp<Surface> &surface,
+ const sp<ICrypto> &crypto,
+ const sp<IDescrambler> &descrambler,
+ uint32_t flags) {
+
+ // Update the codec importance.
+ updateCodecImportance(format);
+
+ // Create and set up metrics for this codec.
+ mediametrics_handle_t nextMetricsHandle = createMediaMetrics(format, flags);
+
+ sp<AMessage> msg = new AMessage(kWhatConfigure, this);
msg->setMessage("format", format);
msg->setInt32("flags", flags);
msg->setObject("surface", surface);
@@ -3166,7 +3208,49 @@
msg->setInt64("timeUs", presentationTimeUs);
msg->setInt32("flags", flags);
msg->setPointer("errorDetailMsg", errorDetailMsg);
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+status_t MediaCodec::queueInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &infos,
+ AString *errorDetailMsg) {
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+ uint32_t bufferFlags = 0;
+ uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG;
+ uint32_t andFlags = flagsinAllAU;
+ if (infos == nullptr || infos->value.empty()) {
+ ALOGE("ERROR: Large Audio frame with no BufferInfo");
+ return BAD_VALUE;
+ }
+ int infoIdx = 0;
+ std::vector<AccessUnitInfo> &accessUnitInfo = infos->value;
+ int64_t minTimeUs = accessUnitInfo.front().mTimestamp;
+ bool foundEndOfStream = false;
+ for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) {
+ bufferFlags |= accessUnitInfo[infoIdx].mFlags;
+ andFlags &= accessUnitInfo[infoIdx].mFlags;
+ if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+ foundEndOfStream = true;
+ }
+ }
+ bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU));
+ if (infoIdx != accessUnitInfo.size()) {
+ ALOGE("queueInputBuffers has incorrect access-units");
+ return -EINVAL;
+ }
+ msg->setSize("index", index);
+ msg->setSize("offset", offset);
+ msg->setSize("size", size);
+ msg->setInt64("timeUs", minTimeUs);
+ // Make this represent flags for the entire buffer
+ // decodeOnly Flag is set only when all buffers are decodeOnly
+ msg->setInt32("flags", bufferFlags);
+ msg->setObject("accessUnitInfo", infos);
+ msg->setPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
@@ -3210,28 +3294,30 @@
status_t MediaCodec::queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
-
+ if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+ return BAD_VALUE;
+ }
+ status_t err = OK;
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
new WrapperObject<std::shared_ptr<C2Buffer>>{buffer}};
msg->setObject("c2buffer", obj);
- msg->setInt64("timeUs", presentationTimeUs);
- msg->setInt32("flags", flags);
+ if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+ return err;
+ }
if (tunings && tunings->countEntries() > 0) {
msg->setMessage("tunings", tunings);
}
msg->setPointer("errorDetailMsg", errorDetailMsg);
-
sp<AMessage> response;
- status_t err = PostAndAwaitResponse(msg, &response);
+ err = PostAndAwaitResponse(msg, &response);
return err;
}
@@ -3246,14 +3332,16 @@
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern &pattern,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
-
+ if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+ return BAD_VALUE;
+ }
+ status_t err = OK;
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
sp<WrapperObject<sp<hardware::HidlMemory>>> memory{
@@ -3267,15 +3355,16 @@
msg->setInt32("mode", mode);
msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
msg->setInt32("skipBlocks", pattern.mSkipBlocks);
- msg->setInt64("timeUs", presentationTimeUs);
- msg->setInt32("flags", flags);
+ if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+ return err;
+ }
if (tunings && tunings->countEntries() > 0) {
msg->setMessage("tunings", tunings);
}
msg->setPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> response;
- status_t err = PostAndAwaitResponse(msg, &response);
+ err = PostAndAwaitResponse(msg, &response);
return err;
}
@@ -4750,6 +4839,33 @@
}
}
}
+ int32_t largeFrameParam;
+ if (format->findInt32(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, &largeFrameParam) ||
+ format->findInt32(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+ &largeFrameParam)) {
+ if(mComponentName.startsWith("OMX")) {
+ mErrorLog.log(LOG_TAG,
+ "Large Frame params are not supported on OMX codecs."
+ "Currently only supported on C2 audio codec.");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ if (!mime.startsWith("audio")) {
+ mErrorLog.log(LOG_TAG,
+ "Large Frame params only works with audio codec");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ if (!(mFlags & kFlagIsAsync)) {
+ mErrorLog.log(LOG_TAG, "Large Frame audio" \
+ "config works only with async mode");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ }
+
mReplyID = replyID;
setState(CONFIGURING);
@@ -5898,10 +6014,10 @@
status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
size_t index;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
+ size_t offset = 0;
+ size_t size = 0;
+ int64_t timeUs = 0;
+ uint32_t flags = 0;
CHECK(msg->findSize("index", &index));
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -5983,9 +6099,13 @@
"client does not own the buffer #%zu", index));
return -EACCES;
}
- auto setInputBufferParams = [this, &buffer]
+ auto setInputBufferParams = [this, &msg, &buffer]
(int64_t timeUs, uint32_t flags = 0) -> status_t {
status_t err = OK;
+ sp<RefBase> obj;
+ if (msg->findObject("accessUnitInfo", &obj)) {
+ buffer->meta()->setObject("accessUnitInfo", obj);
+ }
buffer->meta()->setInt64("timeUs", timeUs);
if (flags & BUFFER_FLAG_EOS) {
buffer->meta()->setInt32("eos", true);
@@ -6495,10 +6615,11 @@
if (discardDecodeOnlyOutputBuffer(index)) {
continue;
}
+ sp<AMessage> msg = mCallback->dup();
const sp<MediaCodecBuffer> &buffer =
mPortBuffers[kPortIndexOutput][index].mData;
- sp<AMessage> msg = mCallback->dup();
- msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
+ int32_t outputCallbackID = CB_OUTPUT_AVAILABLE;
+ sp<RefBase> accessUnitInfoObj;
msg->setInt32("index", index);
msg->setSize("offset", buffer->offset());
msg->setSize("size", buffer->size());
@@ -6512,6 +6633,15 @@
CHECK(buffer->meta()->findInt32("flags", &flags));
msg->setInt32("flags", flags);
+ buffer->meta()->findObject("accessUnitInfo", &accessUnitInfoObj);
+ if (accessUnitInfoObj) {
+ outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE;
+ msg->setObject("accessUnitInfo", accessUnitInfoObj);
+ sp<BufferInfosWrapper> auInfo(
+ (decltype(auInfo.get()))accessUnitInfoObj.get());
+ auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM;
+ }
+ msg->setInt32("callbackID", outputCallbackID);
statsBufferReceived(timeUs, buffer);
@@ -6591,6 +6721,7 @@
return NO_INIT;
}
updateLowLatency(params);
+ updateCodecImportance(params);
mapFormat(mComponentName, params, nullptr, false);
updateTunnelPeek(params);
mCodec->signalSetParameters(params);
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
index f16e635..604dcb0 100644
--- a/media/libstagefright/SurfaceUtils.cpp
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -298,6 +298,11 @@
ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err);
break;
}
+ if (img == nullptr) {
+ (void)buf->unlock(); // Since lock() was successful.
+ ALOGE("error pushing blank frames: lock succeeded: buf mapping is nullptr");
+ break;
+ }
*img = 0;
diff --git a/media/libstagefright/colorconversion/fuzzer/Android.bp b/media/libstagefright/colorconversion/fuzzer/Android.bp
index 76b054a..237e715 100644
--- a/media/libstagefright/colorconversion/fuzzer/Android.bp
+++ b/media/libstagefright/colorconversion/fuzzer/Android.bp
@@ -47,9 +47,15 @@
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "android-fwk-video@google.com",
],
- componentid: 155276,
+ componentid: 42195,
+ hotlists: ["4593311"],
+ description: "The fuzzer targets the APIs of libstagefright_color_conversion",
+ vector: "local_no_privileges_required",
+ service_privilege: "constrained",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index 509f7a9..0d9e0ec 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -66,7 +66,7 @@
</MediaCodec>
<MediaCodec name="c2.android.raw.decoder" type="audio/raw">
<Alias name="OMX.google.raw.decoder" />
- <Limit name="channel-count" max="8" />
+ <Limit name="channel-count" max="12" />
<Limit name="sample-rate" ranges="8000-192000" />
<Limit name="bitrate" range="1-10000000" />
</MediaCodec>
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index ee41867..24020d1 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -80,7 +80,7 @@
</MediaCodec>
<MediaCodec name="c2.android.raw.decoder" type="audio/raw">
<Alias name="OMX.google.raw.decoder" />
- <Limit name="channel-count" max="8" />
+ <Limit name="channel-count" max="12" />
<Limit name="sample-rate" ranges="8000-192000" />
<Limit name="bitrate" range="1-10000000" />
<Attribute name="software-codec" />
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 0927653..8741daa 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -61,6 +61,16 @@
using hardware::cas::native::V1_0::IDescrambler;
+struct AccessUnitInfo {
+ uint32_t mFlags;
+ uint32_t mSize;
+ int64_t mTimestamp;
+ AccessUnitInfo(uint32_t flags, uint32_t size, int64_t ptsUs)
+ :mFlags(flags), mSize(size), mTimestamp(ptsUs) {
+ }
+ ~AccessUnitInfo() {}
+};
+
struct CodecParameterDescriptor {
std::string name;
AMessage::Type type;
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 054a4b8..1ff8acf 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -77,6 +77,9 @@
virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
virtual status_t setNextFd(int fd);
+ // Returns true if the timestamp is valid which is compatible with the Mpeg4.
+ // Note that this overloads that method in the base class.
+ bool isSampleMetadataValid(size_t trackIndex, int64_t timeUs) override;
protected:
virtual ~MPEG4Writer();
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index baa5b7e..6622f4f 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -28,6 +28,7 @@
#include <media/MediaMetrics.h>
#include <media/MediaProfiles.h>
#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/CodecErrorLog.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <media/stagefright/MediaHistogram.h>
@@ -56,6 +57,7 @@
struct AString;
struct BatteryChecker;
class BufferChannelBase;
+struct AccessUnitInfo;
struct CodecBase;
struct CodecParameterDescriptor;
class IBatteryStats;
@@ -78,6 +80,8 @@
using aidl::android::media::MediaResourceParcel;
using aidl::android::media::ClientConfigParcel;
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
struct MediaCodec : public AHandler {
enum Domain {
DOMAIN_UNKNOWN = 0,
@@ -115,6 +119,7 @@
CB_OUTPUT_FORMAT_CHANGED = 4,
CB_RESOURCE_RECLAIMED = 5,
CB_CRYPTO_ERROR = 6,
+ CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7,
};
static const pid_t kNoPid = -1;
@@ -185,6 +190,13 @@
uint32_t flags,
AString *errorDetailMsg = NULL);
+ status_t queueInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &accessUnitInfo,
+ AString *errorDetailMsg = NULL);
+
status_t queueSecureInputBuffer(
size_t index,
size_t offset,
@@ -201,8 +213,7 @@
status_t queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg = NULL);
@@ -216,8 +227,7 @@
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern &pattern,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg = NULL);
@@ -320,6 +330,9 @@
status_t reclaim(bool force = false);
friend struct ResourceManagerClient;
+ // to create the metrics associated with this codec.
+ mediametrics_handle_t createMediaMetrics(const sp<AMessage>& format, uint32_t flags);
+
private:
enum State {
UNINITIALIZED,
@@ -462,6 +475,7 @@
void resetMetricsFields();
void updateEphemeralMediametrics(mediametrics_handle_t item);
void updateLowLatency(const sp<AMessage> &msg);
+ void updateCodecImportance(const sp<AMessage>& msg);
void onGetMetrics(const sp<AMessage>& msg);
constexpr const char *asString(TunnelPeekState state, const char *default_string="?");
void updateTunnelPeek(const sp<AMessage> &msg);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 7334639..f4c40e1 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -794,6 +794,7 @@
inline constexpr char KEY_HDR10_PLUS_INFO[] = "hdr10-plus-info";
inline constexpr char KEY_HEIGHT[] = "height";
inline constexpr char KEY_I_FRAME_INTERVAL[] = "i-frame-interval";
+inline constexpr char KEY_IMPORTANCE[] = "importance";
inline constexpr char KEY_INTRA_REFRESH_PERIOD[] = "intra-refresh-period";
inline constexpr char KEY_IS_ADTS[] = "is-adts";
inline constexpr char KEY_IS_AUTOSELECT[] = "is-autoselect";
@@ -809,6 +810,9 @@
inline constexpr char KEY_MAX_FPS_TO_ENCODER[] = "max-fps-to-encoder";
inline constexpr char KEY_MAX_HEIGHT[] = "max-height";
inline constexpr char KEY_MAX_INPUT_SIZE[] = "max-input-size";
+inline constexpr char KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE[] = "buffer-batch-max-output-size";
+inline constexpr char KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE[] =
+ "buffer-batch-threshold-output-size";
inline constexpr char KEY_MAX_OUTPUT_CHANNEL_COUNT[] = "max-output-channel-count";
inline constexpr char KEY_MAX_PTS_GAP_TO_ENCODER[] = "max-pts-gap-to-encoder";
inline constexpr char KEY_MAX_WIDTH[] = "max-width";
diff --git a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
index 0fb5690..66f0bb1 100644
--- a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
+++ b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
@@ -59,6 +59,12 @@
int32_t mReadHead;
int32_t mCapacity;
char* mCutBuffer;
+
+ /*
+ * Added to use Access unit skip cut in Codec2 framework
+ */
+ friend class MultiAccessUnitSkipCutBuffer;
+
DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer);
};
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 54c5697..79ab009 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -184,6 +184,9 @@
cc_defaults {
name: "libstagefright_softomx-defaults",
+ // TODO (b/316432618) Software OMX codecs are no longer used, disable building them till
+ // this code is removed completely.
+ enabled: false,
vendor_available: true,
cflags: [
diff --git a/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp b/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
index 65d537b..f95fc4d 100644
--- a/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
+++ b/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
@@ -148,6 +148,11 @@
return C2_TRANSACTION_FAILED;
}
}
+ status = static_cast<c2_status_t>(configResult.status.status);
+ if (status != C2_BAD_INDEX) {
+ LOG(DEBUG) << "config -- call failed: "
+ << status << ".";
+ }
size_t i = failures->size();
failures->resize(i + configResult.failures.size());
for (const c2_aidl::SettingResult& sf : configResult.failures) {
@@ -320,8 +325,8 @@
heapParams->reserve(heapParams->size() + numIndices);
}
c2_status_t status = C2_OK;
- c2_aidl::Params aidlParams;
- ScopedAStatus transResult = mAidlConfigurable->query(indices, true, &aidlParams);
+ c2_aidl::IConfigurable::QueryResult aidlResult;
+ ScopedAStatus transResult = mAidlConfigurable->query(indices, true, &aidlResult);
if (!transResult.isOk()) {
if (transResult.getExceptionCode() == EX_SERVICE_SPECIFIC) {
status = c2_status_t(transResult.getServiceSpecificError());
@@ -332,8 +337,12 @@
return C2_TRANSACTION_FAILED;
}
}
+ status = static_cast<c2_status_t>(aidlResult.status.status);
+ if (status != C2_OK) {
+ LOG(DEBUG) << "query -- call failed: " << status << ".";
+ }
std::vector<C2Param*> paramPointers;
- if (!c2_aidl::utils::ParseParamsBlob(¶mPointers, aidlParams)) {
+ if (!c2_aidl::utils::ParseParamsBlob(¶mPointers, aidlResult.params)) {
LOG(ERROR) << "query -- error while parsing params.";
return C2_CORRUPTED;
}
@@ -488,9 +497,9 @@
}
c2_status_t status = C2_OK;
- std::vector<c2_aidl::FieldSupportedValuesQueryResult> queryResults;
+ c2_aidl::IConfigurable::QuerySupportedValuesResult queryResult;
ScopedAStatus transResult = mAidlConfigurable->querySupportedValues(
- aidlFields, true, &queryResults);
+ aidlFields, true, &queryResult);
if (!transResult.isOk()) {
if (transResult.getExceptionCode() == EX_SERVICE_SPECIFIC) {
status = c2_status_t(transResult.getServiceSpecificError());
@@ -502,14 +511,19 @@
return C2_TRANSACTION_FAILED;
}
}
- if (queryResults.size() != fields.size()) {
+ status = static_cast<c2_status_t>(queryResult.status.status);
+ if (status != C2_OK) {
+ LOG(DEBUG) << "querySupportedValues -- call failed: "
+ << status << ".";
+ }
+ if (queryResult.values.size() != fields.size()) {
LOG(ERROR) << "querySupportedValues -- "
"input and output lists "
"have different sizes.";
return C2_CORRUPTED;
}
for (size_t i = 0; i < fields.size(); ++i) {
- if (!c2_aidl::utils::FromAidl(&fields[i], aidlFields[i], queryResults[i])) {
+ if (!c2_aidl::utils::FromAidl(&fields[i], aidlFields[i], queryResult.values[i])) {
LOG(ERROR) << "querySupportedValues -- "
"invalid returned value.";
return C2_CORRUPTED;
@@ -803,6 +817,7 @@
}
}
+ bool registered = false;
if (platformVersion >= __ANDROID_API_V__) {
if (!aidlStore) {
aidlStore = ::ndk::SharedRefBase::make<c2_aidl::utils::ComponentStore>(
@@ -812,23 +827,27 @@
std::string(c2_aidl::IComponentStore::descriptor) + "/software";
binder_exception_t ex = AServiceManager_addService(
aidlStore->asBinder().get(), serviceName.c_str());
- if (ex != EX_NONE) {
+ if (ex == EX_NONE) {
+ registered = true;
+ } else {
LOG(ERROR) << "Cannot register software Codec2 AIDL service.";
- return;
}
}
if (!hidlStore) {
- hidlStore = ::android::sp<V1_0::utils::ComponentStore>::make(
+ hidlStore = ::android::sp<V1_2::utils::ComponentStore>::make(
std::make_shared<H2C2ComponentStore>(nullptr));
- hidlVer = "1.0";
+ hidlVer = "1.2";
}
- if (hidlStore->registerAsService("software") != android::OK) {
+ if (hidlStore->registerAsService("software") == android::OK) {
+ registered = true;
+ } else {
LOG(ERROR) << "Cannot register software Codec2 v" << hidlVer << " service.";
- return;
}
- LOG(INFO) << "Software Codec2 service created and registered.";
+ if (registered) {
+ LOG(INFO) << "Software Codec2 service created and registered.";
+ }
ABinderProcess_joinThreadPool();
::android::hardware::joinRpcThreadpool();
diff --git a/media/module/extractors/fuzzers/Android.bp b/media/module/extractors/fuzzers/Android.bp
index 0a8d2ab..d096d63 100644
--- a/media/module/extractors/fuzzers/Android.bp
+++ b/media/module/extractors/fuzzers/Android.bp
@@ -189,6 +189,8 @@
],
dictionary: "mkv_extractor_fuzzer.dict",
+
+ corpus: ["corpus/*"],
}
cc_fuzz {
diff --git a/media/module/extractors/fuzzers/corpus/103c24dec0f5da3638a771e451cecfe38339b29c b/media/module/extractors/fuzzers/corpus/103c24dec0f5da3638a771e451cecfe38339b29c
new file mode 100755
index 0000000..8b31683
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/103c24dec0f5da3638a771e451cecfe38339b29c
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/2e532f8eb60e1c757f4399377e8f563a3cf3abf2e b/media/module/extractors/fuzzers/corpus/2e532f8eb60e1c757f4399377e8f563a3cf3abf2e
new file mode 100755
index 0000000..f4d475d
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/2e532f8eb60e1c757f4399377e8f563a3cf3abf2e
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/45e6a4014883a7e1f1200e2a53eabb4f0109aec3 b/media/module/extractors/fuzzers/corpus/45e6a4014883a7e1f1200e2a53eabb4f0109aec3
new file mode 100755
index 0000000..8438e66
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/45e6a4014883a7e1f1200e2a53eabb4f0109aec3
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/465b39984b71d8b4c2b80072993fb7ec73b4af69 b/media/module/extractors/fuzzers/corpus/465b39984b71d8b4c2b80072993fb7ec73b4af69
new file mode 100755
index 0000000..2f622cd
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/465b39984b71d8b4c2b80072993fb7ec73b4af69
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/4ef9546eab199719aadead2d26d3c1d72f42e600 b/media/module/extractors/fuzzers/corpus/4ef9546eab199719aadead2d26d3c1d72f42e600
new file mode 100755
index 0000000..f053c01
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/4ef9546eab199719aadead2d26d3c1d72f42e600
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/6b35d5a3af88baf240293ff1b8adf0b774055e65 b/media/module/extractors/fuzzers/corpus/6b35d5a3af88baf240293ff1b8adf0b774055e65
new file mode 100755
index 0000000..872451c
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/6b35d5a3af88baf240293ff1b8adf0b774055e65
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/7e798d68a1bf154e079227ee43e69ea27844b7e8 b/media/module/extractors/fuzzers/corpus/7e798d68a1bf154e079227ee43e69ea27844b7e8
new file mode 100755
index 0000000..2f7e3ea
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/7e798d68a1bf154e079227ee43e69ea27844b7e8
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/d2fd225343c99872f5a825f7f06b8c1dac0e8687 b/media/module/extractors/fuzzers/corpus/d2fd225343c99872f5a825f7f06b8c1dac0e8687
new file mode 100755
index 0000000..10b5f9a
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/d2fd225343c99872f5a825f7f06b8c1dac0e8687
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/d5602e69abf068ed8f1277e412149b8664d06620 b/media/module/extractors/fuzzers/corpus/d5602e69abf068ed8f1277e412149b8664d06620
new file mode 100755
index 0000000..969b7a2
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/d5602e69abf068ed8f1277e412149b8664d06620
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/e73fa90346e7287b7e923b0ebf07ce8988d94498 b/media/module/extractors/fuzzers/corpus/e73fa90346e7287b7e923b0ebf07ce8988d94498
new file mode 100755
index 0000000..bd146b1
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/e73fa90346e7287b7e923b0ebf07ce8988d94498
Binary files differ
diff --git a/media/module/extractors/fuzzers/corpus/f1ee160337d3a467402a2217897477f0fab15da b/media/module/extractors/fuzzers/corpus/f1ee160337d3a467402a2217897477f0fab15da
new file mode 100755
index 0000000..7cf844e
--- /dev/null
+++ b/media/module/extractors/fuzzers/corpus/f1ee160337d3a467402a2217897477f0fab15da
Binary files differ
diff --git a/media/module/extractors/mp4/ItemTable.cpp b/media/module/extractors/mp4/ItemTable.cpp
index 7fe5ba7..06da18d 100644
--- a/media/module/extractors/mp4/ItemTable.cpp
+++ b/media/module/extractors/mp4/ItemTable.cpp
@@ -1452,9 +1452,9 @@
info.isExif(), (long long)offset, (long long)size);
if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
ExternalMetaItem metaItem = {
- .isExif = info.isExif(),
.offset = offset,
.size = size,
+ .isExif = info.isExif(),
};
mItemIdToMetaMap.add(info.itemId, metaItem);
}
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index a9ca078..4b0dfe8 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -522,7 +522,7 @@
return AMEDIA_ERROR_UNKNOWN;
}
- [=] {
+ [this, &track] {
int64_t duration;
int32_t samplerate;
// Only for audio track.
diff --git a/media/module/extractors/wav/WAVExtractor.cpp b/media/module/extractors/wav/WAVExtractor.cpp
index 9c3bac6..d278103 100644
--- a/media/module/extractors/wav/WAVExtractor.cpp
+++ b/media/module/extractors/wav/WAVExtractor.cpp
@@ -219,7 +219,7 @@
mNumChannels = U16_LE_AT(&formatSpec[2]);
- if (mNumChannels < 1 || mNumChannels > FCC_8) {
+ if (mNumChannels < 1 || mNumChannels > FCC_12) {
ALOGE("Unsupported number of channels (%d)", mNumChannels);
return AMEDIA_ERROR_UNSUPPORTED;
}
diff --git a/media/module/foundation/include/media/stagefright/foundation/AMessage.h b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
index 7594565..b301c53 100644
--- a/media/module/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
@@ -356,6 +356,16 @@
DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};
+/*
+ * Helper struct for wrapping any object with RefBase.
+ */
+template <typename T>
+struct WrapperObject : public RefBase {
+ WrapperObject(const T& v) : value(v) {}
+ WrapperObject(T&& v) : value(std::move(v)) {}
+ T value;
+};
+
} // namespace android
#endif // A_MESSAGE_H_
diff --git a/media/module/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml b/media/module/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
index c3a0ced..f8906dc 100644
--- a/media/module/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
+++ b/media/module/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Unit test configuration for {MODULE}">
<option name="test-suite-tag" value="TranscoderTests" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="false" />
<option name="push-file" key="TranscodingTestAssets" value="/data/local/tmp/TranscodingTestAssets" />
diff --git a/media/module/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/module/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
index 88c3fd3..fed8fc9 100644
--- a/media/module/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
+++ b/media/module/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -20,6 +20,7 @@
#define LOG_TAG "VideoTrackTranscoderTests"
#include <android-base/logging.h>
+#include <android/binder_process.h>
#include <fcntl.h>
#include <gtest/gtest.h>
#include <media/MediaSampleReaderNDK.h>
@@ -221,5 +222,6 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockHandle.h b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
index 111485c..aa632a4 100644
--- a/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
+++ b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
@@ -45,18 +45,18 @@
pkt.size());
// packet is bigger than what the caller can handle,
- if (pkt.size() > len) {
+ if (pkt.size() - mPacketOffset > len) {
memcpy(data, pkt.data() + mPacketOffset, len);
mPacketOffset += len;
readAmt = len;
// packet is equal or smaller than the caller buffer
} else {
- memcpy(data, pkt.data() + mPacketOffset, pkt.size());
+ memcpy(data, pkt.data() + mPacketOffset, pkt.size() - mPacketOffset);
mPacketNumber++;
mPacketOffset = 0;
- readAmt = pkt.size();
+ readAmt = pkt.size() - mPacketOffset;
}
return readAmt;
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 8b9dde3..9ec7700 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -208,19 +208,21 @@
}
cc_test {
- name: "AImageReaderWindowHandleTest",
+ name: "AImageReaderWindowTest",
test_suites: ["device-tests"],
- srcs: ["tests/AImageReaderWindowHandleTest.cpp"],
+ srcs: ["tests/AImageReaderWindowTest.cpp"],
shared_libs: [
"libbinder",
"libmediandk",
"libmediautils",
"libnativewindow",
"libgui",
+ "libhidlbase",
"libutils",
"libui",
"libcutils",
"android.hardware.graphics.bufferqueue@1.0",
+ "android.hidl.token@1.0",
],
header_libs: [
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 161b5e3..a26681e 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -428,6 +428,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
EXPORT const char* AMEDIAFORMAT_KEY_ICC_PROFILE = "icc-profile";
+EXPORT const char* AMEDIAFORMAT_KEY_IMPORTANCE = "importance";
EXPORT const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
@@ -449,6 +450,10 @@
EXPORT const char* AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
+EXPORT const char* AMEDIAFORMAT_KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE =
+ "buffer-batch-max-output-size";
+EXPORT const char* AMEDIAFORMAT_KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE =
+ "buffer-batch-threshold-output-size";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER = "max-pts-gap-to-encoder";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
diff --git a/media/ndk/TEST_MAPPING b/media/ndk/TEST_MAPPING
index e420812..1a15728 100644
--- a/media/ndk/TEST_MAPPING
+++ b/media/ndk/TEST_MAPPING
@@ -1,7 +1,7 @@
// mappings for frameworks/av/media/ndk
{
"presubmit": [
- { "name": "AImageReaderWindowHandleTest" },
+ { "name": "AImageReaderWindowTest" },
{ "name": "libmediandk_test" }
]
}
diff --git a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
index 2b22f0f..fcb0520 100644
--- a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
+++ b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
@@ -16,6 +16,9 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <media/NdkMediaCrypto.h>
+#include <functional>
+
+#include <functional>
constexpr size_t kMaxString = 256;
constexpr size_t kMinBytes = 0;
diff --git a/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp b/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp
index 6c11798..6450742 100644
--- a/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp
+++ b/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp
@@ -18,6 +18,7 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <gui/BufferQueue.h>
#include <media/NdkImageReader.h>
+#include <functional>
constexpr int32_t kMaxSize = INT_MAX;
constexpr int32_t kMinSize = 1;
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index b722b74..4fc9918 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -534,7 +534,8 @@
* Get the native_handle_t corresponding to the ANativeWindow owned by the
* AImageReader provided.
*
- * This is deprecated in API level 35 and will return AMEDIA_ERROR_UNKNOWN.
+ * This is deprecated on devices with vendor API level greater than 34 and
+ * will return AMEDIA_ERROR_UNKNOWN on those devices.
* The native_handle_t is no longer used with AIDL interfaces and
* ANativeWindow is used directly instead.
* Use AImageRead_getWindow to get the ANativeWindow and use that object.
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index b2cdf8d..cc1dd9f 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -168,6 +168,7 @@
extern const char* AMEDIAFORMAT_KEY_GRID_ROWS __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21);
+extern const char* AMEDIAFORMAT_KEY_IMPORTANCE __INTRODUCED_IN(35);
extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_IS_ADTS __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT __INTRODUCED_IN(21);
@@ -186,6 +187,8 @@
extern const char* AMEDIAFORMAT_KEY_MAX_B_FRAMES __INTRODUCED_IN(34);
extern const char* AMEDIAFORMAT_KEY_MAX_HEIGHT __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE __INTRODUCED_IN(21);
+extern const char* AMEDIAFORMAT_KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE __INTRODUCED_IN(35);
+extern const char* AMEDIAFORMAT_KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE __INTRODUCED_IN(35);
extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_MIME __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA __INTRODUCED_IN(28);
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 4f045fd..262c169 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -185,11 +185,11 @@
AMediaCodecCryptoInfo_setPattern; # introduced=24
AMediaCodec_configure;
AMediaCodec_createCodecByName;
- AMediaCodec_createCodecByNameForClient; # systemapi # introduced=31
+ AMediaCodec_createCodecByNameForClient; # systemapi introduced=31
AMediaCodec_createDecoderByType;
- AMediaCodec_createDecoderByTypeForClient; # systemapi # introduced=31
+ AMediaCodec_createDecoderByTypeForClient; # systemapi introduced=31
AMediaCodec_createEncoderByType;
- AMediaCodec_createEncoderByTypeForClient; # systemapi # introduced=31
+ AMediaCodec_createEncoderByTypeForClient; # systemapi introduced=31
AMediaCodec_delete;
AMediaCodec_dequeueInputBuffer;
AMediaCodec_dequeueOutputBuffer;
diff --git a/media/ndk/tests/AImageReaderWindowHandleTest.cpp b/media/ndk/tests/AImageReaderWindowHandleTest.cpp
deleted file mode 100644
index 27864c2..0000000
--- a/media/ndk/tests/AImageReaderWindowHandleTest.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <media/NdkImageReader.h>
-#include <media/NdkImage.h>
-#include <mediautils/AImageReaderUtils.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
-#include <NdkImagePriv.h>
-#include <NdkImageReaderPriv.h>
-#include <vndk/hardware_buffer.h>
-#include <memory>
-
-namespace android {
-
-using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::
- IGraphicBufferProducer;
-using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
-using aimg::AImageReader_getHGBPFromHandle;
-
-typedef IGraphicBufferProducer::QueueBufferInput QueueBufferInput;
-typedef IGraphicBufferProducer::QueueBufferOutput QueueBufferOutput;
-
-static constexpr uint64_t kImageBufferUsage =
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
-static constexpr int kImageWidth = 640;
-static constexpr int kImageHeight = 480;
-static constexpr int kImageFormat = AIMAGE_FORMAT_RGBA_8888;
-static constexpr int kMaxImages = 1;
-
-static constexpr int64_t kQueueBufferInputTimeStamp = 1384888611;
-static constexpr bool kQueueBufferInputIsAutoTimeStamp = false;
-static constexpr android_dataspace kQueueBufferInputDataspace = HAL_DATASPACE_UNKNOWN;
-static const Rect kQueueBufferInputRect = Rect(kImageWidth, kImageHeight);
-static constexpr int kQueueBufferInputScalingMode = 0;
-static constexpr int kQueueBufferInputTransform = 0;
-static const sp<Fence> kQueueBufferInputFence = Fence::NO_FENCE;
-
-static constexpr int kOnImageAvailableWaitUs = 100 * 1000;
-
-class AImageReaderWindowHandleTest : public ::testing::Test {
- public:
- void SetUp() override {
- AImageReader_newWithUsage(kImageWidth, kImageHeight, kImageFormat,
- kImageBufferUsage , kMaxImages, &imageReader_);
- media_status_t ret = AMEDIA_ERROR_UNKNOWN;
- ASSERT_NE(imageReader_, nullptr);
- ret = AImageReader_setImageListener(imageReader_,
- &imageReaderAvailableCb_);
- ASSERT_EQ(ret, AMEDIA_OK);
- ret = AImageReader_setBufferRemovedListener(imageReader_,
- &imageReaderDetachedCb_);
- ASSERT_EQ(ret, AMEDIA_OK);
- }
- void TearDown() override {
- if (imageReader_) {
- AImageReader_delete(imageReader_);
- }
- }
-
- void HandleImageAvailable() {
- AImage *outImage = nullptr;
- media_status_t ret = AMEDIA_OK;
- auto imageDeleter = [](AImage *img) { AImage_delete(img); };
- std::unique_ptr<AImage, decltype(imageDeleter)> img(nullptr, imageDeleter);
-
- // Test that the image can be acquired.
- ret = AImageReader_acquireNextImage(imageReader_, &outImage);
- ASSERT_EQ(ret, AMEDIA_OK);
- img.reset(outImage);
- ASSERT_NE(img, nullptr);
-
- // Test that we can get a handle to the image's hardware buffer and a
- // native handle to it.
- AHardwareBuffer *hardwareBuffer = nullptr;
- ret = AImage_getHardwareBuffer(img.get(), &hardwareBuffer);
- ASSERT_EQ(ret, AMEDIA_OK);
- ASSERT_NE(hardwareBuffer, nullptr);
- const native_handle_t *nh = AHardwareBuffer_getNativeHandle(hardwareBuffer);
- ASSERT_NE(nh, nullptr);
- std::unique_lock<std::mutex> lock(imageAvailableMutex_);
- imageAvailable_ = true;
- imageCondVar_.notify_one();
- }
-
- static void onImageAvailable(void *context, AImageReader *reader) {
- (void)reader;
- AImageReaderWindowHandleTest *thisContext =
- reinterpret_cast<AImageReaderWindowHandleTest *>(context);
- thisContext->HandleImageAvailable();
- }
-
- static void onBufferRemoved(void *, AImageReader *, AHardwareBuffer *) {
- }
-
- AImageReader *imageReader_ = nullptr;
- AImageReader_ImageListener imageReaderAvailableCb_{this, onImageAvailable};
- AImageReader_BufferRemovedListener imageReaderDetachedCb_{this, onBufferRemoved};
- std::mutex imageAvailableMutex_;
- std::condition_variable imageCondVar_;
- bool imageAvailable_ = false;
-};
-
-static void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
- const size_t PIXEL_SIZE = 4;
- for (int x = 0; x < w; x++) {
- for (int y = 0; y < h; y++) {
- off_t offset = (y * stride + x) * PIXEL_SIZE;
- for (int c = 0; c < 4; c++) {
- int parityX = (x / (1 << (c+2))) & 1;
- int parityY = (y / (1 << (c+2))) & 1;
- buf[offset + c] = (parityX ^ parityY) ? 231 : 35;
- }
- }
- }
-}
-
-TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) {
- // Check that we can create a native_handle_t corresponding to the
- // AImageReader.
- native_handle_t *nh = nullptr;
- AImageReader_getWindowNativeHandle(imageReader_, &nh);
- ASSERT_NE(nh, nullptr);
-
- // Check that there are only ints in the handle.
- ASSERT_EQ(nh->numFds, 0);
- ASSERT_NE(nh->numInts, 0);
-
- // Check that the HGBP can be retrieved from the handle.
- sp<HGraphicBufferProducer> hgbp = AImageReader_getHGBPFromHandle(nh);
- ASSERT_NE(hgbp, nullptr);
- sp<IGraphicBufferProducer> igbp = new H2BGraphicBufferProducer(hgbp);
- int dequeuedSlot = -1;
- sp<Fence> dequeuedFence;
- IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, igbp->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
-
- // Test that we can dequeue a buffer.
- ASSERT_EQ(OK,
- ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
- (igbp->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
- kImageWidth, kImageHeight,
- kImageFormat, kImageBufferUsage,
- nullptr, nullptr)));
- EXPECT_LE(0, dequeuedSlot);
- EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
-
- sp<GraphicBuffer> dequeuedBuffer;
- igbp->requestBuffer(dequeuedSlot, &dequeuedBuffer);
- uint8_t* img = nullptr;
- ASSERT_EQ(NO_ERROR, dequeuedBuffer->lock(kImageBufferUsage, (void**)(&img)));
-
- // Write in some dummy image data.
- fillRGBA8Buffer(img, dequeuedBuffer->getWidth(), dequeuedBuffer->getHeight(),
- dequeuedBuffer->getStride());
- ASSERT_EQ(NO_ERROR, dequeuedBuffer->unlock());
- QueueBufferInput queueBufferInput(kQueueBufferInputTimeStamp,
- kQueueBufferInputIsAutoTimeStamp,
- kQueueBufferInputDataspace,
- kQueueBufferInputRect,
- kQueueBufferInputScalingMode,
- kQueueBufferInputTransform,
- kQueueBufferInputFence);
- QueueBufferOutput queueBufferOutput;
- ASSERT_EQ(OK, igbp->queueBuffer(dequeuedSlot, queueBufferInput,
- &queueBufferOutput));
- // wait until the onImageAvailable callback is called, or timeout completes.
- std::unique_lock<std::mutex> lock(imageAvailableMutex_);
- imageCondVar_.wait_for(lock, std::chrono::microseconds(kOnImageAvailableWaitUs),
- [this]{ return this->imageAvailable_;});
- EXPECT_TRUE(imageAvailable_) << "Timed out waiting for image data to be handled!\n";
-}
-
-class AImageReaderPrivateFormatTest : public ::testing::Test {
- public:
- void SetUp() override {
- auto status = AImageReader_new(kImageWidth, kImageHeight, AIMAGE_FORMAT_RAW_DEPTH,
- kMaxImages, &imgReader);
- EXPECT_TRUE(status == AMEDIA_OK);
- }
-
- void TearDown() override {
- if (imgReader) {
- AImageReader_delete(imgReader);
- }
- }
- AImageReader *imgReader = nullptr;
-};
-
-TEST_F(AImageReaderPrivateFormatTest, CreateTest) {
- EXPECT_TRUE(imgReader != nullptr);
-}
-
-
-} // namespace android
diff --git a/media/ndk/tests/AImageReaderWindowTest.cpp b/media/ndk/tests/AImageReaderWindowTest.cpp
new file mode 100644
index 0000000..650b990
--- /dev/null
+++ b/media/ndk/tests/AImageReaderWindowTest.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hidl/token/1.0/ITokenManager.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <hidl/ServiceManagement.h>
+#include <media/NdkImageReader.h>
+#include <media/NdkImage.h>
+#include <mediautils/AImageReaderUtils.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <NdkImagePriv.h>
+#include <NdkImageReaderPriv.h>
+#include <vndk/hardware_buffer.h>
+#include <memory>
+
+namespace android {
+
+using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer;
+using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+using hidl::manager::V1_2::IServiceManager;
+using hidl::token::V1_0::ITokenManager;
+using aimg::AImageReader_getHGBPFromHandle;
+
+typedef IGraphicBufferProducer::QueueBufferInput QueueBufferInput;
+typedef IGraphicBufferProducer::QueueBufferOutput QueueBufferOutput;
+
+static constexpr uint64_t kImageBufferUsage =
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+static constexpr int kImageWidth = 640;
+static constexpr int kImageHeight = 480;
+static constexpr int kImageFormat = AIMAGE_FORMAT_RGBA_8888;
+static constexpr int kMaxImages = 1;
+
+static constexpr int64_t kQueueBufferInputTimeStamp = 1384888611;
+static constexpr bool kQueueBufferInputIsAutoTimeStamp = false;
+static constexpr android_dataspace kQueueBufferInputDataspace = HAL_DATASPACE_UNKNOWN;
+static const Rect kQueueBufferInputRect = Rect(kImageWidth, kImageHeight);
+static constexpr int kQueueBufferInputScalingMode = 0;
+static constexpr int kQueueBufferInputTransform = 0;
+static const sp<Fence> kQueueBufferInputFence = Fence::NO_FENCE;
+
+static constexpr int kOnImageAvailableWaitUs = 100 * 1000;
+
+class AImageReaderWindowTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ AImageReader_newWithUsage(kImageWidth, kImageHeight, kImageFormat,
+ kImageBufferUsage , kMaxImages, &imageReader_);
+ media_status_t ret = AMEDIA_ERROR_UNKNOWN;
+ ASSERT_NE(imageReader_, nullptr);
+ ret = AImageReader_setImageListener(imageReader_,
+ &imageReaderAvailableCb_);
+ ASSERT_EQ(ret, AMEDIA_OK);
+ ret = AImageReader_setBufferRemovedListener(imageReader_,
+ &imageReaderDetachedCb_);
+ ASSERT_EQ(ret, AMEDIA_OK);
+ }
+ void TearDown() override {
+ if (imageReader_) {
+ AImageReader_delete(imageReader_);
+ }
+ }
+
+ void HandleImageAvailable() {
+ AImage *outImage = nullptr;
+ media_status_t ret = AMEDIA_OK;
+ auto imageDeleter = [](AImage *img) { AImage_delete(img); };
+ std::unique_ptr<AImage, decltype(imageDeleter)> img(nullptr, imageDeleter);
+
+ // Test that the image can be acquired.
+ ret = AImageReader_acquireNextImage(imageReader_, &outImage);
+ ASSERT_EQ(ret, AMEDIA_OK);
+ img.reset(outImage);
+ ASSERT_NE(img, nullptr);
+
+ // Test that we can get a handle to the image's hardware buffer and a
+ // native handle to it.
+ AHardwareBuffer *hardwareBuffer = nullptr;
+ ret = AImage_getHardwareBuffer(img.get(), &hardwareBuffer);
+ ASSERT_EQ(ret, AMEDIA_OK);
+ ASSERT_NE(hardwareBuffer, nullptr);
+ const native_handle_t *nh = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+ ASSERT_NE(nh, nullptr);
+ std::unique_lock<std::mutex> lock(imageAvailableMutex_);
+ imageAvailable_ = true;
+ imageCondVar_.notify_one();
+ }
+
+ static void onImageAvailable(void *context, AImageReader *reader) {
+ (void)reader;
+ AImageReaderWindowTest *thisContext =
+ reinterpret_cast<AImageReaderWindowTest *>(context);
+ thisContext->HandleImageAvailable();
+ }
+
+ static void onBufferRemoved(void *, AImageReader *, AHardwareBuffer *) {
+ }
+
+ static void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
+ const size_t PIXEL_SIZE = 4;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ off_t offset = (y * stride + x) * PIXEL_SIZE;
+ for (int c = 0; c < 4; c++) {
+ int parityX = (x / (1 << (c+2))) & 1;
+ int parityY = (y / (1 << (c+2))) & 1;
+ buf[offset + c] = (parityX ^ parityY) ? 231 : 35;
+ }
+ }
+ }
+ }
+
+ void validateIGBP(sp<IGraphicBufferProducer>& igbp) {
+ int dequeuedSlot = -1;
+ sp<Fence> dequeuedFence;
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, igbp->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
+
+ // Test that we can dequeue a buffer.
+ ASSERT_EQ(OK,
+ ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ (igbp->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+ kImageWidth, kImageHeight,
+ kImageFormat, kImageBufferUsage,
+ nullptr, nullptr)));
+ EXPECT_LE(0, dequeuedSlot);
+ EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
+
+ sp<GraphicBuffer> dequeuedBuffer;
+ igbp->requestBuffer(dequeuedSlot, &dequeuedBuffer);
+ uint8_t* img = nullptr;
+ ASSERT_EQ(NO_ERROR, dequeuedBuffer->lock(kImageBufferUsage, (void**)(&img)));
+
+ // Write in some placeholder image data.
+ fillRGBA8Buffer(img, dequeuedBuffer->getWidth(), dequeuedBuffer->getHeight(),
+ dequeuedBuffer->getStride());
+ ASSERT_EQ(NO_ERROR, dequeuedBuffer->unlock());
+ QueueBufferInput queueBufferInput(kQueueBufferInputTimeStamp,
+ kQueueBufferInputIsAutoTimeStamp,
+ kQueueBufferInputDataspace,
+ kQueueBufferInputRect,
+ kQueueBufferInputScalingMode,
+ kQueueBufferInputTransform,
+ kQueueBufferInputFence);
+ QueueBufferOutput queueBufferOutput;
+ ASSERT_EQ(OK, igbp->queueBuffer(dequeuedSlot, queueBufferInput,
+ &queueBufferOutput));
+ // wait until the onImageAvailable callback is called, or timeout completes.
+ std::unique_lock<std::mutex> lock(imageAvailableMutex_);
+ imageCondVar_.wait_for(lock, std::chrono::microseconds(kOnImageAvailableWaitUs),
+ [this]{ return this->imageAvailable_;});
+ EXPECT_TRUE(imageAvailable_) << "Timed out waiting for image data to be handled!\n";
+ }
+
+ AImageReader *imageReader_ = nullptr;
+ AImageReader_ImageListener imageReaderAvailableCb_{this, onImageAvailable};
+ AImageReader_BufferRemovedListener imageReaderDetachedCb_{this, onBufferRemoved};
+ std::mutex imageAvailableMutex_;
+ std::condition_variable imageCondVar_;
+ bool imageAvailable_ = false;
+};
+
+
+TEST_F(AImageReaderWindowTest, CreateWindowNativeHandle) {
+ // Check that we can create a native_handle_t corresponding to the
+ // AImageReader.
+ native_handle_t *nh = nullptr;
+ media_status_t status = AImageReader_getWindowNativeHandle(imageReader_, &nh);
+
+ // On newer devices without the HIDL TokenManager service this API is
+ // deprecated and will return an error.
+ if (IServiceManager::Transport::EMPTY ==
+ hardware::defaultServiceManager1_2()->getTransport(ITokenManager::descriptor, "default")) {
+ EXPECT_EQ(status, AMEDIA_ERROR_UNKNOWN);
+ return;
+ }
+ ASSERT_NE(nh, nullptr);
+
+ // Check that there are only ints in the handle.
+ ASSERT_EQ(nh->numFds, 0);
+ ASSERT_NE(nh->numInts, 0);
+
+ // Check that the HGBP can be retrieved from the handle.
+ sp<HGraphicBufferProducer> hgbp = AImageReader_getHGBPFromHandle(nh);
+ ASSERT_NE(hgbp, nullptr);
+ sp<IGraphicBufferProducer> igbp = new H2BGraphicBufferProducer(hgbp);
+
+ validateIGBP(igbp);
+}
+
+TEST_F(AImageReaderWindowTest, CreateWindow) {
+ ANativeWindow* window = nullptr;
+ media_status_t status = AImageReader_getWindow(imageReader_, &window);
+
+ ASSERT_NE(window, nullptr);
+
+ sp<IGraphicBufferProducer> igbp = Surface::getIGraphicBufferProducer(window);
+
+ validateIGBP(igbp);
+}
+
+class AImageReaderPrivateFormatTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ auto status = AImageReader_new(kImageWidth, kImageHeight, AIMAGE_FORMAT_RAW_DEPTH,
+ kMaxImages, &imgReader);
+ EXPECT_TRUE(status == AMEDIA_OK);
+ }
+
+ void TearDown() override {
+ if (imgReader) {
+ AImageReader_delete(imgReader);
+ }
+ }
+ AImageReader *imgReader = nullptr;
+};
+
+TEST_F(AImageReaderPrivateFormatTest, CreateTest) {
+ EXPECT_TRUE(imgReader != nullptr);
+}
+
+
+} // namespace android
diff --git a/media/utils/MethodStatistics.cpp b/media/utils/MethodStatistics.cpp
index 086757b..80f0fc4 100644
--- a/media/utils/MethodStatistics.cpp
+++ b/media/utils/MethodStatistics.cpp
@@ -20,6 +20,8 @@
// Repository for MethodStatistics Objects
+// It's important to have the HAL class name defined with suffix "Hidl/Aidl" because
+// TimerThread::isRequestFromHal use this string to match binder call to/from hal.
std::shared_ptr<std::vector<std::string>>
getStatisticsClassesForModule(std::string_view moduleName) {
static const std::map<std::string, std::shared_ptr<std::vector<std::string>>,
@@ -34,6 +36,15 @@
"StreamOutHalHidl",
})
},
+ {
+ METHOD_STATISTICS_MODULE_NAME_AUDIO_AIDL,
+ std::shared_ptr<std::vector<std::string>>(
+ new std::vector<std::string>{
+ "DeviceHalAidl",
+ "EffectHalAidl",
+ "StreamHalAidl",
+ })
+ },
};
auto it = m.find(moduleName);
if (it == m.end()) return {};
@@ -61,6 +72,9 @@
addClassesToMap(
getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
m);
+ addClassesToMap(
+ getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_AIDL),
+ m);
return m;
}();
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 3966103..25852e4 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -104,11 +104,16 @@
//
/* static */
bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
- const size_t hidlPos = request->tag.asStringView().find("Hidl");
- if (hidlPos == std::string::npos) return false;
- // should be a separator afterwards Hidl which indicates the string was in the class.
- const size_t separatorPos = request->tag.asStringView().find("::", hidlPos);
- return separatorPos != std::string::npos;
+ for (const auto& s : {"Hidl", "Aidl"}) {
+ const auto& tagSV = request->tag.asStringView();
+ const size_t halStrPos = tagSV.find(s);
+ // should be a separator afterwards Hidl/Aidl which indicates the string was in the class.
+ if (halStrPos != std::string::npos && tagSV.find("::", halStrPos) != std::string::npos) {
+ return true;
+ }
+ }
+
+ return false;
}
struct TimerThread::SnapshotAnalysis TimerThread::getSnapshotAnalysis(size_t retiredCount) const {
diff --git a/media/utils/include/mediautils/ExtendedAccumulator.h b/media/utils/include/mediautils/ExtendedAccumulator.h
index 7e3e170..30045f3 100644
--- a/media/utils/include/mediautils/ExtendedAccumulator.h
+++ b/media/utils/include/mediautils/ExtendedAccumulator.h
@@ -48,9 +48,9 @@
public:
enum class Wrap {
- NORMAL = 0,
- UNDERFLOW = 1,
- OVERFLOW = 2,
+ Normal = 0,
+ Underflow = 1,
+ Overflow = 2,
};
using UnsignedInt = Integral;
@@ -63,11 +63,11 @@
std::pair<SignedInt, Wrap> poll(UnsignedInt value) {
auto acc = mAccumulated.load(std::memory_order_relaxed);
const auto bottom_bits = static_cast<UnsignedInt>(acc);
- std::pair<SignedInt, Wrap> res = {0, Wrap::NORMAL};
+ std::pair<SignedInt, Wrap> res = {0, Wrap::Normal};
const bool overflow = __builtin_sub_overflow(value, bottom_bits, &res.first);
if (overflow) {
- res.second = (res.first > 0) ? Wrap::OVERFLOW : Wrap::UNDERFLOW;
+ res.second = (res.first > 0) ? Wrap::Overflow : Wrap::Underflow;
}
const bool acc_overflow = __builtin_add_overflow(acc, res.first, &acc);
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
index c8b36d8..2543dfa 100644
--- a/media/utils/include/mediautils/MethodStatistics.h
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -124,6 +124,7 @@
// Managed Statistics support.
// Supported Modules
#define METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL "AudioHidl"
+#define METHOD_STATISTICS_MODULE_NAME_AUDIO_AIDL "AudioAidl"
// Returns a vector of class names for the module, or a nullptr if module not found.
std::shared_ptr<std::vector<std::string>>
diff --git a/media/utils/include/mediautils/StaticStringView.h b/media/utils/include/mediautils/StaticStringView.h
index 14be240..e9a5deb 100644
--- a/media/utils/include/mediautils/StaticStringView.h
+++ b/media/utils/include/mediautils/StaticStringView.h
@@ -21,15 +21,15 @@
#pragma push_macro("EXPLICIT_CONVERSION_GENERATE_OPERATOR")
#undef EXPLICIT_CONVERSION_GENERATE_OPERATOR
-#define EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, op) \
- friend constexpr bool operator op(T lhs, T rhs) { \
- return operator op(static_cast<U>(lhs), static_cast<U>(rhs)); \
- } \
- friend constexpr bool operator op(T lhs, U rhs) { \
- return operator op(static_cast<U>(lhs), rhs); \
- } \
- friend constexpr bool operator op(U lhs, T rhs) { \
- return operator op(lhs, static_cast<U>(rhs)); \
+#define EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, op) \
+ friend constexpr bool operator op(T lhs, T rhs) { \
+ return static_cast<U>(lhs) op static_cast<U>(rhs); \
+ } \
+ friend constexpr bool operator op(T lhs, U rhs) { \
+ return static_cast<U>(lhs) op rhs; \
+ } \
+ friend constexpr bool operator op(U lhs, T rhs) { \
+ return lhs op static_cast<U>(rhs); \
}
#pragma push_macro("EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS")
diff --git a/media/utils/tests/extended_accumulator_tests.cpp b/media/utils/tests/extended_accumulator_tests.cpp
index e243e7e..2591df0 100644
--- a/media/utils/tests/extended_accumulator_tests.cpp
+++ b/media/utils/tests/extended_accumulator_tests.cpp
@@ -68,10 +68,10 @@
EXPECT_EQ(result, delta);
// Test overflow/underflow event reporting.
- if (next < base) EXPECT_EQ(TestDetect::Wrap::UNDERFLOW, status);
+ if (next < base) EXPECT_EQ(TestDetect::Wrap::Underflow, status);
else if (next > base + std::numeric_limits<TestUInt>::max())
- EXPECT_EQ(TestDetect::Wrap::OVERFLOW, status);
- else EXPECT_EQ(TestDetect::Wrap::NORMAL, status);
+ EXPECT_EQ(TestDetect::Wrap::Overflow, status);
+ else EXPECT_EQ(TestDetect::Wrap::Normal, status);
}
// Test this utility on every combination of prior and update value for the
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 98b6c27..403fb9e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -270,9 +270,6 @@
BatteryNotifier::getInstance().noteResetAudio();
mMediaLogNotifier->run("MediaLogNotifier");
- std::vector<pid_t> halPids;
- mDevicesFactoryHal->getHalPids(&halPids);
- mediautils::TimeCheck::setAudioHalPids(halPids);
// Notify that we have started (also called when audioserver service restarts)
mediametrics::LogItem(mMetricsId)
@@ -859,12 +856,15 @@
dprintf(fd, "\nIEffect binder call profile:\n");
write(fd, timeCheckStats.c_str(), timeCheckStats.size());
- // Automatically fetch HIDL statistics.
- std::shared_ptr<std::vector<std::string>> hidlClassNames =
- mediautils::getStatisticsClassesForModule(
- METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL);
- if (hidlClassNames) {
- for (const auto& className : *hidlClassNames) {
+ // Automatically fetch HIDL or AIDL statistics.
+ const std::string_view halType = (mDevicesFactoryHal->getHalVersion().getType() ==
+ AudioHalVersionInfo::Type::HIDL)
+ ? METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL
+ : METHOD_STATISTICS_MODULE_NAME_AUDIO_AIDL;
+ const std::shared_ptr<std::vector<std::string>> halClassNames =
+ mediautils::getStatisticsClassesForModule(halType);
+ if (halClassNames) {
+ for (const auto& className : *halClassNames) {
auto stats = mediautils::getStatisticsForClass(className);
if (stats) {
timeCheckStats = stats->dump();
@@ -3196,41 +3196,19 @@
return 0;
}
- audio_config_t halconfig = *config;
- sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();
- sp<StreamInHalInterface> inStream;
- status_t status = inHwHal->openInputStream(
- *input, devices, &halconfig, flags, address, source,
- outputDevice, outputDeviceAddress, &inStream);
- ALOGV("openInput_l() openInputStream returned input %p, devices %#x, SamplingRate %d"
- ", Format %#x, Channels %#x, flags %#x, status %d addr %s",
- inStream.get(),
+ AudioStreamIn *inputStream = nullptr;
+ status_t status = inHwDev->openInputStream(
+ &inputStream,
+ *input,
devices,
- halconfig.sample_rate,
- halconfig.format,
- halconfig.channel_mask,
flags,
- status, address);
+ config,
+ address,
+ source,
+ outputDevice,
+ outputDeviceAddress.c_str());
- // If the input could not be opened with the requested parameters and we can handle the
- // conversion internally, try to open again with the proposed parameters.
- if (status == BAD_VALUE &&
- audio_is_linear_pcm(config->format) &&
- audio_is_linear_pcm(halconfig.format) &&
- (halconfig.sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
- (audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_LIMIT) &&
- (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_LIMIT)) {
- // FIXME describe the change proposed by HAL (save old values so we can log them here)
- ALOGV("openInput_l() reopening with proposed sampling rate and channel mask");
- inStream.clear();
- status = inHwHal->openInputStream(
- *input, devices, &halconfig, flags, address, source,
- outputDevice, outputDeviceAddress, &inStream);
- // FIXME log this new status; HAL should not propose any further changes
- }
-
- if (status == NO_ERROR && inStream != 0) {
- AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream, flags);
+ if (status == NO_ERROR) {
if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) {
const sp<IAfMmapCaptureThread> thread =
IAfMmapCaptureThread::create(this, *input, inHwDev, inputStream, mSystemReady);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index ae60ed0..2577ca8 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -23,6 +23,7 @@
#include <audio_utils/mutex.h>
#include <audio_utils/LinearMap.h>
#include <binder/AppOpsManager.h>
+#include <utils/RWLock.h>
namespace android {
@@ -352,6 +353,7 @@
// Must hold thread lock to access tee patches
template <class F>
void forEachTeePatchTrack_l(F f) {
+ RWLock::AutoRLock readLock(mTeePatchesRWLock);
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
};
@@ -387,6 +389,7 @@
audio_output_flags_t mFlags;
TeePatches mTeePatches;
std::optional<TeePatches> mTeePatchesToUpdate;
+ RWLock mTeePatchesRWLock;
const float mSpeed;
const bool mIsSpatialized;
const bool mIsBitPerfect;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d8d727c..01e7b0d 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -185,7 +185,7 @@
// Minimum amount of time between checking to see if the timestamp is advancing
// for underrun detection. If we check too frequently, we may not detect a
// timestamp update and will falsely detect underrun.
-static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000;
+static constexpr nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1'000'000;
// The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good
// balance between power consumption and latency, and allows threads to be scheduled reliably
@@ -9544,10 +9544,24 @@
void RecordThread::readInputParameters_l()
{
- status_t result = mInput->stream->getAudioProperties(&mSampleRate, &mChannelMask, &mHALFormat);
- LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving audio properties from HAL: %d", result);
- mFormat = mHALFormat;
+ const audio_config_base_t audioConfig = mInput->getAudioProperties();
+ mSampleRate = audioConfig.sample_rate;
+ mChannelMask = audioConfig.channel_mask;
+ if (!audio_is_input_channel(mChannelMask)) {
+ LOG_ALWAYS_FATAL("Channel mask %#x not valid for input", mChannelMask);
+ }
+
mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+
+ // Get actual HAL format.
+ status_t result = mInput->stream->getAudioProperties(nullptr, nullptr, &mHALFormat);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Error when retrieving input stream format: %d", result);
+ // Get format from the shim, which will be different than the HAL format
+ // if recording compressed audio from IEC61937 wrapped sources.
+ mFormat = audioConfig.format;
+ if (!audio_is_valid_format(mFormat)) {
+ LOG_ALWAYS_FATAL("Format %#x not valid for input", mFormat);
+ }
if (audio_is_linear_pcm(mFormat)) {
LOG_ALWAYS_FATAL_IF(mChannelCount > FCC_LIMIT, "HAL channel count %d > %d",
mChannelCount, FCC_LIMIT);
@@ -9555,8 +9569,7 @@
// Can have more that FCC_LIMIT channels in encoded streams.
ALOGI("HAL format %#x is not linear pcm", mFormat);
}
- result = mInput->stream->getFrameSize(&mFrameSize);
- LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
+ mFrameSize = mInput->getFrameSize();
LOG_ALWAYS_FATAL_IF(mFrameSize <= 0, "Error frame size was %zu but must be greater than zero",
mFrameSize);
result = mInput->stream->getBufferSize(&mBufferSize);
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 224c65b..4fe5b84 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1081,7 +1081,13 @@
// Additionally PatchProxyBufferProvider::obtainBuffer (called by PathTrack::getNextBuffer)
// does not allow 0 frame size request contrary to getNextBuffer
}
- for (auto& teePatch : mTeePatches) {
+ TeePatches teePatches;
+ if (mTeePatchesRWLock.tryReadLock() == NO_ERROR) {
+ // Cache a copy of tee patches in case it is updated while using.
+ teePatches = mTeePatches;
+ mTeePatchesRWLock.unlock();
+ }
+ for (auto& teePatch : teePatches) {
IAfPatchRecord* patchRecord = teePatch.patchRecord.get();
const size_t framesWritten = patchRecord->writeFrames(
sourceBuffer.i8, frameCount, mFrameSize);
@@ -1094,7 +1100,7 @@
using namespace std::chrono_literals;
// Average is ~20us per track, this should virtually never be logged (Logging takes >200us)
ALOGD_IF(spent > 500us, "%s: took %lldus to intercept %zu tracks", __func__,
- spent.count(), mTeePatches.size());
+ spent.count(), teePatches.size());
}
// ExtendedAudioBufferProvider interface
@@ -1616,7 +1622,10 @@
void Track::updateTeePatches_l() {
if (mTeePatchesToUpdate.has_value()) {
forEachTeePatchTrack_l([](const auto& patchTrack) { patchTrack->destroy(); });
- mTeePatches = mTeePatchesToUpdate.value();
+ {
+ RWLock::AutoWLock writeLock(mTeePatchesRWLock);
+ mTeePatches = std::move(mTeePatchesToUpdate.value());
+ }
if (mState == TrackBase::ACTIVE || mState == TrackBase::RESUMING ||
mState == TrackBase::STOPPING_1) {
forEachTeePatchTrack_l([](const auto& patchTrack) { patchTrack->start(); });
diff --git a/services/audioflinger/datapath/Android.bp b/services/audioflinger/datapath/Android.bp
index ee98aef..4235f14 100644
--- a/services/audioflinger/datapath/Android.bp
+++ b/services/audioflinger/datapath/Android.bp
@@ -43,11 +43,14 @@
srcs: [
"AudioHwDevice.cpp",
+ "AudioStreamIn.cpp",
"AudioStreamOut.cpp",
+ "SpdifStreamIn.cpp",
"SpdifStreamOut.cpp",
],
header_libs: [
+ "libaudioclient_headers",
"libaudiohal_headers",
"liberror_headers",
],
diff --git a/services/audioflinger/datapath/AudioHwDevice.cpp b/services/audioflinger/datapath/AudioHwDevice.cpp
index 67e9991..95e9ecc 100644
--- a/services/audioflinger/datapath/AudioHwDevice.cpp
+++ b/services/audioflinger/datapath/AudioHwDevice.cpp
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2007, 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.
-*/
+ *
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#define LOG_TAG "AudioHwDevice"
//#define LOG_NDEBUG 0
@@ -21,10 +21,13 @@
#include <system/audio.h>
#include <utils/Log.h>
+#include <audio_utils/spdif/SPDIFDecoder.h>
#include <audio_utils/spdif/SPDIFEncoder.h>
+#include <media/AudioResamplerPublic.h>
#include "AudioHwDevice.h"
#include "AudioStreamOut.h"
+#include "SpdifStreamIn.h"
#include "SpdifStreamOut.h"
namespace android {
@@ -47,12 +50,8 @@
auto outputStream = new AudioStreamOut(this, flags);
// Try to open the HAL first using the current format.
- ALOGV("openOutputStream(), try "
- " sampleRate %d, Format %#x, "
- "channelMask %#x",
- config->sample_rate,
- config->format,
- config->channel_mask);
+ ALOGV("openOutputStream(), try sampleRate %d, format %#x, channelMask %#x", config->sample_rate,
+ config->format, config->channel_mask);
status_t status = outputStream->open(handle, deviceType, config, address);
if (status != NO_ERROR) {
@@ -62,13 +61,8 @@
// FIXME Look at any modification to the config.
// The HAL might modify the config to suggest a wrapped format.
// Log this so we can see what the HALs are doing.
- ALOGI("openOutputStream(), HAL returned"
- " sampleRate %d, Format %#x, "
- "channelMask %#x, status %d",
- config->sample_rate,
- config->format,
- config->channel_mask,
- status);
+ ALOGI("openOutputStream(), HAL returned sampleRate %d, format %#x, channelMask %#x,"
+ " status %d", config->sample_rate, config->format, config->channel_mask, status);
// If the data is encoded then try again using wrapped PCM.
const bool wrapperNeeded = !audio_has_proportional_frames(originalConfig.format)
@@ -96,6 +90,79 @@
return status;
}
+status_t AudioHwDevice::openInputStream(
+ AudioStreamIn **ppStreamIn,
+ audio_io_handle_t handle,
+ audio_devices_t deviceType,
+ audio_input_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char *outputDeviceAddress) {
+
+ struct audio_config originalConfig = *config;
+ auto inputStream = new AudioStreamIn(this, flags);
+
+ // Try to open the HAL first using the current format.
+ ALOGV("openInputStream(), try sampleRate %d, format %#x, channelMask %#x", config->sample_rate,
+ config->format, config->channel_mask);
+ status_t status = inputStream->open(handle, deviceType, config, address, source, outputDevice,
+ outputDeviceAddress);
+
+ // If the input could not be opened with the requested parameters and we can handle the
+ // conversion internally, try to open again with the proposed parameters.
+ if (status == BAD_VALUE &&
+ audio_is_linear_pcm(originalConfig.format) &&
+ audio_is_linear_pcm(config->format) &&
+ (config->sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
+ (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_LIMIT) &&
+ (audio_channel_count_from_in_mask(originalConfig.channel_mask) <= FCC_LIMIT)) {
+ // FIXME describe the change proposed by HAL (save old values so we can log them here)
+ ALOGV("openInputStream() reopening with proposed sampling rate and channel mask");
+ status = inputStream->open(handle, deviceType, config, address, source,
+ outputDevice, outputDeviceAddress);
+ // FIXME log this new status; HAL should not propose any further changes
+ if (status != NO_ERROR) {
+ delete inputStream;
+ inputStream = nullptr;
+ }
+ } else if (status != NO_ERROR) {
+ delete inputStream;
+ inputStream = nullptr;
+
+ // FIXME Look at any modification to the config.
+ // The HAL might modify the config to suggest a wrapped format.
+ // Log this so we can see what the HALs are doing.
+ ALOGI("openInputStream(), HAL returned sampleRate %d, format %#x, channelMask %#x,"
+ " status %d", config->sample_rate, config->format, config->channel_mask, status);
+
+ // If the data is encoded then try again using wrapped PCM.
+ const bool unwrapperNeeded = !audio_has_proportional_frames(originalConfig.format)
+ && ((flags & AUDIO_INPUT_FLAG_DIRECT) != 0);
+
+ if (unwrapperNeeded) {
+ if (SPDIFDecoder::isFormatSupported(originalConfig.format)) {
+ inputStream = new SpdifStreamIn(this, flags, originalConfig.format);
+ status = inputStream->open(handle, deviceType, &originalConfig, address, source,
+ outputDevice, outputDeviceAddress);
+ if (status != NO_ERROR) {
+ ALOGE("ERROR - openInputStream(), SPDIF open returned %d",
+ status);
+ delete inputStream;
+ inputStream = nullptr;
+ }
+ } else {
+ ALOGE("ERROR - openInputStream(), SPDIFDecoder does not support format 0x%08x",
+ originalConfig.format);
+ }
+ }
+ }
+
+ *ppStreamIn = inputStream;
+ return status;
+}
+
bool AudioHwDevice::supportsAudioPatches() const {
bool result;
return mHwDevice->supportsAudioPatches(&result) == OK ? result : false;
diff --git a/services/audioflinger/datapath/AudioHwDevice.h b/services/audioflinger/datapath/AudioHwDevice.h
index cfb6fbd..80c1473 100644
--- a/services/audioflinger/datapath/AudioHwDevice.h
+++ b/services/audioflinger/datapath/AudioHwDevice.h
@@ -1,22 +1,21 @@
/*
-**
-** Copyright 2007, 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.
-*/
+ *
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-#ifndef ANDROID_AUDIO_HW_DEVICE_H
-#define ANDROID_AUDIO_HW_DEVICE_H
+#pragma once
#include <stdint.h>
#include <stdlib.h>
@@ -30,6 +29,7 @@
namespace android {
+class AudioStreamIn;
class AudioStreamOut;
class AudioHwDevice {
@@ -89,6 +89,17 @@
struct audio_config *config,
const char *address);
+ status_t openInputStream(
+ AudioStreamIn **ppStreamIn,
+ audio_io_handle_t handle,
+ audio_devices_t deviceType,
+ audio_input_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char *outputDeviceAddress);
+
[[nodiscard]] bool supportsAudioPatches() const;
[[nodiscard]] status_t getAudioPort(struct audio_port_v7 *port) const;
@@ -112,5 +123,3 @@
};
} // namespace android
-
-#endif // ANDROID_AUDIO_HW_DEVICE_H
diff --git a/services/audioflinger/datapath/AudioStreamIn.cpp b/services/audioflinger/datapath/AudioStreamIn.cpp
new file mode 100644
index 0000000..24f3bb9
--- /dev/null
+++ b/services/audioflinger/datapath/AudioStreamIn.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+#include "AudioStreamIn.h"
+
+#include <media/audiohal/DeviceHalInterface.h>
+#include <media/audiohal/StreamHalInterface.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include "AudioHwDevice.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+AudioStreamIn::AudioStreamIn(AudioHwDevice *dev, audio_input_flags_t flags)
+ : audioHwDev(dev)
+ , flags(flags)
+{
+}
+
+// This must be defined here together with the HAL includes above and
+// not solely in the header.
+AudioStreamIn::~AudioStreamIn() = default;
+
+sp<DeviceHalInterface> AudioStreamIn::hwDev() const
+{
+ return audioHwDev->hwDevice();
+}
+
+status_t AudioStreamIn::getCapturePosition(int64_t* frames, int64_t* time)
+{
+ if (stream == nullptr) {
+ return NO_INIT;
+ }
+
+ int64_t halPosition = 0;
+ const status_t status = stream->getCapturePosition(&halPosition, time);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ // Adjust for standby using HAL rate frames.
+ // Only apply this correction if the HAL is getting PCM frames.
+ if (mHalFormatHasProportionalFrames) {
+ const uint64_t adjustedPosition = (halPosition <= mFramesReadAtStandby) ?
+ 0 : (halPosition - mFramesReadAtStandby);
+ // Scale from HAL sample rate to application rate.
+ *frames = adjustedPosition / mRateMultiplier;
+ } else {
+ // For compressed formats.
+ *frames = halPosition;
+ }
+
+ return status;
+}
+
+status_t AudioStreamIn::open(
+ audio_io_handle_t handle,
+ audio_devices_t deviceType,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char *outputDeviceAddress)
+{
+ sp<StreamInHalInterface> inStream;
+
+ int status = hwDev()->openInputStream(
+ handle,
+ deviceType,
+ config,
+ flags,
+ address,
+ source,
+ outputDevice,
+ outputDeviceAddress,
+ &inStream);
+ ALOGV("AudioStreamIn::open(), HAL returned stream %p, sampleRate %d, format %#x,"
+ " channelMask %#x, status %d", inStream.get(), config->sample_rate, config->format,
+ config->channel_mask, status);
+
+ if (status == NO_ERROR) {
+ stream = inStream;
+ mHalFormatHasProportionalFrames = audio_has_proportional_frames(config->format);
+ status = stream->getFrameSize(&mHalFrameSize);
+ LOG_ALWAYS_FATAL_IF(status != OK, "Error retrieving frame size from HAL: %d", status);
+ LOG_ALWAYS_FATAL_IF(mHalFrameSize == 0, "Error frame size was %zu but must be greater than"
+ " zero", mHalFrameSize);
+ }
+
+ return status;
+}
+
+audio_config_base_t AudioStreamIn::getAudioProperties() const
+{
+ audio_config_base_t result = AUDIO_CONFIG_BASE_INITIALIZER;
+ if (stream->getAudioProperties(&result) != OK) {
+ result.sample_rate = 0;
+ result.channel_mask = AUDIO_CHANNEL_INVALID;
+ result.format = AUDIO_FORMAT_INVALID;
+ }
+ return result;
+}
+
+status_t AudioStreamIn::standby()
+{
+ mFramesReadAtStandby = mFramesRead;
+ return stream->standby();
+}
+
+status_t AudioStreamIn::read(void* buffer, size_t bytes, size_t* read)
+{
+ const status_t result = stream->read(buffer, bytes, read);
+ if (result == OK && *read > 0 && mHalFrameSize > 0) {
+ mFramesRead += *read / mHalFrameSize;
+ }
+ return result;
+}
+
+} // namespace android
diff --git a/services/audioflinger/datapath/AudioStreamIn.h b/services/audioflinger/datapath/AudioStreamIn.h
index 604a4e4..6d1c6a7 100644
--- a/services/audioflinger/datapath/AudioStreamIn.h
+++ b/services/audioflinger/datapath/AudioStreamIn.h
@@ -31,30 +31,57 @@
virtual status_t standby() = 0;
};
-// AudioStreamIn is immutable, so its fields are const.
-// The methods must not be const to match StreamHalInterface signature.
-
-struct AudioStreamIn : public Source {
+/**
+ * Managed access to a HAL input stream.
+ */
+class AudioStreamIn : public Source {
+public:
const AudioHwDevice* const audioHwDev;
- const sp<StreamInHalInterface> stream;
+ sp<StreamInHalInterface> stream;
const audio_input_flags_t flags;
- AudioStreamIn(
- const AudioHwDevice* dev, const sp<StreamInHalInterface>& in,
- audio_input_flags_t flags)
- : audioHwDev(dev), stream(in), flags(flags) {}
+ [[nodiscard]] sp<DeviceHalInterface> hwDev() const;
- status_t read(void* buffer, size_t bytes, size_t* read) final {
- return stream->read(buffer, bytes, read);
- }
+ AudioStreamIn(AudioHwDevice *dev, audio_input_flags_t flags);
- status_t getCapturePosition(int64_t* frames, int64_t* time) final {
- return stream->getCapturePosition(frames, time);
- }
+ virtual status_t open(
+ audio_io_handle_t handle,
+ audio_devices_t deviceType,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char *outputDeviceAddress);
- status_t standby() final { return stream->standby(); }
+ ~AudioStreamIn() override;
- sp<DeviceHalInterface> hwDev() const { return audioHwDev->hwDevice(); }
+ status_t getCapturePosition(int64_t* frames, int64_t* time) override;
+
+ status_t read(void* buffer, size_t bytes, size_t* read) override;
+
+ /**
+ * @return frame size from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] virtual size_t getFrameSize() const { return mHalFrameSize; }
+
+ /**
+ * @return audio stream configuration: channel mask, format, sample rate:
+ * - channel mask from the perspective of the application and the AudioFlinger,
+ * The HAL is in stereo mode when playing multi-channel compressed audio over HDMI;
+ * - format from the perspective of the application and the AudioFlinger;
+ * - sample rate from the perspective of the application and the AudioFlinger,
+ * The HAL may be running at a higher sample rate if, for example, playing wrapped EAC3.
+ */
+ [[nodiscard]] virtual audio_config_base_t getAudioProperties() const;
+
+ status_t standby() override;
+
+protected:
+ uint64_t mFramesRead = 0;
+ int64_t mFramesReadAtStandby = 0;
+ int mRateMultiplier = 1;
+ bool mHalFormatHasProportionalFrames = false;
+ size_t mHalFrameSize = 0;
};
} // namespace android
diff --git a/services/audioflinger/datapath/AudioStreamOut.cpp b/services/audioflinger/datapath/AudioStreamOut.cpp
index 6fa82e5..1830d15 100644
--- a/services/audioflinger/datapath/AudioStreamOut.cpp
+++ b/services/audioflinger/datapath/AudioStreamOut.cpp
@@ -1,30 +1,31 @@
/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "AudioStreamOut.h"
+
#include <media/audiohal/DeviceHalInterface.h>
#include <media/audiohal/StreamHalInterface.h>
#include <system/audio.h>
#include <utils/Log.h>
#include "AudioHwDevice.h"
-#include "AudioStreamOut.h"
namespace android {
@@ -132,14 +133,9 @@
config,
address,
&outStream);
- ALOGV("AudioStreamOut::open(), HAL returned "
- " stream %p, sampleRate %d, Format %#x, "
- "channelMask %#x, status %d",
- outStream.get(),
- config->sample_rate,
- config->format,
- config->channel_mask,
- status);
+ ALOGV("AudioStreamOut::open(), HAL returned stream %p, sampleRate %d, format %#x,"
+ " channelMask %#x, status %d", outStream.get(), config->sample_rate, config->format,
+ config->channel_mask, status);
// Some HALs may not recognize AUDIO_FORMAT_IEC61937. But if we declare
// it as PCM then it will probably work.
@@ -162,7 +158,7 @@
mHalFormatHasProportionalFrames = audio_has_proportional_frames(config->format);
status = stream->getFrameSize(&mHalFrameSize);
LOG_ALWAYS_FATAL_IF(status != OK, "Error retrieving frame size from HAL: %d", status);
- LOG_ALWAYS_FATAL_IF(mHalFrameSize <= 0, "Error frame size was %zu but must be greater than"
+ LOG_ALWAYS_FATAL_IF(mHalFrameSize == 0, "Error frame size was %zu but must be greater than"
" zero", mHalFrameSize);
}
diff --git a/services/audioflinger/datapath/AudioStreamOut.h b/services/audioflinger/datapath/AudioStreamOut.h
index ce00f8c..ea41bba 100644
--- a/services/audioflinger/datapath/AudioStreamOut.h
+++ b/services/audioflinger/datapath/AudioStreamOut.h
@@ -1,27 +1,28 @@
/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-#ifndef ANDROID_AUDIO_STREAM_OUT_H
-#define ANDROID_AUDIO_STREAM_OUT_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <system/audio.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
namespace android {
@@ -34,9 +35,6 @@
*/
class AudioStreamOut {
public:
-// AudioStreamOut is immutable, so its fields are const.
-// For emphasis, we could also make all pointers to them be "const *",
-// but that would clutter the code unnecessarily.
AudioHwDevice * const audioHwDev;
sp<StreamOutHalInterface> stream;
const audio_output_flags_t flags;
@@ -101,15 +99,13 @@
virtual void presentationComplete() { mExpectRetrograde = true; }
protected:
- uint64_t mFramesWritten = 0; // reset by flush
- uint64_t mFramesWrittenAtStandby = 0;
- uint64_t mRenderPosition = 0; // reset by flush, standby, or presentation complete
- int mRateMultiplier = 1;
- bool mHalFormatHasProportionalFrames = false;
- size_t mHalFrameSize = 0;
- bool mExpectRetrograde = false; // see presentationComplete
+ uint64_t mFramesWritten = 0; // reset by flush
+ uint64_t mFramesWrittenAtStandby = 0;
+ uint64_t mRenderPosition = 0; // reset by flush, standby, or presentation complete
+ int mRateMultiplier = 1;
+ bool mHalFormatHasProportionalFrames = false;
+ size_t mHalFrameSize = 0;
+ bool mExpectRetrograde = false; // see presentationComplete
};
} // namespace android
-
-#endif // ANDROID_AUDIO_STREAM_OUT_H
diff --git a/services/audioflinger/datapath/SpdifStreamIn.cpp b/services/audioflinger/datapath/SpdifStreamIn.cpp
new file mode 100644
index 0000000..98ce712
--- /dev/null
+++ b/services/audioflinger/datapath/SpdifStreamIn.cpp
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright 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.
+ */
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+#include "Configuration.h"
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include <audio_utils/spdif/SPDIFDecoder.h>
+
+#include "AudioHwDevice.h"
+#include "SpdifStreamIn.h"
+
+namespace android {
+
+/**
+ * If the HAL is generating IEC61937 data and AudioFlinger expects elementary stream then we need to
+ * extract the data using an SPDIF decoder.
+ */
+SpdifStreamIn::SpdifStreamIn(AudioHwDevice *dev,
+ audio_input_flags_t flags,
+ audio_format_t format)
+ : AudioStreamIn(dev, flags)
+ , mSpdifDecoder(this, format)
+{
+}
+
+status_t SpdifStreamIn::open(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char* outputDeviceAddress)
+{
+ struct audio_config customConfig = *config;
+
+ mApplicationConfig.format = config->format;
+ mApplicationConfig.sample_rate = config->sample_rate;
+ mApplicationConfig.channel_mask = config->channel_mask;
+
+ mRateMultiplier = spdif_rate_multiplier(config->format);
+ if (mRateMultiplier <= 0) {
+ ALOGE("ERROR SpdifStreamIn::open() unrecognized format 0x%08X\n", config->format);
+ return BAD_VALUE;
+ }
+ customConfig.sample_rate = config->sample_rate * mRateMultiplier;
+ customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ customConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
+
+ // Always print this because otherwise it could be very confusing if the
+ // HAL and AudioFlinger are using different formats.
+ // Print before open() because HAL may modify customConfig.
+ ALOGI("SpdifStreamIn::open() AudioFlinger requested sampleRate %d, format %#x, channelMask %#x",
+ config->sample_rate, config->format, config->channel_mask);
+ ALOGI("SpdifStreamIn::open() HAL configured for sampleRate %d, format %#x, channelMask %#x",
+ customConfig.sample_rate, customConfig.format, customConfig.channel_mask);
+
+ const status_t status = AudioStreamIn::open(
+ handle,
+ devices,
+ &customConfig,
+ address,
+ source,
+ outputDevice,
+ outputDeviceAddress);
+
+ ALOGI("SpdifStreamIn::open() status = %d", status);
+
+#ifdef TEE_SINK
+ if (status == OK) {
+ // Don't use PCM 16-bit format to avoid WAV encoding IEC61937 data.
+ mTee.set(customConfig.sample_rate,
+ audio_channel_count_from_in_mask(customConfig.channel_mask),
+ AUDIO_FORMAT_IEC61937, NBAIO_Tee::TEE_FLAG_INPUT_THREAD);
+ mTee.setId(std::string("_") + std::to_string(handle) + "_C");
+ }
+#endif
+
+ return status;
+}
+
+int SpdifStreamIn::standby()
+{
+ mSpdifDecoder.reset();
+ return AudioStreamIn::standby();
+}
+
+status_t SpdifStreamIn::readDataBurst(void* buffer, size_t bytes, size_t* read)
+{
+ status_t status = AudioStreamIn::read(buffer, bytes, read);
+
+#ifdef TEE_SINK
+ if (*read > 0) {
+ mTee.write(reinterpret_cast<const char *>(buffer), *read / AudioStreamIn::getFrameSize());
+ }
+#endif
+ return status;
+}
+
+status_t SpdifStreamIn::read(void* buffer, size_t numBytes, size_t* read)
+{
+ // Read from SPDIF extractor. It will call back to readDataBurst().
+ const auto bytesRead = mSpdifDecoder.read(buffer, numBytes);
+ if (bytesRead >= 0) {
+ *read = bytesRead;
+ return OK;
+ }
+ return NOT_ENOUGH_DATA;
+}
+
+} // namespace android
diff --git a/services/audioflinger/datapath/SpdifStreamIn.h b/services/audioflinger/datapath/SpdifStreamIn.h
new file mode 100644
index 0000000..78832ee
--- /dev/null
+++ b/services/audioflinger/datapath/SpdifStreamIn.h
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+#include "AudioStreamIn.h"
+
+#include <audio_utils/spdif/SPDIFDecoder.h>
+#include <afutils/NBAIO_Tee.h>
+
+namespace android {
+
+/**
+ * Stream that is a PCM data burst in the HAL but looks like an encoded stream
+ * to the AudioFlinger. Wraps encoded data in an SPDIF wrapper per IEC61973-3.
+ */
+class SpdifStreamIn : public AudioStreamIn {
+public:
+
+ SpdifStreamIn(AudioHwDevice *dev, audio_input_flags_t flags,
+ audio_format_t format);
+
+ status_t open(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ const char *address,
+ audio_source_t source,
+ audio_devices_t outputDevice,
+ const char* outputDeviceAddress) override;
+
+ /**
+ * Read audio buffer from driver. If at least one frame was read successfully prior to the error,
+ * it is suggested that the driver return that successful (short) byte count
+ * and then return an error in the subsequent call.
+ *
+ * If set_callback() has previously been called to enable non-blocking mode
+ * the write() is not allowed to block. It must write only the number of
+ * bytes that currently fit in the driver/hardware buffer and then return
+ * this byte count. If this is less than the requested write size the
+ * callback function must be called when more space is available in the
+ * driver/hardware buffer.
+ */
+ status_t read(void* buffer, size_t bytes, size_t* read) override;
+
+ /**
+ * @return frame size from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] size_t getFrameSize() const override { return sizeof(int8_t); }
+
+ /**
+ * @return audio_config_base_t from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] audio_config_base_t getAudioProperties() const override {
+ return mApplicationConfig;
+ }
+
+ /**
+ * @return format from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] virtual audio_format_t getFormat() const { return mApplicationConfig.format; }
+
+ /**
+ * The HAL may be running at a higher sample rate if, for example, reading wrapped EAC3.
+ * @return sample rate from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] virtual uint32_t getSampleRate() const { return mApplicationConfig.sample_rate; }
+
+ /**
+ * The HAL is in stereo mode when reading multi-channel compressed audio.
+ * @return channel mask from the perspective of the application and the AudioFlinger.
+ */
+ [[nodiscard]] virtual audio_channel_mask_t getChannelMask() const {
+ return mApplicationConfig.channel_mask;
+ }
+
+ status_t standby() override;
+
+private:
+
+ class MySPDIFDecoder : public SPDIFDecoder
+ {
+ public:
+ MySPDIFDecoder(SpdifStreamIn *spdifStreamIn, audio_format_t format)
+ : SPDIFDecoder(format)
+ , mSpdifStreamIn(spdifStreamIn)
+ {
+ }
+
+ ssize_t readInput(void* buffer, size_t bytes) override
+ {
+ size_t bytesRead = 0;
+ const auto result = mSpdifStreamIn->readDataBurst(buffer, bytes, &bytesRead);
+ if (result < 0) {
+ return result;
+ }
+ return bytesRead;
+ }
+
+ protected:
+ SpdifStreamIn * const mSpdifStreamIn;
+ };
+
+ MySPDIFDecoder mSpdifDecoder;
+ audio_config_base_t mApplicationConfig = AUDIO_CONFIG_BASE_INITIALIZER;
+
+ status_t readDataBurst(void* data, size_t bytes, size_t* read);
+
+#ifdef TEE_SINK
+ NBAIO_Tee mTee;
+#endif
+
+};
+
+} // namespace android
diff --git a/services/audioflinger/datapath/SpdifStreamOut.cpp b/services/audioflinger/datapath/SpdifStreamOut.cpp
index 0c6a5a1..65a4eec 100644
--- a/services/audioflinger/datapath/SpdifStreamOut.cpp
+++ b/services/audioflinger/datapath/SpdifStreamOut.cpp
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
@@ -42,10 +42,10 @@
}
status_t SpdifStreamOut::open(
- audio_io_handle_t handle,
- audio_devices_t devices,
- struct audio_config *config,
- const char *address)
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ const char *address)
{
struct audio_config customConfig = *config;
@@ -53,22 +53,10 @@
mApplicationConfig.sample_rate = config->sample_rate;
mApplicationConfig.channel_mask = config->channel_mask;
- // Some data bursts run at a higher sample rate.
- // TODO Move this into the audio_utils as a static method.
- switch(config->format) {
- case AUDIO_FORMAT_E_AC3:
- case AUDIO_FORMAT_E_AC3_JOC:
- mRateMultiplier = 4;
- break;
- case AUDIO_FORMAT_AC3:
- case AUDIO_FORMAT_DTS:
- case AUDIO_FORMAT_DTS_HD:
- mRateMultiplier = 1;
- break;
- default:
- ALOGE("ERROR SpdifStreamOut::open() unrecognized format 0x%08X\n",
- config->format);
- return BAD_VALUE;
+ mRateMultiplier = spdif_rate_multiplier(config->format);
+ if (mRateMultiplier <= 0) {
+ ALOGE("ERROR SpdifStreamOut::open() unrecognized format 0x%08X\n", config->format);
+ return BAD_VALUE;
}
customConfig.sample_rate = config->sample_rate * mRateMultiplier;
@@ -78,16 +66,10 @@
// Always print this because otherwise it could be very confusing if the
// HAL and AudioFlinger are using different formats.
// Print before open() because HAL may modify customConfig.
- ALOGI("SpdifStreamOut::open() AudioFlinger requested"
- " sampleRate %d, format %#x, channelMask %#x",
- config->sample_rate,
- config->format,
- config->channel_mask);
- ALOGI("SpdifStreamOut::open() HAL configured for"
- " sampleRate %d, format %#x, channelMask %#x",
- customConfig.sample_rate,
- customConfig.format,
- customConfig.channel_mask);
+ ALOGI("SpdifStreamOut::open() AudioFlinger requested sampleRate %d, format %#x,"
+ " channelMask %#x", config->sample_rate, config->format, config->channel_mask);
+ ALOGI("SpdifStreamOut::open() HAL configured for sampleRate %d, format %#x, channelMask %#x",
+ customConfig.sample_rate, customConfig.format, customConfig.channel_mask);
const status_t status = AudioStreamOut::open(
handle,
diff --git a/services/audioflinger/datapath/SpdifStreamOut.h b/services/audioflinger/datapath/SpdifStreamOut.h
index 56d57f6..c6d27ba 100644
--- a/services/audioflinger/datapath/SpdifStreamOut.h
+++ b/services/audioflinger/datapath/SpdifStreamOut.h
@@ -1,22 +1,21 @@
/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-#ifndef ANDROID_SPDIF_STREAM_OUT_H
-#define ANDROID_SPDIF_STREAM_OUT_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -40,8 +39,6 @@
SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
audio_format_t format);
- ~SpdifStreamOut() override = default;
-
status_t open(
audio_io_handle_t handle,
audio_devices_t devices,
@@ -116,10 +113,10 @@
SpdifStreamOut * const mSpdifStreamOut;
};
- MySPDIFEncoder mSpdifEncoder;
- audio_config_base_t mApplicationConfig = AUDIO_CONFIG_BASE_INITIALIZER;
+ MySPDIFEncoder mSpdifEncoder;
+ audio_config_base_t mApplicationConfig = AUDIO_CONFIG_BASE_INITIALIZER;
- ssize_t writeDataBurst(const void* data, size_t bytes);
+ ssize_t writeDataBurst(const void* data, size_t bytes);
#ifdef TEE_SINK
NBAIO_Tee mTee;
@@ -128,5 +125,3 @@
};
} // namespace android
-
-#endif // ANDROID_SPDIF_STREAM_OUT_H
diff --git a/services/audioflinger/datapath/ThreadMetrics.h b/services/audioflinger/datapath/ThreadMetrics.h
index c643a57..4eb8aa0 100644
--- a/services/audioflinger/datapath/ThreadMetrics.h
+++ b/services/audioflinger/datapath/ThreadMetrics.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_AUDIO_THREADMETRICS_H
-#define ANDROID_AUDIO_THREADMETRICS_H
+#pragma once
#include <media/MediaMetricsItem.h>
@@ -210,5 +209,3 @@
};
} // namespace android
-
-#endif // ANDROID_AUDIO_THREADMETRICS_H
diff --git a/services/audioflinger/datapath/TrackMetrics.h b/services/audioflinger/datapath/TrackMetrics.h
index 2b44acb..ad5d3db 100644
--- a/services/audioflinger/datapath/TrackMetrics.h
+++ b/services/audioflinger/datapath/TrackMetrics.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_AUDIO_TRACKMETRICS_H
-#define ANDROID_AUDIO_TRACKMETRICS_H
+#pragma once
#include <binder/IActivityManager.h>
#include <binder/IPCThreadState.h>
@@ -306,5 +305,3 @@
};
} // namespace android
-
-#endif // ANDROID_AUDIO_TRACKMETRICS_H
diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
index cf20260..d206637 100644
--- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h
+++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
@@ -89,10 +89,12 @@
status_t addProfile(const sp<IOProfile> &profile);
status_t addOutputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address);
+ audio_devices_t device, const String8& address,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE);
status_t removeOutputProfile(const std::string& name);
status_t addInputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address);
+ audio_devices_t device, const String8& address,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
status_t removeInputProfile(const std::string& name);
audio_module_handle_t getHandle() const { return mHandle; }
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index cb45f54..6696b45 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -59,12 +59,13 @@
}
status_t HwModule::addOutputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address)
+ audio_devices_t device, const String8& address,
+ audio_output_flags_t flags)
{
sp<IOProfile> profile = new OutputProfile(name);
-
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
config->sample_rate));
+ profile->setFlags(flags);
sp<DeviceDescriptor> devDesc =
new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
@@ -128,11 +129,13 @@
}
status_t HwModule::addInputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address)
+ audio_devices_t device, const String8& address,
+ audio_input_flags_t flags)
{
sp<IOProfile> profile = new InputProfile(name);
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
config->sample_rate));
+ profile->setFlags(flags);
sp<DeviceDescriptor> devDesc =
new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d4176c1..2fd908f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3225,7 +3225,8 @@
ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str());
return BAD_VALUE;
}
- ALOGV("%s: group %d matching with %s", __FUNCTION__, group, toString(attributes).c_str());
+ ALOGV("%s: group %d matching with %s index %d",
+ __FUNCTION__, group, toString(attributes).c_str(), index);
status_t status = NO_ERROR;
IVolumeCurves &curves = getVolumeCurves(attributes);
VolumeSource vs = toVolumeSource(group);
@@ -3342,6 +3343,21 @@
status = volStatus;
}
}
+
+ // update voice volume if the an active call route exists
+ if (mCallRxSourceClient != nullptr && mCallRxSourceClient->isConnected()
+ && (curSrcDevices.find(
+ Volume::getDeviceForVolume({mCallRxSourceClient->sinkDevice()->type()}))
+ != curSrcDevices.end())) {
+ bool isVoiceVolSrc;
+ bool isBtScoVolSrc;
+ if (isVolumeConsistentForCalls(vs, {mCallRxSourceClient->sinkDevice()->type()},
+ isVoiceVolSrc, isBtScoVolSrc, __func__)
+ && (isVoiceVolSrc || isBtScoVolSrc)) {
+ setVoiceVolume(index, curves, isVoiceVolSrc, 0);
+ }
+ }
+
mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
return status;
}
@@ -3638,9 +3654,13 @@
outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
inputConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
rSubmixModule->addOutputProfile(address.c_str(), &outputConfig,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address);
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address,
+ audio_is_linear_pcm(outputConfig.format)
+ ? AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT);
rSubmixModule->addInputProfile(address.c_str(), &inputConfig,
- AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, address,
+ audio_is_linear_pcm(inputConfig.format)
+ ? AUDIO_INPUT_FLAG_NONE : AUDIO_INPUT_FLAG_DIRECT);
if ((res = setDeviceConnectionStateInt(deviceTypeToMakeAvailable,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
@@ -4397,8 +4417,8 @@
if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) {
continue;
}
- if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
- != AUDIO_OUTPUT_FLAG_NONE) {
+ if (offloadPossible && ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ != AUDIO_OUTPUT_FLAG_NONE)) {
if ((directMode & AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED)
!= AUDIO_DIRECT_NOT_SUPPORTED) {
// Already reports offload gapless supported. No need to report offload support.
@@ -7811,26 +7831,16 @@
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR;
}
- VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
- VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
- bool isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
- bool isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (btScoVolSrc == volumeSource);
- bool isScoRequested = isScoRequestedForComm();
- bool isHAUsed = isHearingAidUsedForComm();
-
- // do not change in call volume if bluetooth is connected and vice versa
- // if sco and call follow same curves, bypass forceUseForComm
- if ((callVolSrc != btScoVolSrc) &&
- ((isVoiceVolSrc && isScoRequested) ||
- (isBtScoVolSrc && !(isScoRequested || isHAUsed))) &&
- !isSingleDeviceType(deviceTypes, AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
- ALOGV("%s cannot set volume group %d volume when is%srequested for comm", __func__,
- volumeSource, isScoRequested ? " " : " not ");
+ bool isVoiceVolSrc;
+ bool isBtScoVolSrc;
+ if (!isVolumeConsistentForCalls(
+ volumeSource, deviceTypes, isVoiceVolSrc, isBtScoVolSrc, __func__)) {
// Do not return an error here as AudioService will always set both voice call
- // and bluetooth SCO volumes due to stream aliasing.
+ // and Bluetooth SCO volumes due to stream aliasing.
return NO_ERROR;
}
+
if (deviceTypes.empty()) {
deviceTypes = outputDesc->devices().types();
index = curves.getVolumeIndex(deviceTypes);
@@ -7855,21 +7865,51 @@
deviceTypes, delayMs, force, isVoiceVolSrc);
if (outputDesc == mPrimaryOutput && (isVoiceVolSrc || isBtScoVolSrc)) {
- float voiceVolume;
- // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
- if (isVoiceVolSrc) {
- voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
- } else {
- voiceVolume = index == 0 ? 0.0 : 1.0;
- }
- if (voiceVolume != mLastVoiceVolume) {
- mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
- mLastVoiceVolume = voiceVolume;
- }
+ setVoiceVolume(index, curves, isVoiceVolSrc, delayMs);
}
return NO_ERROR;
}
+void AudioPolicyManager::setVoiceVolume(
+ int index, IVolumeCurves &curves, bool isVoiceVolSrc, int delayMs) {
+ float voiceVolume;
+ // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed
+ // by the headset
+ if (isVoiceVolSrc) {
+ voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
+ } else {
+ voiceVolume = index == 0 ? 0.0 : 1.0;
+ }
+ if (voiceVolume != mLastVoiceVolume) {
+ mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+ mLastVoiceVolume = voiceVolume;
+ }
+}
+
+bool AudioPolicyManager::isVolumeConsistentForCalls(VolumeSource volumeSource,
+ const DeviceTypeSet& deviceTypes,
+ bool& isVoiceVolSrc,
+ bool& isBtScoVolSrc,
+ const char* caller) {
+ const VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
+ const VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
+ const bool isScoRequested = isScoRequestedForComm();
+ const bool isHAUsed = isHearingAidUsedForComm();
+
+ isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
+ isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (btScoVolSrc == volumeSource);
+
+ if ((callVolSrc != btScoVolSrc) &&
+ ((isVoiceVolSrc && isScoRequested) ||
+ (isBtScoVolSrc && !(isScoRequested || isHAUsed))) &&
+ !isSingleDeviceType(deviceTypes, AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
+ ALOGV("%s cannot set volume group %d volume when is%srequested for comm", caller,
+ volumeSource, isScoRequested ? " " : " not ");
+ return false;
+ }
+ return true;
+}
+
void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
const DeviceTypeSet& deviceTypes,
int delayMs,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 5b9f6ad..8f8550c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -575,6 +575,20 @@
DeviceTypeSet deviceTypes,
int delayMs = 0, bool force = false);
+ void setVoiceVolume(int index, IVolumeCurves &curves, bool isVoiceVolSrc, int delayMs);
+
+ // returns true if the supplied set of volume source and devices are consistent with
+ // call volume rules:
+ // if Bluetooth SCO and voice call use different volume curves:
+ // - do not apply voice call volume if Bluetooth SCO is used for call
+ // - do not apply Bluetooth SCO volume if SCO or Hearing Aid is not used for call.
+ // Also updates the booleans isVoiceVolSrc and isBtScoVolSrc according to the
+ // volume source supplied.
+ bool isVolumeConsistentForCalls(VolumeSource volumeSource,
+ const DeviceTypeSet& deviceTypes,
+ bool& isVoiceVolSrc,
+ bool& isBtScoVolSrc,
+ const char* caller);
// apply all stream volumes to the specified output and device
void applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
const DeviceTypeSet& deviceTypes,
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 7584632..6de71a3 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -164,6 +164,8 @@
status_t status = af->openInput(request, &response);
if (status == OK) {
*input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_module_handle_t(response.input));
+ *config = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioConfig_audio_config_t(response.config, true /*isInput*/));
}
return status;
}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 31ac392..ae5e9a1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -117,6 +117,10 @@
android_atomic_write(level, &gLogLevel);
}
+int32_t format_as(CameraService::StatusInternal s) {
+ return fmt::underlying(s);
+}
+
// ----------------------------------------------------------------------------
static const std::string sDumpPermission("android.permission.DUMP");
@@ -502,8 +506,8 @@
}
if (newStatus == StatusInternal::NOT_PRESENT) {
- logDeviceRemoved(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
- newStatus));
+ logDeviceRemoved(cameraId, fmt::format("Device status changed from {} to {}",
+ oldStatus, newStatus));
// Set the device status to NOT_PRESENT, clients will no longer be able to connect
// to this device until the status changes
@@ -529,8 +533,8 @@
removeStates(cameraId);
} else {
if (oldStatus == StatusInternal::NOT_PRESENT) {
- logDeviceAdded(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
- newStatus));
+ logDeviceAdded(cameraId, fmt::format("Device status changed from {} to {}",
+ oldStatus, newStatus));
}
updateStatus(newStatus, cameraId);
}
@@ -570,9 +574,9 @@
if (updated) {
std::string idCombo = id + " : " + physicalId;
if (newStatus == StatusInternal::PRESENT) {
- logDeviceAdded(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+ logDeviceAdded(idCombo, fmt::format("Device status changed to {}", newStatus));
} else {
- logDeviceRemoved(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+ logDeviceRemoved(idCombo, fmt::format("Device status changed to {}", newStatus));
}
// Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 68f7f73..72831d5 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -644,6 +644,8 @@
UNKNOWN = static_cast<int32_t>(hardware::ICameraServiceListener::STATUS_UNKNOWN)
};
+ friend int32_t format_as(StatusInternal s);
+
/**
* Container class for the state of each logical camera device, including: ID, status, and
* dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index c27fc90..702d476 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -999,7 +999,7 @@
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
"Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
mCameraIdStr.c_str(), streamInfo.width, streamInfo.height, streamInfo.format,
- streamInfo.dataSpace, strerror(-err), err);
+ static_cast<int>(streamInfo.dataSpace), strerror(-err), err);
} else {
int i = 0;
for (auto& binder : binders) {
@@ -1096,7 +1096,8 @@
if (err != OK) {
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
"Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
- mCameraIdStr.c_str(), width, height, format, dataSpace, strerror(-err), err);
+ mCameraIdStr.c_str(), width, height, format, static_cast<int>(dataSpace),
+ strerror(-err), err);
} else {
// Can not add streamId to mStreamMap here, as the surface is deferred. Add it to
// a separate list to track. Once the deferred surface is set, this id will be
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ee4d855..de3fe97 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3586,6 +3586,7 @@
captureRequest->mRotateAndCropChanged = (mComposerOutput && !mOverrideToPortrait) ? false :
overrideAutoRotateAndCrop(captureRequest);
captureRequest->mAutoframingChanged = overrideAutoframing(captureRequest);
+ captureRequest->mTestPatternChanged = overrideTestPattern(captureRequest);
}
// 'mNextRequests' will at this point contain either a set of HFR batched requests
@@ -3732,7 +3733,6 @@
bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
mPrevTriggers = triggerCount;
- bool testPatternChanged = overrideTestPattern(captureRequest);
bool settingsOverrideChanged = overrideSettingsOverride(captureRequest);
// If the request is the same as last, or we had triggers now or last time or
@@ -3741,7 +3741,7 @@
(mPrevRequest != captureRequest || triggersMixedIn ||
captureRequest->mRotateAndCropChanged ||
captureRequest->mAutoframingChanged ||
- testPatternChanged || settingsOverrideChanged) &&
+ captureRequest->mTestPatternChanged || settingsOverrideChanged) &&
// Request settings are all the same within one batch, so only treat the first
// request in a batch as new
!(batchedRequest && i > 0);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b36a60a..4eed4b5 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -651,6 +651,8 @@
bool mAutoframingAuto;
// Indicates that the auto framing value within 'mSettingsList' was modified
bool mAutoframingChanged = false;
+ // Indicates that the camera test pattern setting is modified
+ bool mTestPatternChanged = false;
// Whether this capture request has its zoom ratio set to 1.0x before
// the framework overrides it for camera HAL consumption.
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index c59138c..152687e 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -84,10 +84,10 @@
status_t res = getEndpointUsage(&consumerUsage);
if (res != OK) consumerUsage = 0;
- lines << fmt::sprintf(" State: %d\n", mState);
+ lines << fmt::sprintf(" State: %d\n", static_cast<int>(mState));
lines << fmt::sprintf(" Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
camera_stream::width, camera_stream::height,
- camera_stream::format, camera_stream::data_space);
+ camera_stream::format, static_cast<int>(camera_stream::data_space));
lines << fmt::sprintf(" Max size: %zu\n", mMaxSize);
lines << fmt::sprintf(" Combined usage: 0x%" PRIx64 ", max HAL buffers: %d\n",
mUsage | consumerUsage, camera_stream::max_buffers);
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index f7257e3..ee64284 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -622,7 +622,7 @@
if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dataSpace != streamInfo.dataSpace) {
std::string msg = fmt::sprintf("Camera %s:Surface dataSpace doesn't match: %d vs %d",
- logicalCameraId.c_str(), dataSpace, streamInfo.dataSpace);
+ logicalCameraId.c_str(), static_cast<int>(dataSpace), static_cast<int>(streamInfo.dataSpace));
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index a2f17c2..506b3bc 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -95,31 +95,6 @@
"android.hidl.memory@1.0",
],
- runtime_libs: [
- "libstagefright_soft_aacdec",
- "libstagefright_soft_aacenc",
- "libstagefright_soft_amrdec",
- "libstagefright_soft_amrnbenc",
- "libstagefright_soft_amrwbenc",
- "libstagefright_soft_avcdec",
- "libstagefright_soft_avcenc",
- "libstagefright_soft_flacdec",
- "libstagefright_soft_flacenc",
- "libstagefright_soft_g711dec",
- "libstagefright_soft_gsmdec",
- "libstagefright_soft_hevcdec",
- "libstagefright_soft_mp3dec",
- "libstagefright_soft_mpeg2dec",
- "libstagefright_soft_mpeg4dec",
- "libstagefright_soft_mpeg4enc",
- "libstagefright_soft_opusdec",
- "libstagefright_soft_rawdec",
- "libstagefright_soft_vorbisdec",
- "libstagefright_soft_vpxdec",
- "libstagefright_soft_vpxenc",
- "libstagefright_softomx_plugin",
- ],
-
// OMX interfaces force this to stay in 32-bit mode;
compile_multilib: "32",
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-riscv64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-riscv64.policy
index a55c3eb..0c6aafd 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-riscv64.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-riscv64.policy
@@ -52,6 +52,9 @@
getdents64: 1
ppoll: 1
+clock_gettime: 1
+pipe2: 1
+
# Required by AddressSanitizer
gettid: 1
sched_yield: 1
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index 73a96e9..7f66859 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -74,10 +74,15 @@
name: "libresourcemanagerservice",
srcs: [
+ "ClientImportanceReclaimPolicy.cpp",
+ "DefaultResourceModel.cpp",
+ "ProcessPriorityReclaimPolicy.cpp",
"ResourceManagerMetrics.cpp",
"ResourceManagerService.cpp",
+ "ResourceManagerServiceNew.cpp",
"ResourceObserverService.cpp",
"ResourceManagerServiceUtils.cpp",
+ "ResourceTracker.cpp",
"ServiceLog.cpp",
"UidObserver.cpp",
@@ -97,6 +102,7 @@
"libstatssocket",
"libprotobuf-cpp-lite",
"libactivitymanager_aidl",
+ "aconfig_mediacodec_flags_c_lib",
],
static_libs: [
diff --git a/services/mediaresourcemanager/ClientImportanceReclaimPolicy.cpp b/services/mediaresourcemanager/ClientImportanceReclaimPolicy.cpp
new file mode 100644
index 0000000..a81b32f
--- /dev/null
+++ b/services/mediaresourcemanager/ClientImportanceReclaimPolicy.cpp
@@ -0,0 +1,88 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ClientImportanceReclaimPolicy"
+#include <utils/Log.h>
+
+#include "ResourceTracker.h"
+#include "ResourceManagerService.h"
+#include "ClientImportanceReclaimPolicy.h"
+
+namespace android {
+
+using aidl::android::media::IResourceManagerClient;
+
+ClientImportanceReclaimPolicy::ClientImportanceReclaimPolicy(
+ const std::shared_ptr<ResourceTracker>& resourceTracker)
+ : mResourceTracker(resourceTracker) {
+}
+
+ClientImportanceReclaimPolicy::~ClientImportanceReclaimPolicy() {
+}
+
+// Find the biggest client from the same process with the lowest importance
+// than that of the requesting client.
+bool ClientImportanceReclaimPolicy::getClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) {
+ pid_t callingPid = reclaimRequestInfo.mCallingPid;
+ int32_t callingImportance = reclaimRequestInfo.mCallingClientImportance;
+ MediaResource::Type type = reclaimRequestInfo.mResources[0].type;
+ MediaResource::SubType subType = reclaimRequestInfo.mResources[0].subType;
+ ClientInfo targetClient;
+ // Look to find the biggest client with lowest importance from the same process that
+ // has the other resources and with the given primary type.
+ bool found = false;
+ MediaResource::SubType primarySubType = subType;
+ for (size_t index = 1; !found && (index < reclaimRequestInfo.mResources.size()); index++) {
+ MediaResource::Type type = reclaimRequestInfo.mResources[index].type;
+ MediaResource::SubType subType = reclaimRequestInfo.mResources[index].subType;
+ found = mResourceTracker->getLeastImportantBiggestClient(
+ callingPid, callingImportance,
+ type, subType, primarySubType,
+ clients, targetClient);
+ }
+ // If no success, then select the biggest client of primary type with lowest importance
+ // from the same process.
+ if (!found) {
+ found = mResourceTracker->getLeastImportantBiggestClient(
+ callingPid, callingImportance,
+ type, subType, MediaResource::SubType::kUnspecifiedSubType,
+ clients, targetClient);
+ }
+ // If we haven't found a client yet, then select the biggest client of different type
+ // with lowest importance from the same process.
+ // This is applicable for codec type only.
+ if (!found) {
+ if (type != MediaResource::Type::kSecureCodec &&
+ type != MediaResource::Type::kNonSecureCodec) {
+ return false;
+ }
+ MediaResourceType otherType = (type == MediaResource::Type::kSecureCodec) ?
+ MediaResource::Type::kNonSecureCodec : MediaResource::Type::kSecureCodec;
+ if (!mResourceTracker->getLeastImportantBiggestClient(
+ callingPid, callingImportance,
+ otherType, subType, MediaResource::SubType::kUnspecifiedSubType,
+ clients, targetClient)) {
+ return false;
+ }
+ }
+ targetClients.emplace_back(targetClient);
+ return true;
+}
+} // namespace android
diff --git a/services/mediaresourcemanager/ClientImportanceReclaimPolicy.h b/services/mediaresourcemanager/ClientImportanceReclaimPolicy.h
new file mode 100644
index 0000000..1a54c7d
--- /dev/null
+++ b/services/mediaresourcemanager/ClientImportanceReclaimPolicy.h
@@ -0,0 +1,64 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_CLIENTIMPORTANCERECLAIMPOLICY_H_
+#define ANDROID_MEDIA_CLIENTIMPORTANCERECLAIMPOLICY_H_
+
+#include <media/MediaResource.h>
+#include "IReclaimPolicy.h"
+
+namespace android {
+
+class ResourceTracker;
+struct ClientInfo;
+
+/*
+ * Implementation of Reclaim Policy based on the client's importance.
+ *
+ * Find the least important (other than that of requesting client) client from the
+ * same process (that is requesting for the resource).
+ * If there are multiple clients with least importance, then pick the biggest
+ * client among them.
+ *
+ */
+class ClientImportanceReclaimPolicy : public IReclaimPolicy {
+public:
+ explicit ClientImportanceReclaimPolicy(const std::shared_ptr<ResourceTracker>& resourceTracker);
+
+ virtual ~ClientImportanceReclaimPolicy();
+
+ /*
+ * Based on the client importance, identify and return the least important client of
+ * the requesting process from the list of given clients that satisfy the resource requested.
+ *
+ * @param[in] reclaimRequestInfo Information about the resource request
+ * @param[in] client List of clients to select from.
+ * @param[out] targetClients Upon success, this will have the list of identified client(s).
+ *
+ * @return true on success, false otherwise
+ */
+ bool getClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) override;
+
+private:
+ std::shared_ptr<ResourceTracker> mResourceTracker;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_CLIENTIMPORTANCERECLAIMPOLICY_H_
diff --git a/services/mediaresourcemanager/DefaultResourceModel.cpp b/services/mediaresourcemanager/DefaultResourceModel.cpp
new file mode 100644
index 0000000..7bad715
--- /dev/null
+++ b/services/mediaresourcemanager/DefaultResourceModel.cpp
@@ -0,0 +1,145 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DefaultResourceModel"
+#include <utils/Log.h>
+
+#include "ResourceManagerServiceUtils.h"
+#include "DefaultResourceModel.h"
+#include "ResourceTracker.h"
+
+namespace android {
+
+DefaultResourceModel::DefaultResourceModel(
+ const std::shared_ptr<ResourceTracker>& resourceTracker,
+ bool supportsMultipleSecureCodecs,
+ bool supportsSecureWithNonSecureCodec)
+ : mSupportsMultipleSecureCodecs(supportsMultipleSecureCodecs),
+ mSupportsSecureWithNonSecureCodec(supportsSecureWithNonSecureCodec),
+ mResourceTracker(resourceTracker) {
+}
+
+DefaultResourceModel::~DefaultResourceModel() {
+}
+
+bool DefaultResourceModel::getAllClients(
+ const ReclaimRequestInfo& reclimRequestInfo,
+ std::vector<ClientInfo>& clients) {
+
+ clients.clear();
+ MediaResourceParcel mediaResource{.type = reclimRequestInfo.mResources[0].type,
+ .subType = reclimRequestInfo.mResources[0].subType};
+ ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid, &mediaResource};
+
+ // Resolve the secure-unsecure codec conflicts if there is any.
+ switch (reclimRequestInfo.mResources[0].type) {
+ case MediaResource::Type::kSecureCodec:
+ // Looking to start a secure codec.
+ // #1. Make sure if multiple secure codecs can coexist
+ if (!mSupportsMultipleSecureCodecs) {
+ if (!mResourceTracker->getNonConflictingClients(resourceRequestInfo, clients)) {
+ // A higher priority process owns an instance of a secure codec.
+ // So this request can't be fulfilled.
+ return false;
+ }
+ }
+ // #2. Make sure a secure codec can coexist if there is an instance
+ // of non-secure codec running already.
+ if (!mSupportsSecureWithNonSecureCodec) {
+ mediaResource.type = MediaResource::Type::kNonSecureCodec;
+ if (!mResourceTracker->getNonConflictingClients(resourceRequestInfo, clients)) {
+ // A higher priority process owns an instance of a non-secure codec.
+ // So this request can't be fulfilled.
+ return false;
+ }
+ }
+ break;
+ case MediaResource::Type::kNonSecureCodec:
+ // Looking to start a non-secure codec.
+ // Make sure a non-secure codec can coexist if there is an instance
+ // of secure codec running already.
+ if (!mSupportsSecureWithNonSecureCodec) {
+ mediaResource.type = MediaResource::Type::kSecureCodec;
+ if (!mResourceTracker->getNonConflictingClients(resourceRequestInfo, clients)) {
+ // A higher priority process owns an instance of a secure codec.
+ // So this request can't be fulfilled.
+ return false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!clients.empty()) {
+ // There is secure/unsecure codec co-existence conflict
+ // and we have only found processes with lower priority holding the
+ // resources. So, all of these need to be reclaimed.
+ return false;
+ }
+
+ // No more resource conflicts.
+ switch (reclimRequestInfo.mResources[0].type) {
+ case MediaResource::Type::kSecureCodec:
+ case MediaResource::Type::kNonSecureCodec:
+ // Handling Codec resource reclaim
+ return getCodecClients(reclimRequestInfo, clients);
+ case MediaResource::Type::kGraphicMemory:
+ case MediaResource::Type::kDrmSession:
+ // Handling DRM and GraphicMemory resource reclaim
+ mediaResource.id = reclimRequestInfo.mResources[0].id;
+ mediaResource.value = reclimRequestInfo.mResources[0].value;
+ return mResourceTracker->getAllClients(resourceRequestInfo, clients);
+ default:
+ break;
+ }
+
+ return !clients.empty();
+}
+
+bool DefaultResourceModel::getCodecClients(
+ const ReclaimRequestInfo& reclimRequestInfo,
+ std::vector<ClientInfo>& clients) {
+ MediaResourceParcel mediaResource;
+ ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid, &mediaResource};
+
+ // 1. Look to find the client(s) with the other resources, for the given
+ // primary type.
+ MediaResource::SubType primarySubType = reclimRequestInfo.mResources[0].subType;
+ for (size_t index = 1; index < reclimRequestInfo.mResources.size(); index++) {
+ mediaResource.type = reclimRequestInfo.mResources[index].type;
+ mediaResource.subType = reclimRequestInfo.mResources[index].subType;
+ mResourceTracker->getAllClients(resourceRequestInfo, clients, primarySubType);
+ }
+
+ // 2. Get all clients of the same type.
+ mediaResource.type = reclimRequestInfo.mResources[0].type;
+ mediaResource.subType = reclimRequestInfo.mResources[0].subType;
+ mResourceTracker->getAllClients(resourceRequestInfo, clients);
+
+ // 3. Get all cliends of the different type.
+ MediaResourceType otherType =
+ (reclimRequestInfo.mResources[0].type == MediaResource::Type::kSecureCodec) ?
+ MediaResource::Type::kNonSecureCodec : MediaResource::Type::kSecureCodec;
+ mediaResource.type = otherType;
+ mResourceTracker->getAllClients(resourceRequestInfo, clients);
+
+ return !clients.empty();
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/DefaultResourceModel.h b/services/mediaresourcemanager/DefaultResourceModel.h
new file mode 100644
index 0000000..1891eda
--- /dev/null
+++ b/services/mediaresourcemanager/DefaultResourceModel.h
@@ -0,0 +1,73 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_DEFAULTRESOURCEMODEL_H_
+#define ANDROID_MEDIA_DEFAULTRESOURCEMODEL_H_
+
+#include "IResourceModel.h"
+
+namespace android {
+
+class ResourceTracker;
+
+/*
+ * Implements the Default Resource Model that handles:
+ * - coexistence of secure codec with another secure/non-secure codecs
+ * - sharing resources among other codecs
+ */
+class DefaultResourceModel : public IResourceModel {
+public:
+ DefaultResourceModel(const std::shared_ptr<ResourceTracker>& resourceTracker,
+ bool supportsMultipleSecureCodecs = true,
+ bool supportsSecureWithNonSecureCodec = true);
+ virtual ~DefaultResourceModel();
+
+ /*
+ * Set the codec co-existence properties
+ */
+ void config(bool supportsMultipleSecureCodecs, bool supportsSecureWithNonSecureCodec) {
+ mSupportsMultipleSecureCodecs = supportsMultipleSecureCodecs;
+ mSupportsSecureWithNonSecureCodec = supportsSecureWithNonSecureCodec;
+ }
+
+ /*
+ * Get a list of all clients that holds the resources requested.
+ * This implementation uses the ResourceModel to select the clients.
+ *
+ * @param[in] reclaimRequestInfo Information about the Reclaim request
+ * @param[out] cliens The list of clients that hold the resources in question.
+ *
+ * @return true if there aren't any resource conflicts and false otherwise.
+ */
+ bool getAllClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ std::vector<ClientInfo>& clients) override;
+
+protected:
+ bool getCodecClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ std::vector<ClientInfo>& clients);
+
+protected:
+ // Keeping these protected to allow extending this implementation
+ // by other resource models.
+ bool mSupportsMultipleSecureCodecs;
+ bool mSupportsSecureWithNonSecureCodec;
+ std::shared_ptr<ResourceTracker> mResourceTracker;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_DEFAULTRESOURCEMODEL_H_
diff --git a/services/mediaresourcemanager/IReclaimPolicy.h b/services/mediaresourcemanager/IReclaimPolicy.h
new file mode 100644
index 0000000..dfbfc12
--- /dev/null
+++ b/services/mediaresourcemanager/IReclaimPolicy.h
@@ -0,0 +1,58 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_IRECLAIMPOLICY_H_
+#define ANDROID_MEDIA_IRECLAIMPOLICY_H_
+
+#include <memory>
+#include <aidl/android/media/IResourceManagerClient.h>
+
+namespace android {
+
+struct ClientInfo;
+struct ReclaimRequestInfo;
+
+/*
+ * Interface that defines Reclaim Policy.
+ *
+ * This provides an interface to select/identify a client based on a specific
+ * Reclaim policy.
+ */
+class IReclaimPolicy {
+public:
+ IReclaimPolicy() {}
+
+ virtual ~IReclaimPolicy() {}
+
+ /*
+ * Based on the Reclaim policy, identify and return a client from the list
+ * of given clients that satisfy the resource requested.
+ *
+ * @param[in] reclaimRequestInfo Information about the resource request
+ * @param[in] client List of clients to select from.
+ * @param[out] targetClients Upon success, this will have the list of identified client(s).
+ *
+ * @return true on success, false otherwise
+ */
+ virtual bool getClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_IRECLAIMPOLICY_H_
diff --git a/services/mediaresourcemanager/IResourceModel.h b/services/mediaresourcemanager/IResourceModel.h
new file mode 100644
index 0000000..f865f54
--- /dev/null
+++ b/services/mediaresourcemanager/IResourceModel.h
@@ -0,0 +1,67 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_IRESOURCEMODEL_H_
+#define ANDROID_MEDIA_IRESOURCEMODEL_H_
+
+#include <memory>
+#include <vector>
+
+#include <aidl/android/media/IResourceManagerClient.h>
+#include <aidl/android/media/MediaResourceParcel.h>
+
+namespace android {
+
+struct ClientInfo;
+struct ReclaimRequestInfo;
+
+/*
+ * Interface that defines Resource Model.
+ *
+ * This provides an interface that manages the resource model.
+ * The primary functionality of the implementation of this resource model is to:
+ * 1. Define a resource model for a device (or family of devices)
+ * For example (and not limited to):
+ * - Can a secure codec coexist with another secure or unsecured codec?
+ * - How many codecs can coexist?
+ * - Can one type of codecs (for example avc) coexist with another type of codec
+ * (for example hevc) independently? OR are they sharing the common
+ * resource pool?
+ * 2. Provide a list of clients that hold requesting resources.
+ */
+class IResourceModel {
+public:
+ IResourceModel() {}
+
+ virtual ~IResourceModel() {}
+
+ /*
+ * Get a list of all clients that holds the resources requested.
+ * This implementation uses the ResourceModel to select the clients.
+ *
+ * @param[in] reclaimRequestInfo Information about the Reclaim request
+ * @param[out] clients The list of clients that hold the resources in question.
+ *
+ * @return true if there aren't any resource conflicts and false otherwise.
+ */
+ virtual bool getAllClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ std::vector<ClientInfo>& clients) = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_IRESOURCEMODEL_H_
diff --git a/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.cpp b/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.cpp
new file mode 100644
index 0000000..5b776a6
--- /dev/null
+++ b/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.cpp
@@ -0,0 +1,135 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ProcessPriorityReclaimPolicy"
+#include <utils/Log.h>
+
+#include "ResourceTracker.h"
+#include "ResourceManagerService.h"
+#include "ProcessPriorityReclaimPolicy.h"
+
+namespace android {
+
+using aidl::android::media::IResourceManagerClient;
+
+ProcessPriorityReclaimPolicy::ProcessPriorityReclaimPolicy(
+ const std::shared_ptr<ResourceTracker>& resourceTracker)
+ : mResourceTracker(resourceTracker) {
+}
+
+ProcessPriorityReclaimPolicy::~ProcessPriorityReclaimPolicy() {
+}
+
+// Process priority (oom score) based reclaim:
+// - Find a process with lowest priority (than that of calling process).
+// - Find the bigegst client (with required resources) from that process.
+bool ProcessPriorityReclaimPolicy::getClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) {
+ // NOTE: This is the behavior of the existing reclaim policy.
+ // We can alter it to select more than one client to reclaim from, depending
+ // on the reclaim polocy.
+
+ MediaResource::Type type = reclaimRequestInfo.mResources[0].type;
+ MediaResource::SubType subType = reclaimRequestInfo.mResources[0].subType;
+ // Find one client to reclaim the needed resources from.
+ // 1. Get the priority of the (reclaim) requesting process.
+ int callingPid = reclaimRequestInfo.mCallingPid;
+ int callingPriority = -1;
+ if (!mResourceTracker->getPriority(callingPid, &callingPriority)) {
+ ALOGE("%s: can't get process priority for pid %d", __func__, callingPid);
+ return false;
+ }
+
+ ClientInfo clientInfo;
+ // 2 Look to find the biggest client from the lowest priority process that
+ // has the other resources and with the given primary type.
+ bool found = false;
+ int lowestPriority = -1;
+ MediaResource::SubType primarySubType = subType;
+ for (size_t index = 1; !found && (index < reclaimRequestInfo.mResources.size()); index++) {
+ MediaResource::Type type = reclaimRequestInfo.mResources[index].type;
+ MediaResource::SubType subType = reclaimRequestInfo.mResources[index].subType;
+ found = getBiggestClientFromLowestPriority(callingPid, callingPriority,
+ type, subType, primarySubType,
+ clients, clientInfo, lowestPriority);
+ }
+ // 3 If we haven't found a client yet, then select the biggest client of primary type.
+ if (!found) {
+ found = getBiggestClientFromLowestPriority(callingPid, callingPriority,
+ type, subType,
+ MediaResource::SubType::kUnspecifiedSubType,
+ clients, clientInfo, lowestPriority);
+ }
+ // 4 If we haven't found a client yet, then select the biggest client of different type.
+ // This is applicable for code type only.
+ if (!found) {
+ if (type != MediaResource::Type::kSecureCodec &&
+ type != MediaResource::Type::kNonSecureCodec) {
+ return false;
+ }
+ MediaResourceType otherType = (type == MediaResource::Type::kSecureCodec) ?
+ MediaResource::Type::kNonSecureCodec : MediaResource::Type::kSecureCodec;
+ if (!getBiggestClientFromLowestPriority(callingPid, callingPriority,
+ otherType, subType,
+ MediaResource::SubType::kUnspecifiedSubType,
+ clients, clientInfo, lowestPriority)) {
+ return false;
+ }
+ }
+
+ targetClients.emplace_back(clientInfo);
+ ALOGI("%s: CallingProcess(%d:%d) will reclaim from the lowestPriorityProcess(%d:%d)",
+ __func__, callingPid, callingPriority, clientInfo.mPid, lowestPriority);
+
+ return true;
+}
+
+bool ProcessPriorityReclaimPolicy::getBiggestClientFromLowestPriority(
+ pid_t callingPid,
+ int callingPriority,
+ MediaResource::Type type, MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& targetClient,
+ int& lowestPriority) {
+ // 1. Find the lowest priority process among all the clients with the
+ // requested resource type.
+ int lowestPriorityPid = -1;
+ lowestPriority = -1;
+ if (!mResourceTracker->getLowestPriorityPid(type, subType, primarySubType, clients,
+ lowestPriorityPid, lowestPriority)) {
+ ALOGD("%s: can't find a process with lower priority than that of the process[%d:%d]",
+ __func__, callingPid, callingPriority);
+ return false;
+ }
+
+ // 2. Make sure that the priority of the target process is less than
+ // requesting process.
+ if (lowestPriority <= callingPriority) {
+ ALOGD("%s: lowest priority %d vs caller priority %d",
+ __func__, lowestPriority, callingPriority);
+ return false;
+ }
+
+ // 3. Look to find the biggest client from that process for the given resources
+ return mResourceTracker->getBiggestClient(lowestPriorityPid, type, subType,
+ clients, targetClient, primarySubType);
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.h b/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.h
new file mode 100644
index 0000000..77bf7e1
--- /dev/null
+++ b/services/mediaresourcemanager/ProcessPriorityReclaimPolicy.h
@@ -0,0 +1,89 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_PROCESSPRIORITYRECLAIMPOLICY_H_
+#define ANDROID_MEDIA_PROCESSPRIORITYRECLAIMPOLICY_H_
+
+#include <media/MediaResource.h>
+#include "IReclaimPolicy.h"
+
+namespace android {
+
+class ResourceTracker;
+struct ClientInfo;
+
+/*
+ * Implementation of the Reclaim Policy based on the process priority.
+ *
+ * Find the lowest priority process (lower than the calling/requesting process’s priority)
+ * that has the required resources.
+ * From that process, find the biggest client and return the same for reclaiming.
+ * If there is a codec co-existence policy, that is addressed as below:
+ * - if these are any conflicting codecs, reclaim all those conflicting clients.
+ * If no conflicting codecs, the reclaim policy will select a client in the order of:
+ * - Find the biggest client from the lowest priority process that
+ * has the other resources and with the given primary type.
+ * - select the biggest client from the lower priority process that
+ * has the primary type.
+ * - If it's a codec reclaim request, then:
+ * - select the biggest client from the lower priority process that
+ * has the othe type (for example secure for a non-secure and vice versa).
+ */
+class ProcessPriorityReclaimPolicy : public IReclaimPolicy {
+public:
+ ProcessPriorityReclaimPolicy(const std::shared_ptr<ResourceTracker>& resourceTracker);
+
+ virtual ~ProcessPriorityReclaimPolicy();
+
+ /*
+ * Based on the process priority, identify and return a client from the list
+ * of given clients that satisfy the resource requested.
+ *
+ * @param[in] reclaimRequestInfo Information about the resource request
+ * @param[in] client List of clients to select from.
+ * @param[out] targetClients Upon success, this will have the list of identified client(s).
+ *
+ * @return true on success, false otherwise
+ */
+ bool getClients(const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) override;
+
+private:
+
+ // Get the biggest client with the given resources from the given list of clients.
+ // The client should belong to lowest possible priority than that of the
+ // calling/requesting process.
+ // returns true on success, false otherwise
+ //
+ bool getBiggestClientFromLowestPriority(
+ pid_t callingPid,
+ int callingPriority,
+ MediaResource::Type type,
+ MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& targetClient,
+ int& lowestPriority);
+
+private:
+ std::shared_ptr<ResourceTracker> mResourceTracker;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_PROCESSPRIORITYRECLAIMPOLICY_H_
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 1953237..3a02443 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -29,37 +29,60 @@
#include <mediautils/BatteryNotifier.h>
#include <mediautils/ProcessInfo.h>
#include <mediautils/SchedulingPolicyService.h>
+#include <com_android_media_codec_flags.h>
-#include "IMediaResourceMonitor.h"
#include "ResourceManagerMetrics.h"
+#include "ResourceManagerServiceNew.h"
#include "ResourceObserverService.h"
#include "ServiceLog.h"
+namespace CodecFeatureFlags = com::android::media::codec::flags;
+
namespace android {
-static void notifyResourceGranted(int pid, const std::vector<MediaResourceParcel>& resources) {
- static const char* const kServiceName = "media_resource_monitor";
- sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
- if (binder != NULL) {
- sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
- for (size_t i = 0; i < resources.size(); ++i) {
- switch (resources[i].subType) {
- case MediaResource::SubType::kHwAudioCodec:
- case MediaResource::SubType::kSwAudioCodec:
- service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
- break;
- case MediaResource::SubType::kHwVideoCodec:
- case MediaResource::SubType::kSwVideoCodec:
- service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
- break;
- case MediaResource::SubType::kHwImageCodec:
- case MediaResource::SubType::kSwImageCodec:
- service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_IMAGE_CODEC);
- break;
- case MediaResource::SubType::kUnspecifiedSubType:
- break;
- }
+void ResourceManagerService::getResourceDump(std::string& resourceLog) const {
+ PidResourceInfosMap mapCopy;
+ std::map<int, int> overridePidMapCopy;
+ {
+ std::scoped_lock lock{mLock};
+ mapCopy = mMap; // Shadow copy, real copy will happen on write.
+ overridePidMapCopy = mOverridePidMap;
+ }
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ resourceLog.append(" Processes:\n");
+ for (const auto& [pid, infos] : mapCopy) {
+ snprintf(buffer, SIZE, " Pid: %d\n", pid);
+ resourceLog.append(buffer);
+ int priority = 0;
+ if (getPriority_l(pid, &priority)) {
+ snprintf(buffer, SIZE, " Priority: %d\n", priority);
+ } else {
+ snprintf(buffer, SIZE, " Priority: <unknown>\n");
}
+ resourceLog.append(buffer);
+
+ for (const auto& [infoKey, info] : infos) {
+ resourceLog.append(" Client:\n");
+ snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId);
+ resourceLog.append(buffer);
+
+ std::string clientName = info.name;
+ snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
+ resourceLog.append(buffer);
+
+ const ResourceList& resources = info.resources;
+ resourceLog.append(" Resources:\n");
+ resourceLog.append(resources.toString());
+ }
+ }
+
+ resourceLog.append(" Process Pid override:\n");
+ for (auto it = overridePidMapCopy.begin(); it != overridePidMapCopy.end(); ++it) {
+ snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n",
+ it->first, it->second);
+ resourceLog.append(buffer);
}
}
@@ -75,20 +98,20 @@
return PERMISSION_DENIED;
}
- PidResourceInfosMap mapCopy;
bool supportsMultipleSecureCodecs;
bool supportsSecureWithNonSecureCodec;
- std::map<int, int> overridePidMapCopy;
String8 serviceLog;
{
std::scoped_lock lock{mLock};
- mapCopy = mMap; // Shadow copy, real copy will happen on write.
supportsMultipleSecureCodecs = mSupportsMultipleSecureCodecs;
supportsSecureWithNonSecureCodec = mSupportsSecureWithNonSecureCodec;
serviceLog = mServiceLog->toString(" " /* linePrefix */);
- overridePidMapCopy = mOverridePidMap;
}
+ // Get all the resource (and overload pid) logs
+ std::string resourceLog;
+ getResourceDump(resourceLog);
+
const size_t SIZE = 256;
char buffer[SIZE];
snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
@@ -100,41 +123,8 @@
supportsSecureWithNonSecureCodec);
result.append(buffer);
- result.append(" Processes:\n");
- for (const auto& [pid, infos] : mapCopy) {
- snprintf(buffer, SIZE, " Pid: %d\n", pid);
- result.append(buffer);
- int priority = 0;
- if (getPriority_l(pid, &priority)) {
- snprintf(buffer, SIZE, " Priority: %d\n", priority);
- } else {
- snprintf(buffer, SIZE, " Priority: <unknown>\n");
- }
- result.append(buffer);
+ result.append(resourceLog.c_str());
- for (const auto& [infoKey, info] : infos) {
- result.append(" Client:\n");
- snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId);
- result.append(buffer);
-
- std::string clientName = info.name;
- snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
- result.append(buffer);
-
- const ResourceList& resources = info.resources;
- result.append(" Resources:\n");
- for (auto it = resources.begin(); it != resources.end(); it++) {
- snprintf(buffer, SIZE, " %s\n", toString(it->second).c_str());
- result.append(buffer);
- }
- }
- }
- result.append(" Process Pid override:\n");
- for (auto it = overridePidMapCopy.begin(); it != overridePidMapCopy.end(); ++it) {
- snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n",
- it->first, it->second);
- result.append(buffer);
- }
result.append(" Events logs (most recent at top):\n");
result.append(serviceLog);
@@ -212,9 +202,35 @@
std::shared_ptr<ResourceManagerService> ResourceManagerService::Create(
const sp<ProcessInfoInterface>& processInfo,
const sp<SystemCallbackInterface>& systemResource) {
- return ::ndk::SharedRefBase::make<ResourceManagerService>(processInfo, systemResource);
+ std::shared_ptr<ResourceManagerService> service = nullptr;
+ // If codec importance feature is on, create the refactored implementation.
+ if (CodecFeatureFlags::codec_importance()) {
+ service = ::ndk::SharedRefBase::make<ResourceManagerServiceNew>(processInfo,
+ systemResource);
+ } else {
+ service = ::ndk::SharedRefBase::make<ResourceManagerService>(processInfo,
+ systemResource);
+ }
+
+ if (service != nullptr) {
+ service->init();
+ }
+
+ return service;
}
+// TEST only function.
+std::shared_ptr<ResourceManagerService> ResourceManagerService::CreateNew(
+ const sp<ProcessInfoInterface>& processInfo,
+ const sp<SystemCallbackInterface>& systemResource) {
+ std::shared_ptr<ResourceManagerService> service =
+ ::ndk::SharedRefBase::make<ResourceManagerServiceNew>(processInfo, systemResource);
+ service->init();
+ return service;
+}
+
+void ResourceManagerService::init() {}
+
ResourceManagerService::~ResourceManagerService() {}
void ResourceManagerService::setObserverService(
@@ -296,31 +312,21 @@
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
- const auto resType = std::tuple(res.type, res.subType, res.id);
if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
ALOGW("Ignoring request to remove negative value of non-drm resource");
continue;
}
- if (info.resources.find(resType) == info.resources.end()) {
- if (res.value <= 0) {
- // We can't init a new entry with negative value, although it's allowed
- // to merge in negative values after the initial add.
- ALOGW("Ignoring request to add new resource entry with value <= 0");
- continue;
- }
+ bool isNewEntry = false;
+ if (!info.resources.add(res, &isNewEntry)) {
+ continue;
+ }
+ if (isNewEntry) {
onFirstAdded(res, info.uid);
- info.resources[resType] = res;
- } else {
- mergeResources(info.resources[resType], res);
}
+
// Add it to the list of added resources for observers.
- auto it = resourceAdded.find(resType);
- if (it == resourceAdded.end()) {
- resourceAdded[resType] = res;
- } else {
- mergeResources(it->second, res);
- }
+ resourceAdded.add(res);
}
if (info.deathNotifier == nullptr && client != nullptr) {
info.deathNotifier = DeathNotifier::Create(
@@ -367,31 +373,22 @@
ResourceList resourceRemoved;
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
- const auto resType = std::tuple(res.type, res.subType, res.id);
if (res.value < 0) {
ALOGW("Ignoring request to remove negative value of resource");
continue;
}
- // ignore if we don't have it
- if (info.resources.find(resType) != info.resources.end()) {
- MediaResourceParcel &resource = info.resources[resType];
+
+ long removedEntryValue = -1;
+ if (info.resources.remove(res, &removedEntryValue)) {
MediaResourceParcel actualRemoved = res;
- if (resource.value > res.value) {
- resource.value -= res.value;
- } else {
+ if (removedEntryValue != -1) {
onLastRemoved(res, info.uid);
- actualRemoved.value = resource.value;
- info.resources.erase(resType);
+ actualRemoved.value = removedEntryValue;
}
// Add it to the list of removed resources for observers.
- auto it = resourceRemoved.find(resType);
- if (it == resourceRemoved.end()) {
- resourceRemoved[resType] = actualRemoved;
- } else {
- mergeResources(it->second, actualRemoved);
- }
+ resourceRemoved.add(actualRemoved);
}
}
if (mObserverService != nullptr && !resourceRemoved.empty()) {
@@ -434,8 +431,8 @@
}
const ResourceInfo& info = foundClient->second;
- for (auto it = info.resources.begin(); it != info.resources.end(); it++) {
- onLastRemoved(it->second, info.uid);
+ for (const MediaResourceParcel& res : info.resources.getResources()) {
+ onLastRemoved(res, info.uid);
}
// Since this client has been removed, update the metrics collector.
@@ -472,114 +469,130 @@
}
}
+bool ResourceManagerService::getTargetClients(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ std::vector<ClientInfo>& targetClients) {
+ int32_t callingPid = clientInfo.pid;
+ std::scoped_lock lock{mLock};
+ if (!mProcessInfo->isPidTrusted(callingPid)) {
+ pid_t actualCallingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using actual calling pid %d", __FUNCTION__,
+ callingPid, actualCallingPid);
+ callingPid = actualCallingPid;
+ }
+ const MediaResourceParcel *secureCodec = NULL;
+ const MediaResourceParcel *nonSecureCodec = NULL;
+ const MediaResourceParcel *graphicMemory = NULL;
+ const MediaResourceParcel *drmSession = NULL;
+ for (size_t i = 0; i < resources.size(); ++i) {
+ switch (resources[i].type) {
+ case MediaResource::Type::kSecureCodec:
+ secureCodec = &resources[i];
+ break;
+ case MediaResource::Type::kNonSecureCodec:
+ nonSecureCodec = &resources[i];
+ break;
+ case MediaResource::Type::kGraphicMemory:
+ graphicMemory = &resources[i];
+ break;
+ case MediaResource::Type::kDrmSession:
+ drmSession = &resources[i];
+ break;
+ default:
+ break;
+ }
+ }
+
+ // first pass to handle secure/non-secure codec conflict
+ if (secureCodec != NULL) {
+ MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
+ .subType = secureCodec->subType};
+ ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
+ if (!mSupportsMultipleSecureCodecs) {
+ if (!getAllClients_l(resourceRequestInfo, targetClients)) {
+ return false;
+ }
+ }
+ if (!mSupportsSecureWithNonSecureCodec) {
+ mediaResource.type = MediaResource::Type::kNonSecureCodec;
+ if (!getAllClients_l(resourceRequestInfo, targetClients)) {
+ return false;
+ }
+ }
+ }
+ if (nonSecureCodec != NULL) {
+ if (!mSupportsSecureWithNonSecureCodec) {
+ MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
+ .subType = nonSecureCodec->subType};
+ ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
+ if (!getAllClients_l(resourceRequestInfo, targetClients)) {
+ return false;
+ }
+ }
+ }
+
+ if (drmSession != NULL) {
+ ResourceRequestInfo resourceRequestInfo{callingPid, drmSession};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ if (targetClients.size() == 0) {
+ return false;
+ }
+ }
+
+ if (targetClients.size() == 0 && graphicMemory != nullptr) {
+ // if no secure/non-secure codec conflict, run second pass to handle other resources.
+ ResourceRequestInfo resourceRequestInfo{callingPid, graphicMemory};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ }
+
+ if (targetClients.size() == 0) {
+ // if we are here, run the third pass to free one codec with the same type.
+ if (secureCodec != nullptr) {
+ ResourceRequestInfo resourceRequestInfo{callingPid, secureCodec};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ }
+ if (nonSecureCodec != nullptr) {
+ ResourceRequestInfo resourceRequestInfo{callingPid, nonSecureCodec};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ }
+ }
+
+ if (targetClients.size() == 0) {
+ // if we are here, run the fourth pass to free one codec with the different type.
+ if (secureCodec != nullptr) {
+ MediaResource temp(MediaResource::Type::kNonSecureCodec, secureCodec->subType, 1);
+ ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ }
+ if (nonSecureCodec != nullptr) {
+ MediaResource temp(MediaResource::Type::kSecureCodec, nonSecureCodec->subType, 1);
+ ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
+ getClientForResource_l(resourceRequestInfo, targetClients);
+ }
+ }
+
+ return !targetClients.empty();
+}
+
Status ResourceManagerService::reclaimResource(const ClientInfoParcel& clientInfo,
const std::vector<MediaResourceParcel>& resources, bool* _aidl_return) {
- int32_t callingPid = clientInfo.pid;
std::string clientName = clientInfo.name;
String8 log = String8::format("reclaimResource(callingPid %d, uid %d resources %s)",
- callingPid, clientInfo.uid, getString(resources).c_str());
+ clientInfo.pid, clientInfo.uid, getString(resources).c_str());
mServiceLog->add(log);
*_aidl_return = false;
+ // Check if there are any resources to be reclaimed before processing.
+ if (resources.empty()) {
+ return Status::ok();
+ }
+
std::vector<ClientInfo> targetClients;
- {
- std::scoped_lock lock{mLock};
- if (!mProcessInfo->isPidTrusted(callingPid)) {
- pid_t actualCallingPid = IPCThreadState::self()->getCallingPid();
- ALOGW("%s called with untrusted pid %d, using actual calling pid %d", __FUNCTION__,
- callingPid, actualCallingPid);
- callingPid = actualCallingPid;
- }
- const MediaResourceParcel *secureCodec = NULL;
- const MediaResourceParcel *nonSecureCodec = NULL;
- const MediaResourceParcel *graphicMemory = NULL;
- const MediaResourceParcel *drmSession = NULL;
- for (size_t i = 0; i < resources.size(); ++i) {
- switch (resources[i].type) {
- case MediaResource::Type::kSecureCodec:
- secureCodec = &resources[i];
- break;
- case MediaResource::Type::kNonSecureCodec:
- nonSecureCodec = &resources[i];
- break;
- case MediaResource::Type::kGraphicMemory:
- graphicMemory = &resources[i];
- break;
- case MediaResource::Type::kDrmSession:
- drmSession = &resources[i];
- break;
- default:
- break;
- }
- }
-
- // first pass to handle secure/non-secure codec conflict
- if (secureCodec != NULL) {
- MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
- .subType = secureCodec->subType};
- ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
- if (!mSupportsMultipleSecureCodecs) {
- if (!getAllClients_l(resourceRequestInfo, targetClients)) {
- return Status::ok();
- }
- }
- if (!mSupportsSecureWithNonSecureCodec) {
- mediaResource.type = MediaResource::Type::kNonSecureCodec;
- if (!getAllClients_l(resourceRequestInfo, targetClients)) {
- return Status::ok();
- }
- }
- }
- if (nonSecureCodec != NULL) {
- if (!mSupportsSecureWithNonSecureCodec) {
- MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
- .subType = nonSecureCodec->subType};
- ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
- if (!getAllClients_l(resourceRequestInfo, targetClients)) {
- return Status::ok();
- }
- }
- }
-
- if (drmSession != NULL) {
- ResourceRequestInfo resourceRequestInfo{callingPid, drmSession};
- getClientForResource_l(resourceRequestInfo, targetClients);
- if (targetClients.size() == 0) {
- return Status::ok();
- }
- }
-
- if (targetClients.size() == 0 && graphicMemory != nullptr) {
- // if no secure/non-secure codec conflict, run second pass to handle other resources.
- ResourceRequestInfo resourceRequestInfo{callingPid, graphicMemory};
- getClientForResource_l(resourceRequestInfo, targetClients);
- }
-
- if (targetClients.size() == 0) {
- // if we are here, run the third pass to free one codec with the same type.
- if (secureCodec != nullptr) {
- ResourceRequestInfo resourceRequestInfo{callingPid, secureCodec};
- getClientForResource_l(resourceRequestInfo, targetClients);
- }
- if (nonSecureCodec != nullptr) {
- ResourceRequestInfo resourceRequestInfo{callingPid, nonSecureCodec};
- getClientForResource_l(resourceRequestInfo, targetClients);
- }
- }
-
- if (targetClients.size() == 0) {
- // if we are here, run the fourth pass to free one codec with the different type.
- if (secureCodec != nullptr) {
- MediaResource temp(MediaResource::Type::kNonSecureCodec, secureCodec->subType, 1);
- ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
- getClientForResource_l(resourceRequestInfo, targetClients);
- }
- if (nonSecureCodec != nullptr) {
- MediaResource temp(MediaResource::Type::kSecureCodec, nonSecureCodec->subType, 1);
- ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
- getClientForResource_l(resourceRequestInfo, targetClients);
- }
- }
+ if (!getTargetClients(clientInfo, resources, targetClients)) {
+ // Nothing to reclaim from.
+ ALOGI("%s: There aren't any clients to reclaim from", __func__);
+ return Status::ok();
}
*_aidl_return = reclaimUnconditionallyFrom(targetClients);
@@ -607,7 +620,7 @@
mResourceManagerMetrics->pushReclaimAtom(clientInfo, priorities, targetClients, reclaimed);
}
-std::shared_ptr<IResourceManagerClient> ResourceManagerService::getClient(
+std::shared_ptr<IResourceManagerClient> ResourceManagerService::getClient_l(
int pid, const int64_t& clientId) const {
std::map<int, ResourceInfos>::const_iterator found = mMap.find(pid);
if (found == mMap.end()) {
@@ -625,7 +638,7 @@
return foundClient->second.client;
}
-bool ResourceManagerService::removeClient(int pid, const int64_t& clientId) {
+bool ResourceManagerService::removeClient_l(int pid, const int64_t& clientId) {
std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
if (found == mMap.end()) {
ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
@@ -652,8 +665,11 @@
int64_t failedClientId = -1;
int32_t failedClientPid = -1;
for (const ClientInfo& targetClient : targetClients) {
- std::shared_ptr<IResourceManagerClient> client = getClient(
- targetClient.mPid, targetClient.mClientId);
+ std::shared_ptr<IResourceManagerClient> client = nullptr;
+ {
+ std::scoped_lock lock{mLock};
+ client = getClient_l(targetClient.mPid, targetClient.mClientId);
+ }
if (client == nullptr) {
// skip already released clients.
continue;
@@ -675,7 +691,7 @@
{
std::scoped_lock lock{mLock};
- bool found = removeClient(failedClientPid, failedClientId);
+ bool found = removeClient_l(failedClientPid, failedClientId);
if (found) {
ALOGW("Failed to reclaim resources from client with pid %d", failedClientPid);
} else {
@@ -686,6 +702,16 @@
return false;
}
+bool ResourceManagerService::overridePid_l(int32_t originalPid, int32_t newPid) {
+ mOverridePidMap.erase(originalPid);
+ if (newPid != -1) {
+ mOverridePidMap.emplace(originalPid, newPid);
+ return true;
+ }
+
+ return false;
+}
+
Status ResourceManagerService::overridePid(int originalPid, int newPid) {
String8 log = String8::format("overridePid(originalPid %d, newPid %d)",
originalPid, newPid);
@@ -705,9 +731,7 @@
{
std::scoped_lock lock{mLock};
- mOverridePidMap.erase(originalPid);
- if (newPid != -1) {
- mOverridePidMap.emplace(originalPid, newPid);
+ if (overridePid_l(originalPid, newPid)) {
mResourceManagerMetrics->addPid(newPid);
}
}
@@ -715,6 +739,29 @@
return Status::ok();
}
+bool ResourceManagerService::overrideProcessInfo_l(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) {
+ removeProcessInfoOverride_l(pid);
+
+ if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
+ // Override value is rejected by ProcessInfo.
+ return false;
+ }
+
+ ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(pid),
+ .uid = 0,
+ .id = 0,
+ .name = "<unknown client>"};
+ auto deathNotifier = DeathNotifier::Create(
+ client, ref<ResourceManagerService>(), clientInfo, true);
+
+ mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
+ return true;
+}
+
Status ResourceManagerService::overrideProcessInfo(
const std::shared_ptr<IResourceManagerClient>& client, int pid, int procState,
int oomScore) {
@@ -735,23 +782,12 @@
}
std::scoped_lock lock{mLock};
- removeProcessInfoOverride_l(pid);
-
- if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
+ if (!overrideProcessInfo_l(client, pid, procState, oomScore)) {
// Override value is rejected by ProcessInfo.
return Status::fromServiceSpecificError(BAD_VALUE);
}
-
- ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(pid),
- .uid = 0,
- .id = 0,
- .name = "<unknown client>"};
- auto deathNotifier = DeathNotifier::Create(
- client, ref<ResourceManagerService>(), clientInfo, true);
-
- mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
-
return Status::ok();
+
}
void ResourceManagerService::removeProcessInfoOverride(int pid) {
@@ -857,11 +893,12 @@
return Status::ok();
}
-bool ResourceManagerService::getPriority_l(int pid, int* priority) {
+bool ResourceManagerService::getPriority_l(int pid, int* priority) const {
int newPid = pid;
- if (mOverridePidMap.find(pid) != mOverridePidMap.end()) {
- newPid = mOverridePidMap[pid];
+ std::map<int, int>::const_iterator found = mOverridePidMap.find(pid);
+ if (found != mOverridePidMap.end()) {
+ newPid = found->second;
ALOGD("getPriority_l: use override pid %d instead original pid %d",
newPid, pid);
}
@@ -1000,8 +1037,7 @@
if (pendingRemovalOnly && !info.pendingRemoval) {
continue;
}
- for (auto it = resources.begin(); it != resources.end(); it++) {
- const MediaResourceParcel &resource = it->second;
+ for (const MediaResourceParcel& resource : resources.getResources()) {
if (hasResourceType(type, subType, resource)) {
if (resource.value > largestValue) {
largestValue = resource.value;
@@ -1053,4 +1089,8 @@
return mResourceManagerMetrics->getCurrentConcurrentPixelCount(pid);
}
+void ResourceManagerService::notifyClientReleased(const ClientInfoParcel& clientInfo) {
+ mResourceManagerMetrics->notifyClientReleased(clientInfo);
+}
+
} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index e22a6b3..dc1600a 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -73,7 +73,8 @@
const sp<SystemCallbackInterface> &systemResource);
virtual ~ResourceManagerService();
- void setObserverService(const std::shared_ptr<ResourceObserverService>& observerService);
+ virtual void setObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService);
// IResourceManagerService interface
Status config(const std::vector<MediaResourcePolicyParcel>& policies) override;
@@ -103,8 +104,6 @@
Status reclaimResourcesFromClientsPendingRemoval(int32_t pid) override;
- Status removeResource(const ClientInfoParcel& clientInfo, bool checkValid);
-
Status notifyClientCreated(const ClientInfoParcel& clientInfo) override;
Status notifyClientStarted(const ClientConfigParcel& clientConfig) override;
@@ -113,88 +112,146 @@
Status notifyClientConfigChanged(const ClientConfigParcel& clientConfig) override;
+protected:
+ // To get notifications when a resource is added for the first time.
+ void onFirstAdded(const MediaResourceParcel& res, uid_t uid);
+ // To get notifications when a resource has been removed at last.
+ void onLastRemoved(const MediaResourceParcel& res, uid_t uid);
+
+ // Reclaims resources from |clients|. Returns true if reclaim succeeded
+ // for all clients.
+ bool reclaimUnconditionallyFrom(const std::vector<ClientInfo>& targetClients);
+
+ // A helper function that returns true if the callingPid has higher priority than pid.
+ // Returns false otherwise.
+ bool isCallingPriorityHigher_l(int callingPid, int pid);
+
+ // To notify the metrics about client being released.
+ void notifyClientReleased(const ClientInfoParcel& clientInfo);
+
+ virtual Status removeResource(const ClientInfoParcel& clientInfo, bool checkValid);
+
private:
friend class ResourceManagerServiceTest;
friend class ResourceManagerServiceTestBase;
friend class DeathNotifier;
friend class OverrideProcessInfoDeathNotifier;
- // Reclaims resources from |clients|. Returns true if reclaim succeeded
- // for all clients.
- bool reclaimUnconditionallyFrom(
- const std::vector<ClientInfo>& targetClients);
-
- // Gets the list of all the clients who own the specified resource type.
- // Returns false if any client belongs to a process with higher priority than the
- // calling process. The clients will remain unchanged if returns false.
- bool getAllClients_l(const ResourceRequestInfo& resourceRequestInfo,
- std::vector<ClientInfo>& clientsInfo);
-
- // Gets the client who owns specified resource type from lowest possible priority process.
- // Returns false if the calling process priority is not higher than the lowest process
- // priority. The client will remain unchanged if returns false.
- bool getLowestPriorityBiggestClient_l(
- const ResourceRequestInfo& resourceRequestInfo,
- ClientInfo& clientInfo);
-
// Gets the client who owns biggest piece of specified resource type from pid.
// Returns false with no change to client if there are no clients holding resources of this
// type.
- bool getBiggestClient_l(int pid, MediaResource::Type type, MediaResource::SubType subType,
+ bool getBiggestClient_l(int pid, MediaResource::Type type,
+ MediaResource::SubType subType,
ClientInfo& clientsInfo,
bool pendingRemovalOnly = false);
- // Same method as above, but with pendingRemovalOnly as true.
+
+ // A helper function that gets the biggest clients of the process pid that
+ // is marked to be (pending) removed and has the needed resources.
bool getBiggestClientPendingRemoval_l(int pid, MediaResource::Type type,
MediaResource::SubType subType,
ClientInfo& clientsInfo);
- // A helper function that returns true if the callingPid has higher priority than pid.
- // Returns false otherwise.
- bool isCallingPriorityHigher_l(int callingPid, int pid);
-
- // A helper function basically calls getLowestPriorityBiggestClient_l and adds
- // the result client to the given Vector.
+ // From the list of clients, pick/select client(s) based on the reclaim policy.
void getClientForResource_l(const ResourceRequestInfo& resourceRequestInfo,
std::vector<ClientInfo>& clientsInfo);
-
- void onFirstAdded(const MediaResourceParcel& res, uid_t uid);
- void onLastRemoved(const MediaResourceParcel& res, uid_t uid);
-
- // Get priority from process's pid
- bool getPriority_l(int pid, int* priority);
-
- void removeProcessInfoOverride(int pid);
-
- void removeProcessInfoOverride_l(int pid);
-
+ // A helper function that pushes Reclaim Atom (for metric collection).
void pushReclaimAtom(const ClientInfoParcel& clientInfo,
const std::vector<ClientInfo>& targetClients,
bool reclaimed);
- // Get the client for given pid and the clientId from the map
- std::shared_ptr<IResourceManagerClient> getClient(int pid, const int64_t& clientId) const;
+ // Remove the override info for the given process
+ void removeProcessInfoOverride_l(int pid);
- // Remove the client for given pid and the clientId from the map
- bool removeClient(int pid, const int64_t& clientId);
+ // Eventually we want to phase out this implementation of IResourceManagerService
+ // (ResourceManagerService) and replace that with the newer implementation
+ // (ResourceManagerServiceNew).
+ // So, marking the following methods as private virtual and for the newer implementation
+ // to override is the easiest way to maintain both implementation.
- // The following utility functions are used only for testing by ResourceManagerServiceTest
+ // Initializes the internal state of the ResourceManagerService
+ virtual void init();
+
+ // Gets the list of all the clients who own the list of specified resource type
+ // and satisfy the resource model and the reclaim policy.
+ virtual bool getTargetClients(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ std::vector<ClientInfo>& targetClients);
+
+ // Gets the list of all the clients who own the specified resource type.
+ // Returns false if any client belongs to a process with higher priority than the
+ // calling process. The clients will remain unchanged if returns false.
+ virtual bool getAllClients_l(const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clientsInfo);
+
+ // Gets the client who owns specified resource type from lowest possible priority process.
+ // Returns false if the calling process priority is not higher than the lowest process
+ // priority. The client will remain unchanged if returns false.
+ virtual bool getLowestPriorityBiggestClient_l(
+ const ResourceRequestInfo& resourceRequestInfo,
+ ClientInfo& clientInfo);
+
+ // override the pid of given process
+ virtual bool overridePid_l(int32_t originalPid, int32_t newPid);
+
+ // override the process info of given process
+ virtual bool overrideProcessInfo_l(const std::shared_ptr<IResourceManagerClient>& client,
+ int pid, int procState, int oomScore);
+
+ // Get priority from process's pid
+ virtual bool getPriority_l(int pid, int* priority) const;
+
// Gets lowest priority process that has the specified resource type.
// Returns false if failed. The output parameters will remain unchanged if failed.
- bool getLowestPriorityPid_l(MediaResource::Type type, MediaResource::SubType subType,
- int* lowestPriorityPid, int* lowestPriority);
+ virtual bool getLowestPriorityPid_l(MediaResource::Type type, MediaResource::SubType subType,
+ int* lowestPriorityPid, int* lowestPriority);
+
+ // Removes the pid from the override map.
+ virtual void removeProcessInfoOverride(int pid);
+
+ // Get the client for given pid and the clientId from the map
+ virtual std::shared_ptr<IResourceManagerClient> getClient_l(
+ int pid, const int64_t& clientId) const;
+
+ // Remove the client for given pid and the clientId from the map
+ virtual bool removeClient_l(int pid, const int64_t& clientId);
+
+ // Get all the resource status for dump
+ virtual void getResourceDump(std::string& resourceLog) const;
+
+ // The following utility functions are used only for testing by ResourceManagerServiceTest
+ // START: TEST only functions
// Get the peak concurrent pixel count (associated with the video codecs) for the process.
long getPeakConcurrentPixelCount(int pid) const;
// Get the current concurrent pixel count (associated with the video codecs) for the process.
long getCurrentConcurrentPixelCount(int pid) const;
+ // To create object of type ResourceManagerServiceNew
+ static std::shared_ptr<ResourceManagerService> CreateNew(
+ const sp<ProcessInfoInterface>& processInfo,
+ const sp<SystemCallbackInterface>& systemResource);
+ // Returns a unmodifiable reference to the internal resource state as a map
+ virtual const std::map<int, ResourceInfos>& getResourceMap() const {
+ return mMap;
+ }
+ // enable/disable process priority based reclaim and client importance based reclaim
+ virtual void setReclaimPolicy(bool processPriority, bool clientImportance) {
+ // Implemented by the refactored/new RMService
+ (void)processPriority;
+ (void)clientImportance;
+ }
+ // END: TEST only functions
+protected:
mutable std::mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<SystemCallbackInterface> mSystemCB;
sp<ServiceLog> mServiceLog;
- PidResourceInfosMap mMap;
bool mSupportsMultipleSecureCodecs;
bool mSupportsSecureWithNonSecureCodec;
int32_t mCpuBoostCount;
+
+private:
+ PidResourceInfosMap mMap;
struct ProcessInfoOverride {
std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
std::shared_ptr<IResourceManagerClient> client;
diff --git a/services/mediaresourcemanager/ResourceManagerServiceNew.cpp b/services/mediaresourcemanager/ResourceManagerServiceNew.cpp
new file mode 100644
index 0000000..af093ca
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceManagerServiceNew.cpp
@@ -0,0 +1,385 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ResourceManagerServiceNew"
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <mediautils/ProcessInfo.h>
+
+#include "DefaultResourceModel.h"
+#include "ClientImportanceReclaimPolicy.h"
+#include "ProcessPriorityReclaimPolicy.h"
+#include "ResourceManagerServiceNew.h"
+#include "ResourceTracker.h"
+#include "ServiceLog.h"
+
+namespace android {
+
+ResourceManagerServiceNew::ResourceManagerServiceNew(
+ const sp<ProcessInfoInterface>& processInfo,
+ const sp<SystemCallbackInterface>& systemResource) :
+ ResourceManagerService(processInfo, systemResource) {}
+
+ResourceManagerServiceNew::~ResourceManagerServiceNew() {}
+
+void ResourceManagerServiceNew::init() {
+ // Create the Resource Tracker
+ mResourceTracker = std::make_shared<ResourceTracker>(ref<ResourceManagerServiceNew>(),
+ mProcessInfo);
+ setUpResourceModels();
+ setUpReclaimPolicies();
+}
+
+void ResourceManagerServiceNew::setUpResourceModels() {
+ std::scoped_lock lock{mLock};
+ // Create/Configure the default resource model.
+ if (mDefaultResourceModel == nullptr) {
+ mDefaultResourceModel = std::make_unique<DefaultResourceModel>(
+ mResourceTracker,
+ mSupportsMultipleSecureCodecs,
+ mSupportsSecureWithNonSecureCodec);
+ } else {
+ DefaultResourceModel* resourceModel =
+ static_cast<DefaultResourceModel*>(mDefaultResourceModel.get());
+ resourceModel->config(mSupportsMultipleSecureCodecs, mSupportsSecureWithNonSecureCodec);
+ }
+}
+
+void ResourceManagerServiceNew::setUpReclaimPolicies() {
+ mReclaimPolicies.clear();
+ // Add Reclaim policies based on:
+ // - the Process priority (oom score)
+ // - the client/codec importance.
+ setReclaimPolicy(true /* processPriority */, true /* clientImportance */);
+}
+
+Status ResourceManagerServiceNew::config(const std::vector<MediaResourcePolicyParcel>& policies) {
+ Status status = ResourceManagerService::config(policies);
+ // Change in the config dictates update to the resource model.
+ setUpResourceModels();
+ return status;
+}
+
+void ResourceManagerServiceNew::setObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService) {
+ ResourceManagerService::setObserverService(observerService);
+ mResourceTracker->setResourceObserverService(observerService);
+}
+
+Status ResourceManagerServiceNew::addResource(
+ const ClientInfoParcel& clientInfo,
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& resources) {
+ int32_t pid = clientInfo.pid;
+ int32_t uid = clientInfo.uid;
+ int64_t clientId = clientInfo.id;
+ String8 log = String8::format("addResource(pid %d, uid %d clientId %lld, resources %s)",
+ pid, uid, (long long) clientId, getString(resources).c_str());
+ mServiceLog->add(log);
+
+ std::scoped_lock lock{mLock};
+ mResourceTracker->addResource(clientInfo, client, resources);
+ notifyResourceGranted(pid, resources);
+
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::removeResource(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources) {
+ int32_t pid = clientInfo.pid;
+ int32_t uid = clientInfo.uid;
+ int64_t clientId = clientInfo.id;
+ String8 log = String8::format("removeResource(pid %d, uid %d clientId %lld, resources %s)",
+ pid, uid, (long long) clientId, getString(resources).c_str());
+ mServiceLog->add(log);
+
+ std::scoped_lock lock{mLock};
+ mResourceTracker->removeResource(clientInfo, resources);
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::removeClient(const ClientInfoParcel& clientInfo) {
+ removeResource(clientInfo, true /*checkValid*/);
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::removeResource(const ClientInfoParcel& clientInfo,
+ bool checkValid) {
+ int32_t pid = clientInfo.pid;
+ int32_t uid = clientInfo.uid;
+ int64_t clientId = clientInfo.id;
+ String8 log = String8::format("removeResource(pid %d, uid %d clientId %lld)",
+ pid, uid, (long long) clientId);
+ mServiceLog->add(log);
+
+ std::scoped_lock lock{mLock};
+ if (mResourceTracker->removeResource(clientInfo, checkValid)) {
+ notifyClientReleased(clientInfo);
+ }
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::reclaimResource(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ bool* _aidl_return) {
+ return ResourceManagerService::reclaimResource(clientInfo, resources, _aidl_return);
+}
+
+bool ResourceManagerServiceNew::overridePid_l(int32_t originalPid, int32_t newPid) {
+ return mResourceTracker->overridePid(originalPid, newPid);
+}
+
+Status ResourceManagerServiceNew::overridePid(int originalPid, int newPid) {
+ return ResourceManagerService::overridePid(originalPid, newPid);
+}
+
+bool ResourceManagerServiceNew::overrideProcessInfo_l(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) {
+ return mResourceTracker->overrideProcessInfo(client, pid, procState, oomScore);
+}
+
+Status ResourceManagerServiceNew::overrideProcessInfo(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) {
+ return ResourceManagerService::overrideProcessInfo(client, pid, procState, oomScore);
+}
+
+void ResourceManagerServiceNew::removeProcessInfoOverride(int pid) {
+ std::scoped_lock lock{mLock};
+
+ mResourceTracker->removeProcessInfoOverride(pid);
+}
+
+Status ResourceManagerServiceNew::markClientForPendingRemoval(const ClientInfoParcel& clientInfo) {
+ int32_t pid = clientInfo.pid;
+ int64_t clientId = clientInfo.id;
+ String8 log = String8::format(
+ "markClientForPendingRemoval(pid %d, clientId %lld)",
+ pid, (long long) clientId);
+ mServiceLog->add(log);
+
+ std::scoped_lock lock{mLock};
+ mResourceTracker->markClientForPendingRemoval(clientInfo);
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::reclaimResourcesFromClientsPendingRemoval(int32_t pid) {
+ String8 log = String8::format("reclaimResourcesFromClientsPendingRemoval(pid %d)", pid);
+ mServiceLog->add(log);
+
+ std::vector<ClientInfo> targetClients;
+ {
+ std::scoped_lock lock{mLock};
+ mResourceTracker->getClientsMarkedPendingRemoval(pid, targetClients);
+ }
+
+ if (!targetClients.empty()) {
+ reclaimUnconditionallyFrom(targetClients);
+ }
+ return Status::ok();
+}
+
+Status ResourceManagerServiceNew::notifyClientCreated(const ClientInfoParcel& clientInfo) {
+ return ResourceManagerService::notifyClientCreated(clientInfo);
+}
+
+Status ResourceManagerServiceNew::notifyClientStarted(const ClientConfigParcel& clientConfig) {
+ return ResourceManagerService::notifyClientStarted(clientConfig);
+}
+
+Status ResourceManagerServiceNew::notifyClientStopped(const ClientConfigParcel& clientConfig) {
+ return ResourceManagerService::notifyClientStopped(clientConfig);
+}
+
+Status ResourceManagerServiceNew::notifyClientConfigChanged(
+ const ClientConfigParcel& clientConfig) {
+ {
+ // Update the ResourceTracker about the change in the configuration.
+ std::scoped_lock lock{mLock};
+ mResourceTracker->updateResource(clientConfig.clientInfo);
+ }
+ return ResourceManagerService::notifyClientConfigChanged(clientConfig);
+}
+
+void ResourceManagerServiceNew::getResourceDump(std::string& resourceLog) const {
+ std::scoped_lock lock{mLock};
+ mResourceTracker->dump(resourceLog);
+}
+
+binder_status_t ResourceManagerServiceNew::dump(int fd, const char** args, uint32_t numArgs) {
+ return ResourceManagerService::dump(fd, args, numArgs);
+}
+
+bool ResourceManagerServiceNew::getTargetClients(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ std::vector<ClientInfo>& targetClients) {
+ int32_t callingPid = clientInfo.pid;
+ std::scoped_lock lock{mLock};
+ if (!mProcessInfo->isPidTrusted(callingPid)) {
+ pid_t actualCallingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using actual calling pid %d", __FUNCTION__,
+ callingPid, actualCallingPid);
+ callingPid = actualCallingPid;
+ }
+
+ // Use the Resource Model to get a list of all the clients that hold the
+ // needed/requested resources.
+ uint32_t callingImportance = std::max(0, clientInfo.importance);
+ ReclaimRequestInfo reclaimRequestInfo{callingPid, callingImportance, resources};
+ std::vector<ClientInfo> clients;
+ if (!mDefaultResourceModel->getAllClients(reclaimRequestInfo, clients)) {
+ if (clients.empty()) {
+ ALOGI("%s: There aren't any clients with given resources. Nothing to reclaim",
+ __func__);
+ return false;
+ }
+ // Since there was a conflict, we need to reclaim all clients.
+ targetClients = std::move(clients);
+ } else {
+ // Select a client among those have the needed resources.
+ getClientForResource_l(reclaimRequestInfo, clients, targetClients);
+ }
+ return !targetClients.empty();
+}
+
+void ResourceManagerServiceNew::getClientForResource_l(
+ const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients) {
+ int callingPid = reclaimRequestInfo.mCallingPid;
+
+ // Before looking into other processes, check if we have clients marked for
+ // pending removal in the same process.
+ ClientInfo targetClient;
+ for (const MediaResourceParcel& resource : reclaimRequestInfo.mResources) {
+ if (mResourceTracker->getBiggestClientPendingRemoval(callingPid, resource.type,
+ resource.subType, targetClient)) {
+ targetClients.emplace_back(targetClient);
+ return;
+ }
+ }
+
+ // Run through all the reclaim policies until a client to reclaim from is identified.
+ for (std::unique_ptr<IReclaimPolicy>& reclaimPolicy : mReclaimPolicies) {
+ if (reclaimPolicy->getClients(reclaimRequestInfo, clients, targetClients)) {
+ return;
+ }
+ }
+}
+
+bool ResourceManagerServiceNew::getLowestPriorityBiggestClient_l(
+ const ResourceRequestInfo& resourceRequestInfo,
+ ClientInfo& clientInfo) {
+ //NOTE: This function is used only by the test: ResourceManagerServiceTest
+ if (resourceRequestInfo.mResource == nullptr) {
+ return false;
+ }
+
+ // Use the DefaultResourceModel to get all the clients with the resources requested.
+ std::vector<MediaResourceParcel> resources{*resourceRequestInfo.mResource};
+ ReclaimRequestInfo reclaimRequestInfo{resourceRequestInfo.mCallingPid, 0, resources};
+ std::vector<ClientInfo> clients;
+ mDefaultResourceModel->getAllClients(reclaimRequestInfo, clients);
+
+ // Use the ProcessPriorityReclaimPolicy to select a client to reclaim from.
+ std::unique_ptr<IReclaimPolicy> reclaimPolicy
+ = std::make_unique<ProcessPriorityReclaimPolicy>(mResourceTracker);
+ std::vector<ClientInfo> targetClients;
+ if (reclaimPolicy->getClients(reclaimRequestInfo, clients, targetClients)) {
+ if (!targetClients.empty()) {
+ clientInfo = targetClients[0];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ResourceManagerServiceNew::getPriority_l(int pid, int* priority) const {
+ return mResourceTracker->getPriority(pid, priority);
+}
+
+bool ResourceManagerServiceNew::getLowestPriorityPid_l(
+ MediaResource::Type type, MediaResource::SubType subType,
+ int* lowestPriorityPid, int* lowestPriority) {
+ //NOTE: This function is used only by the test: ResourceManagerServiceTest
+ return mResourceTracker->getLowestPriorityPid(type, subType,
+ *lowestPriorityPid,
+ *lowestPriority);
+}
+
+bool ResourceManagerServiceNew::getAllClients_l(
+ const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clientsInfo) {
+ //NOTE: This function is used only by the test: ResourceManagerServiceTest
+ MediaResource::Type type = resourceRequestInfo.mResource->type;
+ // Get the list of all clients that has requested resources.
+ std::vector<ClientInfo> clients;
+ mResourceTracker->getAllClients(resourceRequestInfo, clients);
+
+ // Check is there any high priority process holding up the resources already.
+ for (const ClientInfo& info : clients) {
+ if (!isCallingPriorityHigher_l(resourceRequestInfo.mCallingPid, info.mPid)) {
+ // some higher/equal priority process owns the resource,
+ // this request can't be fulfilled.
+ ALOGE("%s: can't reclaim resource %s from pid %d", __func__, asString(type), info.mPid);
+ return false;
+ }
+ clientsInfo.emplace_back(info);
+ }
+ if (clientsInfo.size() == 0) {
+ ALOGV("%s: didn't find any resource %s", __func__, asString(type));
+ }
+ return true;
+}
+
+std::shared_ptr<IResourceManagerClient> ResourceManagerServiceNew::getClient_l(
+ int pid, const int64_t& clientId) const {
+ return mResourceTracker->getClient(pid, clientId);
+}
+
+bool ResourceManagerServiceNew::removeClient_l(int pid, const int64_t& clientId) {
+ return mResourceTracker->removeClient(pid, clientId);
+}
+
+const std::map<int, ResourceInfos>& ResourceManagerServiceNew::getResourceMap() const {
+ return mResourceTracker->getResourceMap();
+}
+
+void ResourceManagerServiceNew::setReclaimPolicy(bool processPriority, bool clientImportance) {
+ mReclaimPolicies.clear();
+ if (processPriority) {
+ // Process priority (oom score) as the Default reclaim policy.
+ mReclaimPolicies.push_back(std::make_unique<ProcessPriorityReclaimPolicy>(
+ mResourceTracker));
+ }
+ if (clientImportance) {
+ mReclaimPolicies.push_back(std::make_unique<ClientImportanceReclaimPolicy>(
+ mResourceTracker));
+ }
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerServiceNew.h b/services/mediaresourcemanager/ResourceManagerServiceNew.h
new file mode 100644
index 0000000..0599936
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceManagerServiceNew.h
@@ -0,0 +1,174 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_RESOURCEMANAGERSERVICENEW_H
+#define ANDROID_MEDIA_RESOURCEMANAGERSERVICENEW_H
+
+#include "ResourceManagerService.h"
+
+namespace android {
+
+class IReclaimPolicy;
+class IResourceModel;
+class ResourceTracker;
+
+//
+// A newer implementation of IResourceManagerService, which
+// eventually will replace the older implementation in ResourceManagerService.
+//
+// To make the transition easier, this implementation overrides the
+// private virtual methods from ResourceManagerService.
+//
+// This implementation is devised to abstract and integrate:
+// - resources into an independent abstraction
+// - resource model as a separate interface (and implementation)
+// - reclaim policy as a separate interface (and implementation)
+//
+class ResourceManagerServiceNew : public ResourceManagerService {
+public:
+
+ explicit ResourceManagerServiceNew(const sp<ProcessInfoInterface>& processInfo,
+ const sp<SystemCallbackInterface>& systemResource);
+ virtual ~ResourceManagerServiceNew();
+
+ // IResourceManagerService interface
+ Status config(const std::vector<MediaResourcePolicyParcel>& policies) override;
+
+ Status addResource(const ClientInfoParcel& clientInfo,
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& resources) override;
+
+ Status removeResource(const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources) override;
+
+ Status removeClient(const ClientInfoParcel& clientInfo) override;
+
+ Status reclaimResource(const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ bool* _aidl_return) override;
+
+ Status overridePid(int32_t originalPid, int32_t newPid) override;
+
+ Status overrideProcessInfo(const std::shared_ptr<IResourceManagerClient>& client,
+ int32_t pid, int32_t procState, int32_t oomScore) override;
+
+ Status markClientForPendingRemoval(const ClientInfoParcel& clientInfo) override;
+
+ Status reclaimResourcesFromClientsPendingRemoval(int32_t pid) override;
+
+ Status notifyClientCreated(const ClientInfoParcel& clientInfo) override;
+
+ Status notifyClientStarted(const ClientConfigParcel& clientConfig) override;
+
+ Status notifyClientStopped(const ClientConfigParcel& clientConfig) override;
+
+ Status notifyClientConfigChanged(const ClientConfigParcel& clientConfig) override;
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ friend class ResourceTracker;
+
+private:
+
+ // Set up the Resource models.
+ void setUpResourceModels();
+
+ // Set up the Reclaim Policies.
+ void setUpReclaimPolicies();
+
+ // From the list of clients, pick/select client(s) based on the reclaim policy.
+ void getClientForResource_l(
+ const ReclaimRequestInfo& reclaimRequestInfo,
+ const std::vector<ClientInfo>& clients,
+ std::vector<ClientInfo>& targetClients);
+
+ // Initializes the internal state of the ResourceManagerService
+ void init() override;
+
+ void setObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService) override;
+
+ // Gets the list of all the clients who own the specified resource type.
+ // Returns false if any client belongs to a process with higher priority than the
+ // calling process. The clients will remain unchanged if returns false.
+ bool getTargetClients(
+ const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources,
+ std::vector<ClientInfo>& targetClients) override;
+
+ // Removes the pid from the override map.
+ void removeProcessInfoOverride(int pid) override;
+
+ // override the pid of given process
+ bool overridePid_l(int32_t originalPid, int32_t newPid) override;
+
+ // override the process info of given process
+ bool overrideProcessInfo_l(const std::shared_ptr<IResourceManagerClient>& client,
+ int pid, int procState, int oomScore) override;
+
+ // Get priority from process's pid
+ bool getPriority_l(int pid, int* priority) const override;
+
+ // Get the client for given pid and the clientId from the map
+ std::shared_ptr<IResourceManagerClient> getClient_l(
+ int pid, const int64_t& clientId) const override;
+
+ // Remove the client for given pid and the clientId from the map
+ bool removeClient_l(int pid, const int64_t& clientId) override;
+
+ // Get all the resource status for dump
+ void getResourceDump(std::string& resourceLog) const override;
+
+ // Returns a unmodifiable reference to the internal resource state as a map
+ const std::map<int, ResourceInfos>& getResourceMap() const override;
+
+ Status removeResource(const ClientInfoParcel& clientInfo, bool checkValid) override;
+
+ // The following utility functions are used only for testing by ResourceManagerServiceTest
+ // START: TEST only functions
+ // Gets the list of all the clients who own the specified resource type.
+ // Returns false if any client belongs to a process with higher priority than the
+ // calling process. The clients will remain unchanged if returns false.
+ bool getAllClients_l(const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clientsInfo) override;
+
+ // Gets the client who owns specified resource type from lowest possible priority process.
+ // Returns false if the calling process priority is not higher than the lowest process
+ // priority. The client will remain unchanged if returns false.
+ bool getLowestPriorityBiggestClient_l(
+ const ResourceRequestInfo& resourceRequestInfo,
+ ClientInfo& clientInfo) override;
+
+ // Gets lowest priority process that has the specified resource type.
+ // Returns false if failed. The output parameters will remain unchanged if failed.
+ bool getLowestPriorityPid_l(MediaResource::Type type, MediaResource::SubType subType,
+ int* lowestPriorityPid, int* lowestPriority) override;
+
+ // enable/disable process priority based reclaim and client importance based reclaim
+ void setReclaimPolicy(bool processPriority, bool clientImportance) override;
+ // END: TEST only functions
+
+private:
+ std::shared_ptr<ResourceTracker> mResourceTracker;
+ std::unique_ptr<IResourceModel> mDefaultResourceModel;
+ std::vector<std::unique_ptr<IReclaimPolicy>> mReclaimPolicies;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_MEDIA_RESOURCEMANAGERSERVICENEW_H
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
index de682f8..679ab13 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
@@ -19,11 +19,101 @@
#define LOG_TAG "ResourceManagerServiceUtils"
#include <utils/Log.h>
+#include <binder/IServiceManager.h>
+
+#include "IMediaResourceMonitor.h"
#include "ResourceManagerService.h"
#include "ResourceManagerServiceUtils.h"
namespace android {
+bool ResourceList::add(const MediaResourceParcel& res, bool* isNewEntry) {
+ // See if it's an existing entry, if so, merge it.
+ for (MediaResourceParcel& item : mResourceList) {
+ if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
+ // We already have an item. Merge them and return
+ mergeResources(item, res);
+ return true;
+ }
+ }
+
+ // Since we have't found this resource yet, it is a new entry.
+ // We can't init a new entry with negative value, although it's allowed
+ // to merge in negative values after the initial add.
+ if (res.value <= 0) {
+ ALOGW("Ignoring request to add new resource entry with value <= 0");
+ return false;
+ }
+ if (isNewEntry) {
+ *isNewEntry = true;
+ }
+ mResourceList.push_back(res);
+ return true;
+}
+
+void ResourceList::addOrUpdate(const MediaResourceParcel& res) {
+ // See if it's an existing entry, just update the value.
+ for (MediaResourceParcel& item : mResourceList) {
+ if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
+ item.value = res.value;
+ return;
+ }
+ }
+
+ // Add the new entry.
+ mResourceList.push_back(res);
+}
+
+bool ResourceList::remove(const MediaResourceParcel& res, long* removedEntryValue) {
+ // Make sure we have an entry for this resource.
+ for (std::vector<MediaResourceParcel>::iterator it = mResourceList.begin();
+ it != mResourceList.end(); it++) {
+ if (it->type == res.type && it->subType == res.subType && it->id == res.id) {
+ if (it->value > res.value) {
+ // Subtract the resource value by given value.
+ it->value -= res.value;
+ } else {
+ // This entry will be removed.
+ if (removedEntryValue) {
+ *removedEntryValue = it->value;
+ }
+ mResourceList.erase(it);
+ }
+ return true;
+ }
+ }
+
+ // No such entry.
+ return false;
+}
+
+std::string ResourceList::toString() const {
+ std::string str;
+ for (const ::aidl::android::media::MediaResourceParcel& res : mResourceList) {
+ str.append(android::toString(res).c_str());
+ str.append("\n");
+ }
+
+ return std::move(str);
+}
+
+bool ResourceList::operator==(const ResourceList& rhs) const {
+ // Make sure the size is the same.
+ if (mResourceList.size() != rhs.mResourceList.size()) {
+ return false;
+ }
+
+ // Create a set from this object and check for the items from the rhs.
+ std::set<::aidl::android::media::MediaResourceParcel> lhs(
+ mResourceList.begin(), mResourceList.end());
+ for (const ::aidl::android::media::MediaResourceParcel& res : rhs.mResourceList) {
+ if (lhs.find(res) == lhs.end()) {
+ return false;
+ }
+ }
+ return true;
+}
+
// Bunch of utility functions that looks for a specific Resource.
// Check whether a given resource (of type and subtype) is found in given resource parcel.
bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
@@ -50,8 +140,8 @@
// Check whether a given resource (of type and subtype) is found in given resource list.
bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
const ResourceList& resources) {
- for (auto it = resources.begin(); it != resources.end(); it++) {
- if (hasResourceType(type, subType, it->second)) {
+ for (const MediaResourceParcel& res : resources.getResources()) {
+ if (hasResourceType(type, subType, res)) {
return true;
}
}
@@ -88,7 +178,6 @@
const std::shared_ptr<IResourceManagerClient>& client,
ResourceInfos& infos) {
ResourceInfos::iterator found = infos.find(clientInfo.id);
-
if (found == infos.end()) {
ResourceInfo info{.pid = clientInfo.pid,
.uid = static_cast<uid_t>(clientInfo.uid),
@@ -96,7 +185,8 @@
.name = clientInfo.name.empty()? "<unknown client>" : clientInfo.name,
.client = client,
.deathNotifier = nullptr,
- .pendingRemoval = false};
+ .pendingRemoval = false,
+ .importance = static_cast<uint32_t>(std::max(0, clientInfo.importance))};
auto [it, inserted] = infos.emplace(clientInfo.id, info);
found = it;
}
@@ -202,4 +292,30 @@
return deathNotifier;
}
+void notifyResourceGranted(int pid, const std::vector<MediaResourceParcel>& resources) {
+ static const char* const kServiceName = "media_resource_monitor";
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
+ if (binder != NULL) {
+ sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
+ for (size_t i = 0; i < resources.size(); ++i) {
+ switch (resources[i].subType) {
+ case MediaResource::SubType::kHwAudioCodec:
+ case MediaResource::SubType::kSwAudioCodec:
+ service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
+ break;
+ case MediaResource::SubType::kHwVideoCodec:
+ case MediaResource::SubType::kSwVideoCodec:
+ service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
+ break;
+ case MediaResource::SubType::kHwImageCodec:
+ case MediaResource::SubType::kSwImageCodec:
+ service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_IMAGE_CODEC);
+ break;
+ case MediaResource::SubType::kUnspecifiedSubType:
+ break;
+ }
+ }
+ }
+}
+
} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.h b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
index ac1e410..32cb219 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.h
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
@@ -18,6 +18,9 @@
#ifndef ANDROID_MEDIA_RESOURCEMANAGERSERVICEUTILS_H_
#define ANDROID_MEDIA_RESOURCEMANAGERSERVICEUTILS_H_
+#include <map>
+#include <set>
+#include <memory>
#include <vector>
#include <aidl/android/media/BnResourceManagerService.h>
@@ -97,10 +100,47 @@
virtual void binderDied();
};
-// A map of tuple(type, sub-type, id) and the resource parcel.
-typedef std::map<std::tuple<
- MediaResource::Type, MediaResource::SubType, std::vector<uint8_t>>,
- ::aidl::android::media::MediaResourceParcel> ResourceList;
+// Encapsulate Resource List as vector of resources instead of map.
+// Since the number of resource is very limited, maintaining it as
+// std::vector helps with both performance and memory requiremnts.
+struct ResourceList {
+ // Add or Update an entry into ResourceList.
+ // If a new entry is added, isNewEntry will be set to true upon return
+ // returns true on successful update, false otherwise.
+ bool add(const ::aidl::android::media::MediaResourceParcel& res, bool* isNewEntry = nullptr);
+
+ // reduce the resource usage by subtracting the resource value.
+ // If the resource value is 0 after reducing the resource usage,
+ // that entry will be removed and removedEntryValue is set to the
+ // value before it was removed upon return otherwise it will be set to -1.
+ // returns true on successful removal of the resource, false otherwise.
+ bool remove(const ::aidl::android::media::MediaResourceParcel& res,
+ long* removedEntryValue = nullptr);
+
+ // Returns true if there aren't any resource entries.
+ bool empty() const {
+ return mResourceList.empty();
+ }
+
+ // Returns resource list as a non-modifiable vectors
+ const std::vector<::aidl::android::media::MediaResourceParcel>& getResources() const {
+ return mResourceList;
+ }
+
+ // Converts resource list into string format
+ std::string toString() const;
+
+ // BEGIN: Test only function
+ // Check if two resource lists are the same.
+ bool operator==(const ResourceList& rhs) const;
+
+ // Add or Update an entry into ResourceList.
+ void addOrUpdate(const ::aidl::android::media::MediaResourceParcel& res);
+ // END: Test only function
+
+private:
+ std::vector<::aidl::android::media::MediaResourceParcel> mResourceList;
+};
// Encapsulation for Resource Info, that contains
// - pid of the app
@@ -120,6 +160,19 @@
std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
ResourceList resources;
bool pendingRemoval{false};
+ uint32_t importance = 0;
+};
+
+/*
+ * Resource Reclaim request info that encapsulates
+ * - the calling/requesting process pid.
+ * - the calling/requesting client's importance.
+ * - the list of resources requesting (to be reclaimed from others)
+ */
+struct ReclaimRequestInfo {
+ int mCallingPid = -1;
+ uint32_t mCallingClientImportance = 0;
+ const std::vector<::aidl::android::media::MediaResourceParcel>& mResources;
};
/*
@@ -170,7 +223,7 @@
//Check whether a given resource (of type and subtype) is found in given resource parcel.
bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
- const MediaResourceParcel& resource);
+ const ::aidl::android::media::MediaResourceParcel& resource);
//Check whether a given resource (of type and subtype) is found in given resource list.
bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
@@ -193,7 +246,13 @@
ResourceInfos& infos);
// Merge resources from r2 into r1.
-void mergeResources(MediaResourceParcel& r1, const MediaResourceParcel& r2);
+void mergeResources(::aidl::android::media::MediaResourceParcel& r1,
+ const ::aidl::android::media::MediaResourceParcel& r2);
+
+// To notify the media_resource_monitor about the resource being granted.
+void notifyResourceGranted(
+ int pid,
+ const std::vector<::aidl::android::media::MediaResourceParcel>& resources);
} // namespace android
diff --git a/services/mediaresourcemanager/ResourceObserverService.cpp b/services/mediaresourcemanager/ResourceObserverService.cpp
index 72e249f..21e61e9 100644
--- a/services/mediaresourcemanager/ResourceObserverService.cpp
+++ b/services/mediaresourcemanager/ResourceObserverService.cpp
@@ -286,9 +286,9 @@
{
std::scoped_lock lock{mObserverLock};
- for (auto &res : resources) {
+ for (const MediaResourceParcel& res : resources.getResources()) {
// Skip if this resource doesn't map to any observable type.
- MediaObservableType observableType = getObservableType(res.second);
+ MediaObservableType observableType = getObservableType(res);
if (observableType == MediaObservableType::kInvalid) {
continue;
}
@@ -303,9 +303,9 @@
auto calleeIt = calleeList.find(subscriber.first);
if (calleeIt == calleeList.end()) {
calleeList.emplace(subscriber.first, CalleeInfo{
- subscriber.second, {{observableType, res.second.value}}});
+ subscriber.second, {{observableType, res.value}}});
} else {
- calleeIt->second.monitors.push_back({observableType, res.second.value});
+ calleeIt->second.monitors.push_back({observableType, res.value});
}
}
}
diff --git a/services/mediaresourcemanager/ResourceTracker.cpp b/services/mediaresourcemanager/ResourceTracker.cpp
new file mode 100644
index 0000000..22381c3
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceTracker.cpp
@@ -0,0 +1,769 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ResourceTracker"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <mediautils/ProcessInfo.h>
+#include "ResourceTracker.h"
+#include "ResourceManagerServiceNew.h"
+#include "ResourceObserverService.h"
+
+namespace android {
+
+inline bool isHwCodec(MediaResource::SubType subType) {
+ return subType == MediaResource::SubType::kHwImageCodec ||
+ subType == MediaResource::SubType::kHwVideoCodec;
+}
+
+// Check whether a given resource (of type and subtype) is found in given resource list
+// that also has the given Primary SubType.
+static bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
+ const ResourceList& resources, MediaResource::SubType primarySubType) {
+ bool foundResource = false;
+ bool matchedPrimary =
+ (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false;
+ for (const MediaResourceParcel& res : resources.getResources()) {
+ if (hasResourceType(type, subType, res)) {
+ foundResource = true;
+ } else if (res.subType == primarySubType) {
+ matchedPrimary = true;
+ } else if (isHwCodec(res.subType) == isHwCodec(primarySubType)) {
+ matchedPrimary = true;
+ }
+ if (matchedPrimary && foundResource) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// See if the given client is already in the list of clients.
+inline bool contains(const std::vector<ClientInfo>& clients, const int64_t& clientId) {
+ std::vector<ClientInfo>::const_iterator found =
+ std::find_if(clients.begin(), clients.end(),
+ [clientId](const ClientInfo& client) -> bool {
+ return client.mClientId == clientId;
+ });
+
+ return found != clients.end();
+}
+
+
+ResourceTracker::ResourceTracker(const std::shared_ptr<ResourceManagerServiceNew>& service,
+ const sp<ProcessInfoInterface>& processInfo) :
+ mService(service),
+ mProcessInfo(processInfo) {
+}
+
+ResourceTracker::~ResourceTracker() {
+}
+
+void ResourceTracker::setResourceObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService) {
+ mObserverService = observerService;
+}
+
+ResourceInfos& ResourceTracker::getResourceInfosForEdit(int pid) {
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ // new pid
+ ResourceInfos infosForPid;
+ auto [it, inserted] = mMap.emplace(pid, infosForPid);
+ found = it;
+ }
+
+ return found->second;
+}
+
+bool ResourceTracker::addResource(const ClientInfoParcel& clientInfo,
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& resources) {
+ int32_t pid = clientInfo.pid;
+ int32_t uid = clientInfo.uid;
+
+ if (!mProcessInfo->isPidUidTrusted(pid, uid)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGW("%s called with untrusted pid %d or uid %d, using calling pid %d, uid %d",
+ __func__, pid, uid, callingPid, callingUid);
+ pid = callingPid;
+ uid = callingUid;
+ }
+ ResourceInfos& infos = getResourceInfosForEdit(pid);
+ ResourceInfo& info = getResourceInfoForEdit(clientInfo, client, infos);
+ ResourceList resourceAdded;
+
+ for (const MediaResourceParcel& res : resources) {
+ if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
+ ALOGV("%s: Ignoring request to remove negative value of non-drm resource", __func__);
+ continue;
+ }
+ bool isNewEntry = false;
+ if (!info.resources.add(res, &isNewEntry)) {
+ continue;
+ }
+ if (isNewEntry) {
+ onFirstAdded(res, info.uid);
+ }
+
+ // Add it to the list of added resources for observers.
+ resourceAdded.add(res);
+ }
+ if (info.deathNotifier == nullptr && client != nullptr) {
+ info.deathNotifier = DeathNotifier::Create(client, mService, clientInfo);
+ }
+ if (mObserverService != nullptr && !resourceAdded.empty()) {
+ mObserverService->onResourceAdded(uid, pid, resourceAdded);
+ }
+
+ return !resourceAdded.empty();
+}
+
+bool ResourceTracker::updateResource(const aidl::android::media::ClientInfoParcel& clientInfo) {
+ ResourceInfos& infos = getResourceInfosForEdit(clientInfo.pid);
+
+ ResourceInfos::iterator found = infos.find(clientInfo.id);
+ if (found == infos.end()) {
+ return false;
+ }
+ // Update the client importance.
+ found->second.importance = std::max(0, clientInfo.importance);
+ return true;
+}
+
+bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& resources) {
+ int32_t pid = clientInfo.pid;
+ int64_t clientId = clientInfo.id;
+
+ if (!mProcessInfo->isPidTrusted(pid)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
+ pid, callingPid);
+ pid = callingPid;
+ }
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
+ return false;
+ }
+
+ ResourceInfos& infos = found->second;
+ ResourceInfos::iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return false;
+ }
+
+ ResourceInfo& info = foundClient->second;
+ ResourceList resourceRemoved;
+ for (const MediaResourceParcel& res : resources) {
+ if (res.value < 0) {
+ ALOGV("%s: Ignoring request to remove negative value of resource", __func__);
+ continue;
+ }
+
+ long removedEntryValue = -1;
+ if (info.resources.remove(res, &removedEntryValue)) {
+ MediaResourceParcel actualRemoved = res;
+ if (removedEntryValue != -1) {
+ onLastRemoved(res, info.uid);
+ actualRemoved.value = removedEntryValue;
+ }
+
+ // Add it to the list of removed resources for observers.
+ resourceRemoved.add(actualRemoved);
+ }
+ }
+ if (mObserverService != nullptr && !resourceRemoved.empty()) {
+ mObserverService->onResourceRemoved(info.uid, pid, resourceRemoved);
+ }
+ return true;
+}
+
+bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo, bool validateCallingPid) {
+ int32_t pid = clientInfo.pid;
+ int64_t clientId = clientInfo.id;
+
+ if (validateCallingPid && !mProcessInfo->isPidTrusted(pid)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
+ pid, callingPid);
+ pid = callingPid;
+ }
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
+ return false;
+ }
+
+ ResourceInfos& infos = found->second;
+ ResourceInfos::iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return false;
+ }
+
+ const ResourceInfo& info = foundClient->second;
+ for (const MediaResourceParcel& res : info.resources.getResources()) {
+ onLastRemoved(res, info.uid);
+ }
+
+ if (mObserverService != nullptr && !info.resources.empty()) {
+ mObserverService->onResourceRemoved(info.uid, pid, info.resources);
+ }
+
+ infos.erase(foundClient);
+ return true;
+}
+
+std::shared_ptr<IResourceManagerClient> ResourceTracker::getClient(
+ int pid, const int64_t& clientId) const {
+ std::map<int, ResourceInfos>::const_iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
+ return nullptr;
+ }
+
+ const ResourceInfos& infos = found->second;
+ ResourceInfos::const_iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return nullptr;
+ }
+
+ return foundClient->second.client;
+}
+
+bool ResourceTracker::removeClient(int pid, const int64_t& clientId) {
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
+ return false;
+ }
+
+ ResourceInfos& infos = found->second;
+ ResourceInfos::iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return false;
+ }
+
+ infos.erase(foundClient);
+ return true;
+}
+
+bool ResourceTracker::markClientForPendingRemoval(const ClientInfoParcel& clientInfo) {
+ int32_t pid = clientInfo.pid;
+ int64_t clientId = clientInfo.id;
+
+ if (!mProcessInfo->isPidTrusted(pid)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
+ pid, callingPid);
+ pid = callingPid;
+ }
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long)clientId);
+ return false;
+ }
+
+ ResourceInfos& infos = found->second;
+ ResourceInfos::iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return false;
+ }
+
+ ResourceInfo& info = foundClient->second;
+ info.pendingRemoval = true;
+ return true;
+}
+
+bool ResourceTracker::getClientsMarkedPendingRemoval(int32_t pid,
+ std::vector<ClientInfo>& targetClients) {
+ if (!mProcessInfo->isPidTrusted(pid)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid);
+ pid = callingPid;
+ }
+
+ // Go through all the MediaResource types (and corresponding subtypes for
+ // each, if applicable) and see if the process (with given pid) holds any
+ // such resources that are marked as pending removal.
+ // Since the use-case of this function is to get all such resources (pending
+ // removal) and reclaim them all - the order in which we look for the
+ // resource type doesn't matter.
+ for (MediaResource::Type type : {MediaResource::Type::kSecureCodec,
+ MediaResource::Type::kNonSecureCodec,
+ MediaResource::Type::kGraphicMemory,
+ MediaResource::Type::kDrmSession}) {
+ switch (type) {
+ // Codec resources are segregated by audio, video and image domains.
+ case MediaResource::Type::kSecureCodec:
+ case MediaResource::Type::kNonSecureCodec:
+ for (MediaResource::SubType subType : {MediaResource::SubType::kHwAudioCodec,
+ MediaResource::SubType::kSwAudioCodec,
+ MediaResource::SubType::kHwVideoCodec,
+ MediaResource::SubType::kSwVideoCodec,
+ MediaResource::SubType::kHwImageCodec,
+ MediaResource::SubType::kSwImageCodec}) {
+ ClientInfo clientInfo;
+ if (getBiggestClientPendingRemoval(pid, type, subType, clientInfo)) {
+ if (!contains(targetClients, clientInfo.mClientId)) {
+ targetClients.emplace_back(clientInfo);
+ }
+ continue;
+ }
+ }
+ break;
+ // Non-codec resources are shared by audio, video and image codecs (no subtype).
+ default:
+ ClientInfo clientInfo;
+ MediaResource::SubType subType = MediaResource::SubType::kUnspecifiedSubType;
+ if (getBiggestClientPendingRemoval(pid, type, subType, clientInfo)) {
+ if (!contains(targetClients, clientInfo.mClientId)) {
+ targetClients.emplace_back(clientInfo);
+ }
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool ResourceTracker::overridePid(int originalPid, int newPid) {
+ mOverridePidMap.erase(originalPid);
+ if (newPid != -1) {
+ mOverridePidMap.emplace(originalPid, newPid);
+ return true;
+ }
+ return false;
+}
+
+bool ResourceTracker::overrideProcessInfo(const std::shared_ptr<IResourceManagerClient>& client,
+ int pid, int procState, int oomScore) {
+ removeProcessInfoOverride(pid);
+
+ if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
+ // Override value is rejected by ProcessInfo.
+ return false;
+ }
+
+ ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(pid),
+ .uid = 0,
+ .id = 0,
+ .name = "<unknown client>"};
+ std::shared_ptr<DeathNotifier> deathNotifier =
+ DeathNotifier::Create(client, mService, clientInfo, true);
+
+ mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
+
+ return true;
+}
+
+void ResourceTracker::removeProcessInfoOverride(int pid) {
+ auto it = mProcessInfoOverrideMap.find(pid);
+ if (it == mProcessInfoOverrideMap.end()) {
+ return;
+ }
+
+ mProcessInfo->removeProcessInfoOverride(pid);
+ mProcessInfoOverrideMap.erase(pid);
+}
+
+bool ResourceTracker::getAllClients(const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clients,
+ MediaResource::SubType primarySubType) {
+ MediaResource::Type type = resourceRequestInfo.mResource->type;
+ MediaResource::SubType subType = resourceRequestInfo.mResource->subType;
+ bool foundClient = false;
+
+ for (auto& [pid, /* ResourceInfos */ infos] : mMap) {
+ for (auto& [id, /* ResourceInfo */ info] : infos) {
+ if (hasResourceType(type, subType, info.resources, primarySubType)) {
+ if (!contains(clients, info.clientId)) {
+ clients.emplace_back(info.pid, info.uid, info.clientId);
+ foundClient = true;
+ }
+ }
+ }
+ }
+
+ return foundClient;
+}
+
+bool ResourceTracker::getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType,
+ int& lowestPriorityPid, int& lowestPriority) {
+ int pid = -1;
+ int priority = -1;
+ for (auto& [tempPid, /* ResourceInfos */ infos] : mMap) {
+ if (infos.size() == 0) {
+ // no client on this process.
+ continue;
+ }
+ if (!hasResourceType(type, subType, infos)) {
+ // doesn't have the requested resource type
+ continue;
+ }
+ int tempPriority = -1;
+ if (!getPriority(tempPid, &tempPriority)) {
+ ALOGV("%s: can't get priority of pid %d, skipped", __func__, tempPid);
+ // TODO: remove this pid from mMap?
+ continue;
+ }
+ if (pid == -1 || tempPriority > priority) {
+ // initial the value
+ pid = tempPid;
+ priority = tempPriority;
+ }
+ }
+
+ bool success = (pid != -1);
+
+ if (success) {
+ lowestPriorityPid = pid;
+ lowestPriority = priority;
+ }
+ return success;
+}
+
+bool ResourceTracker::getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ int& lowestPriorityPid, int& lowestPriority) {
+ int pid = -1;
+ int priority = -1;
+ for (const ClientInfo& client : clients) {
+ const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId);
+ if (info == nullptr) {
+ continue;
+ }
+ if (!hasResourceType(type, subType, info->resources, primarySubType)) {
+ // doesn't have the requested resource type
+ continue;
+ }
+ int tempPriority = -1;
+ if (!getPriority(client.mPid, &tempPriority)) {
+ ALOGV("%s: can't get priority of pid %d, skipped", __func__, client.mPid);
+ // TODO: remove this pid from mMap?
+ continue;
+ }
+ if (pid == -1 || tempPriority > priority) {
+ // initial the value
+ pid = client.mPid;
+ priority = tempPriority;
+ }
+ }
+
+ bool success = (pid != -1);
+
+ if (success) {
+ lowestPriorityPid = pid;
+ lowestPriority = priority;
+ }
+ return success;
+}
+
+bool ResourceTracker::getBiggestClientPendingRemoval(int pid, MediaResource::Type type,
+ MediaResource::SubType subType,
+ ClientInfo& clientInfo) {
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ return false;
+ }
+
+ uid_t uid = -1;
+ int64_t clientId = -1;
+ uint64_t largestValue = 0;
+ const ResourceInfos& infos = found->second;
+ for (const auto& [id, /* ResourceInfo */ info] : infos) {
+ const ResourceList& resources = info.resources;
+ // Skip if the client is not marked pending removal.
+ if (!info.pendingRemoval) {
+ continue;
+ }
+ for (const MediaResourceParcel& resource : resources.getResources()) {
+ if (hasResourceType(type, subType, resource)) {
+ if (resource.value > largestValue) {
+ largestValue = resource.value;
+ clientId = info.clientId;
+ uid = info.uid;
+ }
+ }
+ }
+ }
+
+ if (clientId == -1) {
+ return false;
+ }
+
+ clientInfo.mPid = pid;
+ clientInfo.mUid = uid;
+ clientInfo.mClientId = clientId;
+ return true;
+}
+
+bool ResourceTracker::getBiggestClient(int targetPid,
+ MediaResource::Type type, MediaResource::SubType subType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& clientInfo,
+ MediaResource::SubType primarySubType) {
+ uid_t uid = -1;
+ int64_t clientId = -1;
+ uint64_t largestValue = 0;
+
+ for (const ClientInfo& client : clients) {
+ // Skip the clients that doesn't belong go the targetPid
+ if (client.mPid != targetPid) {
+ continue;
+ }
+ const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId);
+ if (info == nullptr) {
+ continue;
+ }
+
+ const ResourceList& resources = info->resources;
+ bool matchedPrimary =
+ (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false;
+ for (const MediaResourceParcel& resource : resources.getResources()) {
+ if (resource.subType == primarySubType) {
+ matchedPrimary = true;
+ break;
+ } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) {
+ matchedPrimary = true;
+ break;
+ }
+ }
+ // Primary type doesn't match, skip the client
+ if (!matchedPrimary) {
+ continue;
+ }
+ for (const MediaResourceParcel& resource : resources.getResources()) {
+ if (hasResourceType(type, subType, resource)) {
+ if (resource.value > largestValue) {
+ largestValue = resource.value;
+ clientId = info->clientId;
+ uid = info->uid;
+ }
+ }
+ }
+ }
+
+ if (clientId == -1) {
+ ALOGE("%s: can't find resource type %s and subtype %s for pid %d",
+ __func__, asString(type), asString(subType), targetPid);
+ return false;
+ }
+
+ clientInfo.mPid = targetPid;
+ clientInfo.mUid = uid;
+ clientInfo.mClientId = clientId;
+ return true;
+}
+
+bool ResourceTracker::getLeastImportantBiggestClient(int targetPid, int32_t importance,
+ MediaResource::Type type,
+ MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& clientInfo) {
+ uid_t uid = -1;
+ int64_t clientId = -1;
+ uint64_t largestValue = 0;
+
+ for (const ClientInfo& client : clients) {
+ // Skip the clients that doesn't belong go the targetPid
+ if (client.mPid != targetPid) {
+ continue;
+ }
+ const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId);
+ if (info == nullptr) {
+ continue;
+ }
+
+ // Make sure the importance is lower.
+ if (info->importance <= importance) {
+ continue;
+ }
+ const ResourceList& resources = info->resources;
+ bool matchedPrimary =
+ (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false;
+ for (const MediaResourceParcel& resource : resources.getResources()) {
+ if (resource.subType == primarySubType) {
+ matchedPrimary = true;
+ } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) {
+ matchedPrimary = true;
+ }
+ }
+ // Primary type doesn't match, skip the client
+ if (!matchedPrimary) {
+ continue;
+ }
+ for (const MediaResourceParcel& resource : resources.getResources()) {
+ if (hasResourceType(type, subType, resource)) {
+ if (resource.value > largestValue) {
+ largestValue = resource.value;
+ clientId = info->clientId;
+ uid = info->uid;
+ }
+ }
+ }
+ }
+
+ if (clientId == -1) {
+ ALOGE("%s: can't find resource type %s and subtype %s for pid %d",
+ __func__, asString(type), asString(subType), targetPid);
+ return false;
+ }
+
+ clientInfo.mPid = targetPid;
+ clientInfo.mUid = uid;
+ clientInfo.mClientId = clientId;
+ return true;
+}
+
+void ResourceTracker::dump(std::string& resourceLogs) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ resourceLogs.append(" Processes:\n");
+ for (const auto& [pid, /* ResourceInfos */ infos] : mMap) {
+ snprintf(buffer, SIZE, " Pid: %d\n", pid);
+ resourceLogs.append(buffer);
+ int priority = 0;
+ if (getPriority(pid, &priority)) {
+ snprintf(buffer, SIZE, " Priority: %d\n", priority);
+ } else {
+ snprintf(buffer, SIZE, " Priority: <unknown>\n");
+ }
+ resourceLogs.append(buffer);
+
+ for (const auto& [infoKey, /* ResourceInfo */ info] : infos) {
+ resourceLogs.append(" Client:\n");
+ snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId);
+ resourceLogs.append(buffer);
+
+ std::string clientName = info.name;
+ snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
+ resourceLogs.append(buffer);
+
+ const ResourceList& resources = info.resources;
+ resourceLogs.append(" Resources:\n");
+ resourceLogs.append(resources.toString());
+ }
+ }
+ resourceLogs.append(" Process Pid override:\n");
+ for (const auto& [oldPid, newPid] : mOverridePidMap) {
+ snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n", oldPid, newPid);
+ resourceLogs.append(buffer);
+ }
+}
+
+void ResourceTracker::onFirstAdded(const MediaResourceParcel& resource, uid_t uid) {
+ std::shared_ptr<ResourceManagerServiceNew> service = mService.lock();
+ if (service == nullptr) {
+ ALOGW("%s: ResourceManagerService is invalid!", __func__);
+ return;
+ }
+
+ service->onFirstAdded(resource, uid);
+}
+
+void ResourceTracker::onLastRemoved(const MediaResourceParcel& resource, uid_t uid) {
+ std::shared_ptr<ResourceManagerServiceNew> service = mService.lock();
+ if (service == nullptr) {
+ ALOGW("%s: ResourceManagerService is invalid!", __func__);
+ return;
+ }
+
+ service->onLastRemoved(resource, uid);
+}
+
+bool ResourceTracker::getPriority(int pid, int* priority) {
+ int newPid = pid;
+
+ if (mOverridePidMap.find(pid) != mOverridePidMap.end()) {
+ newPid = mOverridePidMap[pid];
+ ALOGD("%s: use override pid %d instead original pid %d", __func__, newPid, pid);
+ }
+
+ return mProcessInfo->getPriority(newPid, priority);
+}
+
+bool ResourceTracker::getNonConflictingClients(const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clients) {
+ MediaResource::Type type = resourceRequestInfo.mResource->type;
+ MediaResource::SubType subType = resourceRequestInfo.mResource->subType;
+ for (auto& [pid, /* ResourceInfos */ infos] : mMap) {
+ for (const auto& [id, /* ResourceInfo */ info] : infos) {
+ if (hasResourceType(type, subType, info.resources)) {
+ if (!isCallingPriorityHigher(resourceRequestInfo.mCallingPid, pid)) {
+ // some higher/equal priority process owns the resource,
+ // this is a conflict.
+ ALOGE("%s: The resource (%s) request from pid %d is conflicting",
+ __func__, asString(type), pid);
+ clients.clear();
+ return false;
+ } else {
+ if (!contains(clients, info.clientId)) {
+ clients.emplace_back(info.pid, info.uid, info.clientId);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+const ResourceInfo* ResourceTracker::getResourceInfo(int pid, const int64_t& clientId) const {
+ std::map<int, ResourceInfos>::const_iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
+ return nullptr;
+ }
+
+ const ResourceInfos& infos = found->second;
+ ResourceInfos::const_iterator foundClient = infos.find(clientId);
+ if (foundClient == infos.end()) {
+ ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
+ return nullptr;
+ }
+
+ return &foundClient->second;
+}
+
+bool ResourceTracker::isCallingPriorityHigher(int callingPid, int pid) {
+ int callingPidPriority;
+ if (!getPriority(callingPid, &callingPidPriority)) {
+ return false;
+ }
+
+ int priority;
+ if (!getPriority(pid, &priority)) {
+ return false;
+ }
+
+ return (callingPidPriority < priority);
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ResourceTracker.h b/services/mediaresourcemanager/ResourceTracker.h
new file mode 100644
index 0000000..20c904d
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceTracker.h
@@ -0,0 +1,256 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MEDIA_RESOURCETRACKER_H_
+#define ANDROID_MEDIA_RESOURCETRACKER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include <media/MediaResource.h>
+#include <aidl/android/media/ClientInfoParcel.h>
+#include <aidl/android/media/IResourceManagerClient.h>
+#include <aidl/android/media/MediaResourceParcel.h>
+
+#include "ResourceManagerServiceUtils.h"
+
+namespace android {
+
+class DeathNotifier;
+class ResourceManagerServiceNew;
+class ResourceObserverService;
+struct ProcessInfoInterface;
+struct ResourceRequestInfo;
+struct ClientInfo;
+
+/*
+ * ResourceTracker abstracts the resources managed by the ResourceManager.
+ * It keeps track of the resource used by the clients (clientid) and by the process (pid)
+ */
+class ResourceTracker {
+public:
+ ResourceTracker(const std::shared_ptr<ResourceManagerServiceNew>& service,
+ const sp<ProcessInfoInterface>& processInfo);
+ ~ResourceTracker();
+
+ /**
+ * Add or update resources for |clientInfo|.
+ *
+ * If |clientInfo| is not tracked yet, it records its associated |client| and adds
+ * |resources| to the tracked resources. If |clientInfo| is already tracked,
+ * it updates the tracked resources by adding |resources| to them (|client| in
+ * this case is unused and unchecked).
+ *
+ * @param clientInfo Info of the calling client.
+ * @param client Interface for the client.
+ * @param resources An array of resources to be added.
+ *
+ * @return true upon successfully adding/updating the resources, false
+ * otherwise.
+ */
+ bool addResource(const aidl::android::media::ClientInfoParcel& clientInfo,
+ const std::shared_ptr<::aidl::android::media::IResourceManagerClient>& client,
+ const std::vector<::aidl::android::media::MediaResourceParcel>& resources);
+
+ // Update the resource info, if there is any changes.
+ bool updateResource(const aidl::android::media::ClientInfoParcel& clientInfo);
+
+ // Remove a set of resources from the given client.
+ // returns true on success, false otherwise.
+ bool removeResource(const aidl::android::media::ClientInfoParcel& clientInfo,
+ const std::vector<::aidl::android::media::MediaResourceParcel>& resources);
+
+ /**
+ * Remove all resources tracked for |clientInfo|.
+ *
+ * If |validateCallingPid| is true, the (pid of the) calling process is validated that it
+ * is from a trusted process.
+ * Returns true on success (|clientInfo| was tracked and optionally the caller
+ * was a validated trusted process), false otherwise (|clientInfo| was not tracked,
+ * or the caller was not a trusted process)
+ */
+ bool removeResource(const aidl::android::media::ClientInfoParcel& clientInfo,
+ bool validateCallingPid);
+
+ // Mark the client for pending removal.
+ // Such clients are primary candidate for reclaim.
+ // returns true on success, false otherwise.
+ bool markClientForPendingRemoval(const aidl::android::media::ClientInfoParcel& clientInfo);
+
+ // Get a list of clients that belong to process with given pid and are maked to be
+ // pending removal by markClientForPendingRemoval.
+ // returns true on success, false otherwise.
+ bool getClientsMarkedPendingRemoval(int32_t pid, std::vector<ClientInfo>& targetClients);
+
+ // Override the pid of originalPid with newPid
+ // To remove the pid entry from the override list, set newPid as -1
+ // returns true on successful override, false otherwise.
+ bool overridePid(int originalPid, int newPid);
+
+ // Override the process info {state, oom score} of the process with pid.
+ // returns true on success, false otherwise.
+ bool overrideProcessInfo(
+ const std::shared_ptr<aidl::android::media::IResourceManagerClient>& client,
+ int pid, int procState, int oomScore);
+
+ // Remove the overridden process info.
+ void removeProcessInfoOverride(int pid);
+
+ // Find all clients that have given resources.
+ // If applicable, match the primary type too.
+ // The |clients| (list) isn't cleared by this function to allow calling this
+ // function multiple times for different resources.
+ // returns true upon finding at lease one client with the given resource request info,
+ // false otherwise (no clients)
+ bool getAllClients(
+ const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clients,
+ MediaResource::SubType primarySubType = MediaResource::SubType::kUnspecifiedSubType);
+
+ // Look for the lowest priority process with the given resources.
+ // Upon success lowestPriorityPid and lowestPriority are
+ // set accordingly and it returns true.
+ // If there isn't a lower priority process with the given resources, it will return false
+ // with out updating lowestPriorityPid and lowerPriority.
+ bool getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType,
+ int& lowestPriorityPid, int& lowestPriority);
+
+ // Look for the lowest priority process with the given resources
+ // among the given client list.
+ // If applicable, match the primary type too.
+ // returns true on success, false otherwise.
+ bool getLowestPriorityPid(
+ MediaResource::Type type, MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ int& lowestPriorityPid, int& lowestPriority);
+
+ // Find the biggest client of the given process with given resources,
+ // that is marked as pending to be removed.
+ // returns true on success, false otherwise.
+ bool getBiggestClientPendingRemoval(
+ int pid, MediaResource::Type type,
+ MediaResource::SubType subType,
+ ClientInfo& clientInfo);
+
+ // Find the biggest client from the process pid, selecting them from the list of clients.
+ // If applicable, match the primary type too.
+ // Returns true when a client is found and clientInfo is updated accordingly.
+ // Upon failure to find a client, it will return false without updating
+ // clientInfo.
+ // Upon failure to find a client, it will return false.
+ bool getBiggestClient(
+ int targetPid,
+ MediaResource::Type type,
+ MediaResource::SubType subType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& clientInfo,
+ MediaResource::SubType primarySubType = MediaResource::SubType::kUnspecifiedSubType);
+
+ // Find the biggest client from the process pid, that has the least importance
+ // (than given importance) among the given list of clients.
+ // If applicable, match the primary type too.
+ // returns true on success, false otherwise.
+ bool getLeastImportantBiggestClient(int targetPid, int32_t importance,
+ MediaResource::Type type,
+ MediaResource::SubType subType,
+ MediaResource::SubType primarySubType,
+ const std::vector<ClientInfo>& clients,
+ ClientInfo& clientInfo);
+
+ // Find the client that belongs to given process(pid) and with the given clientId.
+ // A nullptr is returned upon failure to find the client.
+ std::shared_ptr<::aidl::android::media::IResourceManagerClient> getClient(
+ int pid, const int64_t& clientId) const;
+
+ // Removes the client from the given process(pid) with the given clientId.
+ // returns true on success, false otherwise.
+ bool removeClient(int pid, const int64_t& clientId);
+
+ // Set the resource observer service, to which to notify when the resources
+ // are added and removed.
+ void setResourceObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService);
+
+ // Dump all the resource allocations for all the processes into a given string
+ void dump(std::string& resourceLogs);
+
+ // get the priority of the process.
+ // If we can't get the priority of the process (with given pid), it will
+ // return false.
+ bool getPriority(int pid, int* priority);
+
+ // Check if the given resource request has conflicting clients.
+ // The resource conflict is defined by the ResourceModel (such as
+ // co-existence of secure codec with another secure or non-secure codec).
+ // But here, the ResourceTracker only looks for resources from lower
+ // priority processes.
+ // If is/are only higher or same priority process/es with the given resource,
+ // it will return false.
+ // Otherwise, adds all the clients to the list of clients and return true.
+ bool getNonConflictingClients(const ResourceRequestInfo& resourceRequestInfo,
+ std::vector<ClientInfo>& clients);
+
+ // Returns unmodifiable reference to the resource map.
+ const std::map<int, ResourceInfos>& getResourceMap() const {
+ return mMap;
+ }
+
+private:
+ // Get ResourceInfos associated with the given process.
+ // If none exists, this method will create and associate an empty object and return it.
+ ResourceInfos& getResourceInfosForEdit(int pid);
+
+ // A helper function that returns true if the callingPid has higher priority than pid.
+ // Returns false otherwise.
+ bool isCallingPriorityHigher(int callingPid, int pid);
+
+ // Locate the resource info corresponding to the process pid and
+ // the client clientId.
+ const ResourceInfo* getResourceInfo(int pid, const int64_t& clientId) const;
+
+ // Notify when a resource is added for the first time.
+ void onFirstAdded(const MediaResourceParcel& resource, uid_t uid);
+ // Notify when a resource is removed for the last time.
+ void onLastRemoved(const MediaResourceParcel& resource, uid_t uid);
+
+private:
+ // Structure that defines process info that needs to be overridden.
+ struct ProcessInfoOverride {
+ std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
+ std::shared_ptr<::aidl::android::media::IResourceManagerClient> client;
+ };
+
+ // Map of Resource information indexed through the process id.
+ std::map<int, ResourceInfos> mMap;
+ // A weak reference (to avoid cyclic dependency) to the ResourceManagerService.
+ // ResourceTracker uses this to communicate back with the ResourceManagerService.
+ std::weak_ptr<ResourceManagerServiceNew> mService;
+ // To notify the ResourceObserverService abour resources are added or removed.
+ std::shared_ptr<ResourceObserverService> mObserverService;
+ // Map of pid and their overrided id.
+ std::map<int, int> mOverridePidMap;
+ // Map of pid and their overridden process info.
+ std::map<pid_t, ProcessInfoOverride> mProcessInfoOverrideMap;
+ // Interface that gets process specific information.
+ sp<ProcessInfoInterface> mProcessInfo;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_RESOURCETRACKER_H_
diff --git a/services/mediaresourcemanager/aidl/android/media/ClientInfoParcel.aidl b/services/mediaresourcemanager/aidl/android/media/ClientInfoParcel.aidl
index eb4bc42..aa14ace 100644
--- a/services/mediaresourcemanager/aidl/android/media/ClientInfoParcel.aidl
+++ b/services/mediaresourcemanager/aidl/android/media/ClientInfoParcel.aidl
@@ -41,4 +41,11 @@
* Name of the resource associated with the client.
*/
@utf8InCpp String name;
+
+ /*
+ * Client importance, which ranges from 0 to int_max.
+ * The default importance is high (0)
+ * Based on the reclaim policy, this could be used during reclaim.
+ */
+ int importance = 0;
}
diff --git a/services/mediaresourcemanager/fuzzer/Android.bp b/services/mediaresourcemanager/fuzzer/Android.bp
index a46d87a..b0db12b 100644
--- a/services/mediaresourcemanager/fuzzer/Android.bp
+++ b/services/mediaresourcemanager/fuzzer/Android.bp
@@ -46,6 +46,7 @@
"libstatspull",
"libstatssocket",
"libactivitymanager_aidl",
+ "aconfig_mediacodec_flags_c_lib",
],
fuzz_config: {
cc: [
diff --git a/services/mediaresourcemanager/test/Android.bp b/services/mediaresourcemanager/test/Android.bp
index f903c62..6a64823 100644
--- a/services/mediaresourcemanager/test/Android.bp
+++ b/services/mediaresourcemanager/test/Android.bp
@@ -12,7 +12,10 @@
name: "ResourceManagerService_test",
srcs: ["ResourceManagerService_test.cpp"],
test_suites: ["device-tests"],
- static_libs: ["libresourcemanagerservice"],
+ static_libs: [
+ "libresourcemanagerservice",
+ "aconfig_mediacodec_flags_c_lib",
+ ],
shared_libs: [
"libbinder",
"libbinder_ndk",
@@ -24,6 +27,7 @@
"libstatspull",
"libstatssocket",
"libactivitymanager_aidl",
+ "server_configurable_flags",
],
include_dirs: [
"frameworks/av/include",
@@ -62,6 +66,7 @@
static_libs: [
"libresourcemanagerservice",
"resourceobserver_aidl_interface-V1-ndk",
+ "aconfig_mediacodec_flags_c_lib",
],
shared_libs: [
"libbinder",
@@ -74,6 +79,7 @@
"libstatspull",
"libstatssocket",
"libactivitymanager_aidl",
+ "server_configurable_flags",
],
include_dirs: [
"frameworks/av/include",
diff --git a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
index 52d82b8..7e8a4a0 100644
--- a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
+++ b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
@@ -123,14 +123,16 @@
struct TestClient : public BnResourceManagerClient {
- TestClient(int pid, int uid, const std::shared_ptr<ResourceManagerService> &service)
- : mPid(pid), mUid(uid), mService(service) {}
+ TestClient(int pid, int uid, int32_t clientImportance,
+ const std::shared_ptr<ResourceManagerService> &service)
+ : mPid(pid), mUid(uid), mClientImportance(clientImportance), mService(service) {}
Status reclaimResource(bool* _aidl_return) override {
ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(mPid),
.uid = static_cast<int32_t>(mUid),
.id = getId(ref<TestClient>()),
- .name = "none"};
+ .name = "none",
+ .importance = mClientImportance};
mService->removeClient(clientInfo);
mWasReclaimResourceCalled = true;
*_aidl_return = true;
@@ -150,10 +152,15 @@
virtual ~TestClient() {}
+ inline int pid() const { return mPid; }
+ inline int uid() const { return mUid; }
+ inline int32_t clientImportance() const { return mClientImportance; }
+
private:
bool mWasReclaimResourceCalled = false;
int mPid;
int mUid;
+ int32_t mClientImportance = 0;
std::shared_ptr<ResourceManagerService> mService;
DISALLOW_EVIL_CONSTRUCTORS(TestClient);
};
@@ -168,6 +175,10 @@
static const int kMidPriorityPid = 25;
static const int kHighPriorityPid = 10;
+static const int32_t kHighestCodecImportance = 0;
+static const int32_t kLowestCodecImportance = 100;
+static const int32_t kMidCodecImportance = 50;
+
using EventType = TestSystemCallback::EventType;
using EventEntry = TestSystemCallback::EventEntry;
bool operator== (const EventEntry& lhs, const EventEntry& rhs) {
@@ -198,8 +209,8 @@
return static_cast<TestClient*>(testClient.get());
}
- ResourceManagerServiceTestBase() {
- ALOGI("ResourceManagerServiceTestBase created");
+ ResourceManagerServiceTestBase(bool newRM = false) : mNewRM(newRM) {
+ ALOGI("ResourceManagerServiceTestBase created with %s RM", newRM ? "new" : "old");
}
void SetUp() override {
@@ -207,14 +218,19 @@
// silently ignored.
ABinderProcess_startThreadPool();
mSystemCB = new TestSystemCallback();
- mService = ResourceManagerService::Create(new TestProcessInfo, mSystemCB);
- mTestClient1 = ::ndk::SharedRefBase::make<TestClient>(kTestPid1, kTestUid1, mService);
- mTestClient2 = ::ndk::SharedRefBase::make<TestClient>(kTestPid2, kTestUid2, mService);
- mTestClient3 = ::ndk::SharedRefBase::make<TestClient>(kTestPid2, kTestUid2, mService);
+ if (mNewRM) {
+ mService = ResourceManagerService::CreateNew(new TestProcessInfo, mSystemCB);
+ } else {
+ mService = ResourceManagerService::Create(new TestProcessInfo, mSystemCB);
+ }
+ mTestClient1 = ::ndk::SharedRefBase::make<TestClient>(kTestPid1, kTestUid1, 0, mService);
+ mTestClient2 = ::ndk::SharedRefBase::make<TestClient>(kTestPid2, kTestUid2, 0, mService);
+ mTestClient3 = ::ndk::SharedRefBase::make<TestClient>(kTestPid2, kTestUid2, 0, mService);
}
- std::shared_ptr<IResourceManagerClient> createTestClient(int pid, int uid) {
- return ::ndk::SharedRefBase::make<TestClient>(pid, uid, mService);
+ std::shared_ptr<IResourceManagerClient> createTestClient(int pid, int uid,
+ int32_t importance = 0) {
+ return ::ndk::SharedRefBase::make<TestClient>(pid, uid, importance, mService);
}
sp<TestSystemCallback> mSystemCB;
@@ -229,9 +245,7 @@
// convert resource1 to ResourceList
ResourceList r1;
for (size_t i = 0; i < resources1.size(); ++i) {
- const auto &res = resources1[i];
- const auto resType = std::tuple(res.type, res.subType, res.id);
- r1[resType] = res;
+ r1.addOrUpdate(resources1[i]);
}
return r1 == resources2;
}
@@ -244,6 +258,8 @@
EXPECT_EQ(client, info.client);
EXPECT_TRUE(isEqualResources(resources, info.resources));
}
+
+ bool mNewRM = false;
};
} // namespace android
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 8f05b13..b3a0932 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -77,8 +77,20 @@
}
public:
- ResourceManagerServiceTest() : ResourceManagerServiceTestBase() {}
+ ResourceManagerServiceTest(bool newRM = false) : ResourceManagerServiceTestBase(newRM) {}
+ void updateConfig(bool bSupportsMultipleSecureCodecs, bool bSupportsSecureWithNonSecureCodec) {
+ std::vector<MediaResourcePolicyParcel> policies;
+ policies.push_back(
+ MediaResourcePolicy(
+ IResourceManagerService::kPolicySupportsMultipleSecureCodecs,
+ bSupportsMultipleSecureCodecs ? "true" : "false"));
+ policies.push_back(
+ MediaResourcePolicy(
+ IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec,
+ bSupportsSecureWithNonSecureCodec ? "true" : "false"));
+ mService->config(policies);
+ }
// test set up
// ---------------------------------------------------------------------------------
@@ -129,7 +141,7 @@
resources3.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 100));
mService->addResource(client3Info, mTestClient3, resources3);
- const PidResourceInfosMap &map = mService->mMap;
+ const PidResourceInfosMap &map = mService->getResourceMap();
EXPECT_EQ(2u, map.size());
const auto& mapIndex1 = map.find(kTestPid1);
EXPECT_TRUE(mapIndex1 != map.end());
@@ -159,7 +171,7 @@
// Expected result:
// 1) the client should have been added;
// 2) both resource entries should have been rejected, resource list should be empty.
- const PidResourceInfosMap &map = mService->mMap;
+ const PidResourceInfosMap &map = mService->getResourceMap();
EXPECT_EQ(1u, map.size());
const auto& mapIndex1 = map.find(kTestPid1);
EXPECT_TRUE(mapIndex1 != map.end());
@@ -197,7 +209,6 @@
resources1.clear();
resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MIN));
- expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MIN));
mService->addResource(client1Info, mTestClient1, resources1);
// Expected result:
@@ -213,29 +224,11 @@
EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
- std::vector<MediaResourcePolicyParcel> policies1;
- policies1.push_back(
- MediaResourcePolicy(
- IResourceManagerService::kPolicySupportsMultipleSecureCodecs,
- "true"));
- policies1.push_back(
- MediaResourcePolicy(
- IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec,
- "false"));
- mService->config(policies1);
+ updateConfig(true, false);
EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec);
- std::vector<MediaResourcePolicyParcel> policies2;
- policies2.push_back(
- MediaResourcePolicy(
- IResourceManagerService::kPolicySupportsMultipleSecureCodecs,
- "false"));
- policies2.push_back(
- MediaResourcePolicy(
- IResourceManagerService::kPolicySupportsSecureWithNonSecureCodec,
- "true"));
- mService->config(policies2);
+ updateConfig(false, true);
EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs);
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
}
@@ -254,7 +247,7 @@
resources11.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
mService->addResource(client1Info, mTestClient1, resources11);
- const PidResourceInfosMap &map = mService->mMap;
+ const PidResourceInfosMap &map = mService->getResourceMap();
EXPECT_EQ(1u, map.size());
const auto& mapIndex1 = map.find(kTestPid1);
EXPECT_TRUE(mapIndex1 != map.end());
@@ -299,7 +292,7 @@
resources11.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 200));
mService->addResource(client1Info, mTestClient1, resources11);
- const PidResourceInfosMap &map = mService->mMap;
+ const PidResourceInfosMap &map = mService->getResourceMap();
EXPECT_EQ(1u, map.size());
const auto& mapIndex1 = map.find(kTestPid1);
EXPECT_TRUE(mapIndex1 != map.end());
@@ -339,8 +332,7 @@
// ### secure codec can't coexist and secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = false;
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(false, true);
// priority too low to reclaim resource
ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(kLowPriorityPid),
@@ -374,7 +366,7 @@
.name = "none"};
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
@@ -402,7 +394,7 @@
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
@@ -428,7 +420,7 @@
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
mService->markClientForPendingRemoval(client2Info);
@@ -466,7 +458,7 @@
.name = "none"};
mService->removeClient(client2Info);
- const PidResourceInfosMap &map = mService->mMap;
+ const PidResourceInfosMap &map = mService->getResourceMap();
EXPECT_EQ(2u, map.size());
const ResourceInfos &infos1 = map.at(kTestPid1);
const ResourceInfos &infos2 = map.at(kTestPid2);
@@ -520,8 +512,7 @@
// ### secure codec can't coexist and secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = false;
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(false, true);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -546,8 +537,7 @@
// ### secure codecs can't coexist and secure codec can't coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = false;
- mService->mSupportsSecureWithNonSecureCodec = false;
+ updateConfig(false, false);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -567,8 +557,7 @@
// ### secure codecs can coexist but secure codec can't coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = true;
- mService->mSupportsSecureWithNonSecureCodec = false;
+ updateConfig(true, false);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -599,8 +588,7 @@
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = true;
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -630,8 +618,7 @@
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsMultipleSecureCodecs = true;
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
@@ -677,7 +664,7 @@
// ### secure codec can't coexist with non-secure codec ###
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = false;
+ updateConfig(true, false);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -703,7 +690,7 @@
// ### secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
// priority too low
CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
@@ -733,7 +720,7 @@
// ### secure codec can coexist with non-secure codec ###
{
addResource();
- mService->mSupportsSecureWithNonSecureCodec = true;
+ updateConfig(true, true);
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
@@ -1510,6 +1497,269 @@
client3Config.width * client3Config.height));
EXPECT_TRUE(currentPixelCountP2 == 0);
}
+
+ void addNonSecureVideoCodecResource(std::shared_ptr<IResourceManagerClient>& client,
+ std::vector<ClientInfoParcel>& infos) {
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(createNonSecureVideoCodecResource(1));
+
+ TestClient* testClient = toTestClient(client);
+ ClientInfoParcel clientInfo {.pid = static_cast<int32_t>(testClient->pid()),
+ .uid = static_cast<int32_t>(testClient->uid()),
+ .id = getId(client),
+ .name = "none",
+ .importance = testClient->clientImportance()};
+ mService->addResource(clientInfo, client, resources);
+ infos.push_back(clientInfo);
+ }
+
+ bool doReclaimResource(const ClientInfoParcel& clientInfo) {
+ bool result = false;
+ std::vector<MediaResourceParcel> reclaimResources;
+ reclaimResources.push_back(createNonSecureVideoCodecResource(1));
+ bool success = mService->reclaimResource(clientInfo, reclaimResources, &result).isOk();
+ return success && result;
+ }
+
+ // Verifies the resource reclaim policies
+ // - this verifies the reclaim policies based on:
+ // - process priority (oom score)
+ // - client priority
+ void testReclaimPolicies() {
+ // Create 3 clients with codec importance high, mid and low for a low
+ // priority pid.
+ std::vector<std::shared_ptr<IResourceManagerClient>> lowPriPidClients;
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kHighestCodecImportance));
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kMidCodecImportance));
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kLowestCodecImportance));
+
+ // Create 3 clients with codec importance high, mid and low for a high
+ // priority pid.
+ std::vector<std::shared_ptr<IResourceManagerClient>> highPriPidClients;
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kHighestCodecImportance));
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kMidCodecImportance));
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kLowestCodecImportance));
+
+ // Add non secure video codec resources for all the 3 clients of low priority pid.
+ std::vector<ClientInfoParcel> lowPriPidClientInfos;
+ for (auto& client : lowPriPidClients) {
+ addNonSecureVideoCodecResource(client, lowPriPidClientInfos);
+ }
+ // Add non secure video codec resources for all the 3 clients of high priority pid.
+ std::vector<ClientInfoParcel> highPriPidClientInfos;
+ for (auto& client : highPriPidClients) {
+ addNonSecureVideoCodecResource(client, highPriPidClientInfos);
+ }
+
+ // 1. Set reclaim policy as "Process Priority".
+ // - A process should be reclaiming from:
+ // - a lower priority process if there is any
+ // - else fail.
+ mService->setReclaimPolicy(true /*process priority*/, false /*codec importance*/);
+
+ // 1.A:
+ // - high priority process should be able to reclaim successfully.
+ // - A process should be reclaiming from the low priority process.
+ EXPECT_TRUE(doReclaimResource(highPriPidClientInfos[0]));
+ // Verify that the high priority pid's clients are untouched.
+ bool success = true;
+ for (auto& client : highPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+ // Verify that the one of the client from the low priority pid has been reclaimed.
+ success = false;
+ for (auto& client : lowPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+
+ // 1.B:
+ // - low priority process should fail to reclaim.
+ EXPECT_FALSE(doReclaimResource(lowPriPidClientInfos[0]));
+
+ // 2. Set reclaim policy as "Client Importance".
+ // - A process should be reclaiming from:
+ // - a lower priority client from the same process if any
+ // - else fail.
+ mService->setReclaimPolicy(false /*process priority*/, true /*codec importance*/);
+
+ // 2.A:
+ // - high priority process should be able to reclaim successfully.
+ // - Process should be reclaiming from a lower priority client from the
+ // same process.
+ EXPECT_TRUE(doReclaimResource(highPriPidClientInfos[0]));
+ // Verify that the low priority pid's clients are untouched.
+ success = true;
+ for (auto& client : lowPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+ // Verify that the one of the low priority client from the high priority
+ // pid has been reclaimed.
+ success = false;
+ for (auto& client : highPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+
+ // 2.B:
+ // - high priority process should be able to reclaim successfully.
+ // - Process should be reclaiming from a lower priority client from the
+ // same process.
+ EXPECT_TRUE(doReclaimResource(lowPriPidClientInfos[0]));
+ // Verify that the high priority pid's clients are untouched.
+ success = true;
+ for (auto& client : highPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+ // Verify that the one of the low priority client from the low priority
+ // pid has been reclaimed.
+ success = false;
+ for (auto& client : lowPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+
+ // 2.C:
+ // - lowest priority client from high priority process should fail to reclaim.
+ EXPECT_FALSE(doReclaimResource(highPriPidClientInfos[2]));
+
+ // 2.D:
+ // - lowest priority client from low priority process should fail to reclaim.
+ EXPECT_FALSE(doReclaimResource(lowPriPidClientInfos[2]));
+
+ // 3. Set reclaim policy as "Process Priority and Client Importance".
+ // - A process should be reclaiming from:
+ // - a lower priority process if there is any
+ // - else a lower priority client from the same process if any
+ // - else fail.
+ mService->setReclaimPolicy(true /*process priority*/, true /*codec importance*/);
+
+ // Remove all clients from the low priority process so that we have
+ // only one process (with high priority) with all the resources.
+ for (const auto& clientInfo : lowPriPidClientInfos) {
+ mService->removeClient(clientInfo);
+ }
+ lowPriPidClientInfos.clear();
+ lowPriPidClients.clear();
+ // 3.A:
+ // - high priority process should be able to reclaim successfully.
+ EXPECT_TRUE(doReclaimResource(highPriPidClientInfos[0]));
+ // Verify that the one of the client from the high priority pid has been reclaimed.
+ success = false;
+ for (auto& client : highPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+
+ // 3.B, set the policy back to ReclaimPolicyProcessPriority
+ mService->setReclaimPolicy(true /*process priority*/, false /*codec importance*/);
+
+ // Since there is only one process, the reclaim should fail.
+ EXPECT_FALSE(doReclaimResource(highPriPidClientInfos[0]));
+
+ // 4. Set reclaim policy as "Process Priority and Client Importance".
+ // - A process should be reclaiming from:
+ // - from a lower priority process if there are any
+ // - else from a lower priority client from the same process if there are any
+ // - else fail.
+ mService->setReclaimPolicy(true /*process priority*/, true /*codec importance*/);
+
+ // Remove all clients from the high priority process so that we can
+ // start a new/fresh resource allocation.
+ for (const auto& clientInfo : highPriPidClientInfos) {
+ mService->removeClient(clientInfo);
+ }
+ highPriPidClientInfos.clear();
+ highPriPidClients.clear();
+
+ // Create 3 clients with codec importance high for a low priority pid.
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kHighestCodecImportance));
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kHighestCodecImportance));
+ lowPriPidClients.push_back(
+ createTestClient(kLowPriorityPid, kTestUid1, kHighestCodecImportance));
+
+ // Create 3 clients with codec importance low for a high priority pid.
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kLowestCodecImportance));
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kLowestCodecImportance));
+ highPriPidClients.push_back(
+ createTestClient(kHighPriorityPid, kTestUid2, kLowestCodecImportance));
+
+ // Add non secure video codec resources for all the 3 clients of low priority pid.
+ for (auto& client : lowPriPidClients) {
+ addNonSecureVideoCodecResource(client, lowPriPidClientInfos);
+ }
+ // Add non secure video codec resources for all the 3 clients of high priority pid.
+ for (auto& client : highPriPidClients) {
+ addNonSecureVideoCodecResource(client, highPriPidClientInfos);
+ }
+
+ // 4.A:
+ // - high priority process should be able to reclaim successfully.
+ EXPECT_TRUE(doReclaimResource(highPriPidClientInfos[0]));
+ // Since all clients are of same priority with in high priority process,
+ // none of the clients should be reclaimed.
+ success = true;
+ for (auto& client : highPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+ // Verify that the one of the client from the low priority pid has been reclaimed.
+ success = false;
+ for (auto& client : lowPriPidClients) {
+ if (toTestClient(client)->checkIfReclaimedAndReset()) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+
+ // 4.B, set the policy back to ReclaimPolicyProcessPriority
+ // If low priority process tries to reclaim, it should fail as there
+ // aren't any lower priority clients or lower priority processes.
+ EXPECT_FALSE(doReclaimResource(lowPriPidClientInfos[0]));
+ }
+};
+
+class ResourceManagerServiceNewTest : public ResourceManagerServiceTest {
+public:
+ ResourceManagerServiceNewTest(bool newRM = true) : ResourceManagerServiceTest(newRM) {}
};
TEST_F(ResourceManagerServiceTest, config) {
@@ -1598,4 +1848,95 @@
testConcurrentCodecs();
}
+/////// test cases for ResourceManagerServiceNew ////
+TEST_F(ResourceManagerServiceNewTest, config) {
+ testConfig();
+}
+
+TEST_F(ResourceManagerServiceNewTest, addResource) {
+ addResource();
+}
+
+TEST_F(ResourceManagerServiceNewTest, combineResource) {
+ testCombineResource();
+}
+
+TEST_F(ResourceManagerServiceNewTest, combineResourceNegative) {
+ testCombineResourceWithNegativeValues();
+}
+
+TEST_F(ResourceManagerServiceNewTest, removeResource) {
+ testRemoveResource();
+}
+
+TEST_F(ResourceManagerServiceNewTest, removeClient) {
+ testRemoveClient();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimResource) {
+ testReclaimResourceSecure();
+ testReclaimResourceNonSecure();
+}
+
+TEST_F(ResourceManagerServiceNewTest, getAllClients_l) {
+ testGetAllClients();
+}
+
+TEST_F(ResourceManagerServiceNewTest, getLowestPriorityBiggestClient_l) {
+ testGetLowestPriorityBiggestClient();
+}
+
+TEST_F(ResourceManagerServiceNewTest, getLowestPriorityPid_l) {
+ testGetLowestPriorityPid();
+}
+
+TEST_F(ResourceManagerServiceNewTest, isCallingPriorityHigher_l) {
+ testIsCallingPriorityHigher();
+}
+
+TEST_F(ResourceManagerServiceNewTest, batteryStats) {
+ testBatteryStats();
+}
+
+TEST_F(ResourceManagerServiceNewTest, cpusetBoost) {
+ testCpusetBoost();
+}
+
+TEST_F(ResourceManagerServiceNewTest, overridePid) {
+ testOverridePid();
+}
+
+TEST_F(ResourceManagerServiceNewTest, markClientForPendingRemoval) {
+ testMarkClientForPendingRemoval();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimResources_withVideoCodec_reclaimsOnlyVideoCodec) {
+ testReclaimResources_withVideoCodec_reclaimsOnlyVideoCodec();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimResources_withAudioCodec_reclaimsOnlyAudioCodec) {
+ testReclaimResources_withAudioCodec_reclaimsOnlyAudioCodec();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimResources_withImageCodec_reclaimsOnlyImageCodec) {
+ testReclaimResources_withImageCodec_reclaimsOnlyImageCodec();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimResources_whenPartialResourceMatch_reclaims) {
+ testReclaimResources_whenPartialResourceMatch_reclaims();
+}
+
+TEST_F(ResourceManagerServiceNewTest,
+ reclaimResourcesFromMarkedClients_removesBiggestMarkedClientForSomeResources) {
+ testReclaimResourcesFromMarkedClients_removesBiggestMarkedClientForSomeResources();
+}
+
+TEST_F(ResourceManagerServiceNewTest, concurrentCodecs) {
+ testConcurrentCodecs();
+}
+
+TEST_F(ResourceManagerServiceNewTest, reclaimPolicies) {
+ testReclaimPolicies();
+}
+
} // namespace android
diff --git a/tools/mainline_hook_partial.sh b/tools/mainline_hook_partial.sh
index cd3e579..63ae4c0 100755
--- a/tools/mainline_hook_partial.sh
+++ b/tools/mainline_hook_partial.sh
Binary files differ
diff --git a/tools/mainline_hook_project.sh b/tools/mainline_hook_project.sh
index 1cc3b2b..d58143e 100755
--- a/tools/mainline_hook_project.sh
+++ b/tools/mainline_hook_project.sh
@@ -16,8 +16,8 @@
# tunables
-DEV_BRANCH=master
-MAINLINE_BRANCH=tm-mainline-prod
+DEV_BRANCH=main
+MAINLINE_BRANCH=udc-mainline-prod
###
RED=$(tput setaf 1)