diff --git a/Android.bp b/Android.bp index 9937b9853606b00a67024e27085f02bfb4119af3..60f0ff1b7382a939472975b5329db64ae171326c 100644 --- a/Android.bp +++ b/Android.bp @@ -1,18 +1,3 @@ -// -// Copyright (C) 2021 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE // CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE // DEPENDING ON IT IN YOUR PROJECT. *** @@ -48,3 +33,66 @@ license { "NOTICE", ], } + +aidl_interface { + name: "av-types-aidl", + unstable: true, + host_supported: true, + vendor_available: true, + double_loadable: true, + local_include_dir: "aidl", + srcs: [ + "aidl/android/media/InterpolatorConfig.aidl", + "aidl/android/media/InterpolatorType.aidl", + "aidl/android/media/MicrophoneInfoData.aidl", + "aidl/android/media/VolumeShaperConfiguration.aidl", + "aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl", + "aidl/android/media/VolumeShaperConfigurationType.aidl", + "aidl/android/media/VolumeShaperOperation.aidl", + "aidl/android/media/VolumeShaperOperationFlag.aidl", + "aidl/android/media/VolumeShaperState.aidl", + ], + backend: { + cpp: { + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth.updatable", + "com.android.media", + "com.android.media.swcodec", + ], + }, + }, +} + +cc_library_headers { + name: "av-headers", + export_include_dirs: ["include"], + static_libs: [ + "av-types-aidl-cpp", + ], + export_static_lib_headers: [ + "av-types-aidl-cpp", + ], + header_libs: [ + "libaudioclient_aidl_conversion_util", + ], + export_header_lib_headers: [ + "libaudioclient_aidl_conversion_util", + ], + host_supported: true, + vendor_available: true, + double_loadable: true, + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth.updatable", + "com.android.media", + "com.android.media.swcodec", + ], + target: { + darwin: { + enabled: false, + }, + }, +} diff --git a/MainlineFiles.cfg b/MainlineFiles.cfg index 37d714c371a68d22e575d29bee6da2081fe2a79c..490bbbf32293751a275fd92307ce53fbe9848a72 100644 --- a/MainlineFiles.cfg +++ b/MainlineFiles.cfg @@ -1,10 +1,13 @@ -# +# # mainline files for frameworks/av +# this list used by tools/mainline_hook_*.sh to help separate +# mainline changes vs framework changes, which release at different paces. +# # # ignore comment (#) lines and blank lines # rest are path prefixes starting at root of the project # (so OWNERS, not frameworks/av/OWNERS) -# +# # path # INCLUDE path # EXCLUDE path @@ -24,11 +27,5 @@ media/codec2/components/ media/codecs/ media/extractors/ -media/libstagefright/codecs/amrnb/ -media/libstagefright/codecs/amrwb/ -media/libstagefright/codecs/amrwbenc/ -media/libstagefright/codecs/common/ -media/libstagefright/codecs/flac/ -media/libstagefright/codecs/m4v_h263/ -media/libstagefright/codecs/mp3dec/ -media/libstagefright/mpeg2ts +media/libstagefright/mpeg2ts/ +media/libstagefright/flac/ diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index b1c81bf4c537a999e35846286066175cded15c9c..716b55053952f318e16ea63fa75a85aaaeb0abf4 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -3,3 +3,11 @@ mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_partial.sh ${REPO hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Only turn on clang-format check for the following subfolders. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + media/libmediatranscoding/ + services/mediatranscoding/ diff --git a/aidl/android/media/InterpolatorConfig.aidl b/aidl/android/media/InterpolatorConfig.aidl new file mode 100644 index 0000000000000000000000000000000000000000..49f90e8e3d7bd34c0c412c20f7dfbde3d79184c3 --- /dev/null +++ b/aidl/android/media/InterpolatorConfig.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.media.InterpolatorType; + +/** + * {@hide} + */ +parcelable InterpolatorConfig { + InterpolatorType type = InterpolatorType.CUBIC; + /** For cubic interpolation, the boundary conditions in slope. */ + float firstSlope; + float lastSlope; + /** A flattened list of pairs, monotonically increasing in x. */ + float[] xy; +} diff --git a/aidl/android/media/InterpolatorType.aidl b/aidl/android/media/InterpolatorType.aidl new file mode 100644 index 0000000000000000000000000000000000000000..b722cadcf1cdbc265d4bc0d089a4748627df4428 --- /dev/null +++ b/aidl/android/media/InterpolatorType.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +/** + * Polynomial spline interpolators. + * + * {@hide} + */ +@Backing(type="int") +enum InterpolatorType { + /** Not continuous. */ + STEP, + /** C0. */ + LINEAR, + /** C1. */ + CUBIC, + /** C1 (to provide locally monotonic curves). */ + CUBIC_MONOTONIC, + // CUBIC_C2, // TODO - requires global computation / cache +} diff --git a/aidl/android/media/MicrophoneInfoData.aidl b/aidl/android/media/MicrophoneInfoData.aidl new file mode 100644 index 0000000000000000000000000000000000000000..747bfa58100549e616d0661599101d787751683b --- /dev/null +++ b/aidl/android/media/MicrophoneInfoData.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * {@hide} + */ +parcelable MicrophoneInfoData { + @utf8InCpp String deviceId; + int portId; + int type; + @utf8InCpp String address; + int deviceLocation; + int deviceGroup; + int indexInTheGroup; + float[] geometricLocation; + float[] orientation; + float[] frequencies; + float[] frequencyResponses; + int[] channelMapping; + float sensitivity; + float maxSpl; + float minSpl; + int directionality; +} diff --git a/aidl/android/media/VolumeShaperConfiguration.aidl b/aidl/android/media/VolumeShaperConfiguration.aidl new file mode 100644 index 0000000000000000000000000000000000000000..d6e65053209c948debc86a090d48ae8b5344e286 --- /dev/null +++ b/aidl/android/media/VolumeShaperConfiguration.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.media.InterpolatorConfig; +import android.media.VolumeShaperConfigurationOptionFlag; +import android.media.VolumeShaperConfigurationType; + +/** + * {@hide} + */ +parcelable VolumeShaperConfiguration { + VolumeShaperConfigurationType type = VolumeShaperConfigurationType.ID; + int id; + /** Bitmask, indexed by VolumeShaperConfigurationOptionFlag. */ + int optionFlags; + double durationMs; + @nullable InterpolatorConfig interpolatorConfig; // null if type == ID +} diff --git a/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl b/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl new file mode 100644 index 0000000000000000000000000000000000000000..f583ceeaca25162502b611a781dc629dfbcdfadb --- /dev/null +++ b/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +@Backing(type="int") +enum VolumeShaperConfigurationOptionFlag { + VOLUME_IN_DBFS, + CLOCK_TIME, +} diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl b/aidl/android/media/VolumeShaperConfigurationType.aidl similarity index 78% rename from media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl rename to aidl/android/media/VolumeShaperConfigurationType.aidl index fd0e60f5b74bffb87854a532bcf49261c8bddeb3..aa6334ec8449af2ccb252cf5975e559c9c8edc0e 100644 --- a/media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl +++ b/aidl/android/media/VolumeShaperConfigurationType.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.media; -package android.media.VolumeShaper; - -parcelable Configuration cpp_header "media/VolumeShaper.h"; +@Backing(type="int") +enum VolumeShaperConfigurationType { + ID, + SCALE, +} diff --git a/aidl/android/media/VolumeShaperOperation.aidl b/aidl/android/media/VolumeShaperOperation.aidl new file mode 100644 index 0000000000000000000000000000000000000000..dd9a0e7a8b94fa192921e7f588f74a7b936e0bf8 --- /dev/null +++ b/aidl/android/media/VolumeShaperOperation.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * {@hide} + */ +parcelable VolumeShaperOperation { + /** Operations to do. Bitmask of VolumeShaperOperationFlag. */ + int flags; + /** If >= 0 the id to remove in a replace operation. */ + int replaceId; + /** Position in the curve to set if a valid number (not nan). */ + float xOffset; +} diff --git a/aidl/android/media/VolumeShaperOperationFlag.aidl b/aidl/android/media/VolumeShaperOperationFlag.aidl new file mode 100644 index 0000000000000000000000000000000000000000..8fe5275f042faf4b986ca49e16a5f252264017d0 --- /dev/null +++ b/aidl/android/media/VolumeShaperOperationFlag.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +@Backing(type="int") +enum VolumeShaperOperationFlag { + /** The absence of this flag indicates "play". */ + REVERSE, + TERMINATE, + JOIN, + DELAY, + CREATE_IF_NECESSARY, +} diff --git a/aidl/android/media/VolumeShaperState.aidl b/aidl/android/media/VolumeShaperState.aidl new file mode 100644 index 0000000000000000000000000000000000000000..4085e2b0af723cbd67bc7bf995a1f2b6ad32d9f6 --- /dev/null +++ b/aidl/android/media/VolumeShaperState.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * {@hide} + */ +parcelable VolumeShaperState { + /** Linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME. */ + float volume; + /** Position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME. */ + float xOffset; +} diff --git a/apex/Android.bp b/apex/Android.bp index 85fc7c97d3b9e12ab176a0e312d78a8ef0f7ab7b..b9abd1282fc3e0db4806bd4329e3f2e359325cd9 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -25,6 +25,7 @@ apex_defaults { name: "com.android.media-defaults", updatable: true, bootclasspath_fragments: ["com.android.media-bootclasspath-fragment"], + systemserverclasspath_fragments: ["com.android.media-systemserverclasspath-fragment"], multilib: { first: { // Extractor process runs only with the primary ABI. @@ -40,15 +41,25 @@ apex_defaults { "libmpeg2extractor", "liboggextractor", "libwavextractor", - // JNI - "libmediaparser-jni" + ], + // Transcoding service only run with primary ABI. + binaries: [ + "mediatranscoding", ], }, }, + // JNI + native_shared_libs: [ + "libmediaparser-jni", + "libmediaformatshaper", + ], + compile_multilib: "both", prebuilts: [ - "mediaextractor.policy", "code_coverage.policy", + "com.android.media-mediatranscoding.rc", "crash_dump.policy", + "mediaextractor.policy", + "media-linker-config", ], key: "com.android.media.key", certificate: ":com.android.media.certificate", @@ -73,10 +84,7 @@ apex { name: "com.android.media", manifest: "manifest.json", defaults: ["com.android.media-defaults"], - prebuilts: [ - "current_sdkinfo", - "media-linker-config", - ], + prebuilts: ["current_sdkinfo"], } linker_config { @@ -121,6 +129,13 @@ bootclasspath_fragment { }, } +// Encapsulate the contributions made by the com.android.media to the systemserverclasspath. +systemserverclasspath_fragment { + name: "com.android.media-systemserverclasspath-fragment", + contents: ["service-media-s"], + apex_available: ["com.android.media"], +} + filegroup { name: "com.android.media-androidManifest", srcs: ["AndroidManifest-media.xml"], @@ -137,6 +152,12 @@ apex_defaults { binaries: [ "mediaswcodec", ], + native_shared_libs: [ + "libcodec2_hidl@1.0", + "libcodec2_hidl@1.1", + "libcodec2_hidl@1.2", + "libstagefright_foundation", + ], prebuilts: [ "com.android.media.swcodec-mediaswcodec.rc", "com.android.media.swcodec-ld.config.txt", @@ -145,7 +166,6 @@ apex_defaults { "crash_dump.policy", "mediaswcodec.xml", ], - use_vendor: true, key: "com.android.media.swcodec.key", certificate: ":com.android.media.swcodec.certificate", @@ -165,6 +185,13 @@ apex_defaults { compressible: true, } +prebuilt_etc { + name: "com.android.media-mediatranscoding.rc", + src: "mediatranscoding.rc", + filename: "init.rc", + installable: false, +} + prebuilt_etc { name: "com.android.media.swcodec-mediaswcodec.rc", src: "mediaswcodec.rc", diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING index f036516acef66ae0431f95d8db00ab9ea79d7cac..4b7c0195a9a01324f3129e12dc1e86981158f46b 100644 --- a/apex/TEST_MAPPING +++ b/apex/TEST_MAPPING @@ -14,17 +14,9 @@ }, { "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests" - } - ] - }, - { - "name": "GtsExoPlayerTestCases", - "options" : [ - { - "include-annotation": "android.platform.test.annotations.SocPresubmit" }, { - "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed" + "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests" } ] } diff --git a/apex/manifest.json b/apex/manifest.json index ddd642ed3c13c536733155ca5a6b9f9c51d87caf..5d72031d2d655a017f84d712a366ef9305822788 100644 --- a/apex/manifest.json +++ b/apex/manifest.json @@ -1,4 +1,10 @@ { "name": "com.android.media", - "version": 300000000 + "version": 319999900, + "requireNativeLibs": [ + "libandroid.so", + "libbinder_ndk.so", + "libmediandk.so", + ":sphal" + ] } diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json index 1f05d2eeb42707a968cc039db508b1d76c784efe..b0d962d6cc76e34112c5492606985e2aaf4dd35a 100644 --- a/apex/manifest_codec.json +++ b/apex/manifest_codec.json @@ -1,6 +1,6 @@ { "name": "com.android.media.swcodec", - "version": 300000000, + "version": 319999900, "requireNativeLibs": [ ":sphal" ] diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc index d17481bf98ac5ae732422eaf86e623674834f564..0c9b8c8cfe219f75f0b47a61d9c5e79e3e6e5a34 100644 --- a/apex/mediaswcodec.rc +++ b/apex/mediaswcodec.rc @@ -2,6 +2,5 @@ service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec class main user mediacodec group camera drmrpc mediadrm - override ioprio rt 4 writepid /dev/cpuset/foreground/tasks diff --git a/apex/mediatranscoding.rc b/apex/mediatranscoding.rc new file mode 100644 index 0000000000000000000000000000000000000000..ae9f8baac605fe6d9b15b6d1867fe43bfdbf1a60 --- /dev/null +++ b/apex/mediatranscoding.rc @@ -0,0 +1,12 @@ +# media.transcoding service is defined on com.android.media apex which goes back +# to API29, but we only want it started on API31+ devices. So we declare it as +# "disabled" and start it explicitly on boot. +service media.transcoding /apex/com.android.media/bin/mediatranscoding + class main + user media + group media + ioprio rt 4 + # Restrict to little cores only with system-background cpuset. + writepid /dev/cpuset/system-background/tasks + interface aidl media.transcoding + disabled diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp index 4ff4d063a1862aef9b63d842e28c9384b51b831b..8b810901b56d940709208e60fa94f7e071b61699 100644 --- a/apex/testing/Android.bp +++ b/apex/testing/Android.bp @@ -18,8 +18,6 @@ package { // all of the 'license_kinds' from "frameworks_av_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS default_applicable_licenses: ["frameworks_av_license"], } diff --git a/camera/Android.bp b/camera/Android.bp index 93bc68de6e18ed9cd0c2223b8f3d7eee62c3caa4..6878c203698e3528d2bf9204eac323e745235c57 100644 --- a/camera/Android.bp +++ b/camera/Android.bp @@ -66,10 +66,10 @@ cc_library_shared { "CameraParameters.cpp", "CaptureResult.cpp", "CameraParameters2.cpp", + "CameraSessionStats.cpp", "ICamera.cpp", "ICameraClient.cpp", "ICameraRecordingProxy.cpp", - "ICameraRecordingProxyListener.cpp", "camera2/CaptureRequest.cpp", "camera2/ConcurrentCamera.cpp", "camera2/OutputConfiguration.cpp", @@ -119,6 +119,8 @@ filegroup { "aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl", "aidl/android/hardware/camera2/ICameraDeviceUser.aidl", "aidl/android/hardware/camera2/ICameraOfflineSession.aidl", + "aidl/android/hardware/camera2/ICameraInjectionCallback.aidl", + "aidl/android/hardware/camera2/ICameraInjectionSession.aidl", ], path: "aidl", } diff --git a/camera/Camera.cpp b/camera/Camera.cpp index 84d1d934e19fe2e8a4c1dd6b28768c84f18ed76e..604dbb8b17d456349afb634b019b319abb0f7529 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -72,66 +71,10 @@ Camera::~Camera() } sp Camera::connect(int cameraId, const String16& clientPackageName, - int clientUid, int clientPid) + int clientUid, int clientPid, int targetSdkVersion) { - return CameraBaseT::connect(cameraId, clientPackageName, clientUid, clientPid); -} - -status_t Camera::connectLegacy(int cameraId, int halVersion, - const String16& clientPackageName, - int clientUid, - sp& camera) -{ - ALOGV("%s: connect legacy camera device", __FUNCTION__); - sp c = new Camera(cameraId); - sp<::android::hardware::ICameraClient> cl = c; - status_t status = NO_ERROR; - const sp<::android::hardware::ICameraService>& cs = CameraBaseT::getCameraService(); - - binder::Status ret; - if (cs != nullptr) { - ret = cs.get()->connectLegacy(cl, cameraId, halVersion, clientPackageName, - clientUid, /*out*/&(c->mCamera)); - } - if (ret.isOk() && c->mCamera != nullptr) { - IInterface::asBinder(c->mCamera)->linkToDeath(c); - c->mStatus = NO_ERROR; - camera = c; - } else { - switch(ret.serviceSpecificErrorCode()) { - case hardware::ICameraService::ERROR_DISCONNECTED: - status = -ENODEV; - break; - case hardware::ICameraService::ERROR_CAMERA_IN_USE: - status = -EBUSY; - break; - case hardware::ICameraService::ERROR_INVALID_OPERATION: - status = -EINVAL; - break; - case hardware::ICameraService::ERROR_MAX_CAMERAS_IN_USE: - status = -EUSERS; - break; - case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT: - status = BAD_VALUE; - break; - case hardware::ICameraService::ERROR_DEPRECATED_HAL: - status = -EOPNOTSUPP; - break; - case hardware::ICameraService::ERROR_DISABLED: - status = -EACCES; - break; - case hardware::ICameraService::ERROR_PERMISSION_DENIED: - status = PERMISSION_DENIED; - break; - default: - status = -EINVAL; - ALOGW("An error occurred while connecting to camera %d: %s", cameraId, - (cs != nullptr) ? "Service not available" : ret.toString8().string()); - break; - } - c.clear(); - } - return status; + return CameraBaseT::connect(cameraId, clientPackageName, clientUid, + clientPid, targetSdkVersion); } status_t Camera::reconnect() @@ -214,10 +157,6 @@ void Camera::stopPreview() void Camera::stopRecording() { ALOGV("stopRecording"); - { - Mutex::Autolock _l(mLock); - mRecordingProxyListener.clear(); - } sp <::android::hardware::ICamera> c = mCamera; if (c == 0) return; c->stopRecording(); @@ -325,12 +264,6 @@ void Camera::setListener(const sp& listener) mListener = listener; } -void Camera::setRecordingProxyListener(const sp& listener) -{ - Mutex::Autolock _l(mLock); - mRecordingProxyListener = listener; -} - void Camera::setPreviewCallbackFlags(int flag) { ALOGV("setPreviewCallbackFlags"); @@ -384,19 +317,6 @@ void Camera::dataCallback(int32_t msgType, const sp& dataPtr, // callback from camera service when timestamped frame is ready void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { - // If recording proxy listener is registered, forward the frame and return. - // The other listener (mListener) is ignored because the receiver needs to - // call releaseRecordingFrame. - sp proxylistener; - { - Mutex::Autolock _l(mLock); - proxylistener = mRecordingProxyListener; - } - if (proxylistener != NULL) { - proxylistener->dataCallbackTimestamp(timestamp, msgType, dataPtr); - return; - } - sp listener; { Mutex::Autolock _l(mLock); @@ -413,19 +333,6 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp< void Camera::recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, native_handle_t* handle) { - // If recording proxy listener is registered, forward the frame and return. - // The other listener (mListener) is ignored because the receiver needs to - // call releaseRecordingFrameHandle. - sp proxylistener; - { - Mutex::Autolock _l(mLock); - proxylistener = mRecordingProxyListener; - } - if (proxylistener != NULL) { - proxylistener->recordingFrameHandleCallbackTimestamp(timestamp, handle); - return; - } - sp listener; { Mutex::Autolock _l(mLock); @@ -444,19 +351,6 @@ void Camera::recordingFrameHandleCallbackTimestampBatch( const std::vector& timestamps, const std::vector& handles) { - // If recording proxy listener is registered, forward the frame and return. - // The other listener (mListener) is ignored because the receiver needs to - // call releaseRecordingFrameHandle. - sp proxylistener; - { - Mutex::Autolock _l(mLock); - proxylistener = mRecordingProxyListener; - } - if (proxylistener != NULL) { - proxylistener->recordingFrameHandleCallbackTimestampBatch(timestamps, handles); - return; - } - sp listener; { Mutex::Autolock _l(mLock); @@ -476,10 +370,9 @@ sp Camera::getRecordingProxy() { return new RecordingProxy(this); } -status_t Camera::RecordingProxy::startRecording(const sp& listener) +status_t Camera::RecordingProxy::startRecording() { ALOGV("RecordingProxy::startRecording"); - mCamera->setRecordingProxyListener(listener); mCamera->reconnect(); return mCamera->startRecording(); } @@ -490,23 +383,6 @@ void Camera::RecordingProxy::stopRecording() mCamera->stopRecording(); } -void Camera::RecordingProxy::releaseRecordingFrame(const sp& mem) -{ - ALOGV("RecordingProxy::releaseRecordingFrame"); - mCamera->releaseRecordingFrame(mem); -} - -void Camera::RecordingProxy::releaseRecordingFrameHandle(native_handle_t* handle) { - ALOGV("RecordingProxy::releaseRecordingFrameHandle"); - mCamera->releaseRecordingFrameHandle(handle); -} - -void Camera::RecordingProxy::releaseRecordingFrameHandleBatch( - const std::vector& handles) { - ALOGV("RecordingProxy::releaseRecordingFrameHandleBatch"); - mCamera->releaseRecordingFrameHandleBatch(handles); -} - Camera::RecordingProxy::RecordingProxy(const sp& camera) { mCamera = camera; diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp index 0b0f5846cf013d05206f614e9c21d19add822563..03439fdf72543f99529d3a6c3a0055214a861c53 100644 --- a/camera/CameraBase.cpp +++ b/camera/CameraBase.cpp @@ -152,7 +152,7 @@ const sp<::android::hardware::ICameraService> CameraBase::getC template sp CameraBase::connect(int cameraId, const String16& clientPackageName, - int clientUid, int clientPid) + int clientUid, int clientPid, int targetSdkVersion) { ALOGV("%s: connect", __FUNCTION__); sp c = new TCam(cameraId); @@ -163,7 +163,7 @@ sp CameraBase::connect(int cameraId, if (cs != nullptr) { TCamConnectService fnConnectService = TCamTraits::fnConnectService; ret = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, - clientPid, /*out*/ &c->mCamera); + clientPid, targetSdkVersion, /*out*/ &c->mCamera); } if (ret.isOk() && c->mCamera != nullptr) { IInterface::asBinder(c->mCamera)->linkToDeath(c); diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index 024311f4afbf978300fae38d524d3d4777117c2b..a4ae71be2c6bb7fe008e5e08beb970d2602f4633 100644 --- a/camera/CameraMetadata.cpp +++ b/camera/CameraMetadata.cpp @@ -22,6 +22,7 @@ #include #include +#include namespace android { @@ -527,6 +528,8 @@ status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) { mBuffer = allocate_camera_metadata(newEntryCount, newDataCount); if (mBuffer == NULL) { + // Maintain old buffer to avoid potential memory leak. + mBuffer = oldBuffer; ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); return NO_MEMORY; } @@ -877,5 +880,8 @@ status_t CameraMetadata::getTagFromName(const char *name, return OK; } +metadata_vendor_id_t CameraMetadata::getVendorId() { + return get_camera_metadata_vendor_id(mBuffer); +} }; // namespace android diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28e037ff946a4f84290582969d7e5f7142289e50 --- /dev/null +++ b/camera/CameraSessionStats.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "CameraSessionStats" +#include +#include + +#include + +#include + +namespace android { +namespace hardware { + +status_t CameraStreamStats::readFromParcel(const android::Parcel* parcel) { + if (parcel == NULL) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + status_t err = OK; + + int width = 0; + if ((err = parcel->readInt32(&width)) != OK) { + ALOGE("%s: Failed to read width from parcel", __FUNCTION__); + return err; + } + + int height = 0; + if ((err = parcel->readInt32(&height)) != OK) { + ALOGE("%s: Failed to read height from parcel", __FUNCTION__); + return err; + } + + int format = 0; + if ((err = parcel->readInt32(&format)) != OK) { + ALOGE("%s: Failed to read format from parcel", __FUNCTION__); + return err; + } + + int dataSpace = 0; + if ((err = parcel->readInt32(&dataSpace)) != OK) { + ALOGE("%s: Failed to read dataSpace from parcel", __FUNCTION__); + return err; + } + + int64_t usage = 0; + if ((err = parcel->readInt64(&usage)) != OK) { + ALOGE("%s: Failed to read usage from parcel", __FUNCTION__); + return err; + } + + int64_t requestCount = 0; + if ((err = parcel->readInt64(&requestCount)) != OK) { + ALOGE("%s: Failed to read request count from parcel", __FUNCTION__); + return err; + } + + int64_t errorCount = 0; + if ((err = parcel->readInt64(&errorCount)) != OK) { + ALOGE("%s: Failed to read error count from parcel", __FUNCTION__); + return err; + } + + int startLatencyMs = 0; + if ((err = parcel->readInt32(&startLatencyMs)) != OK) { + ALOGE("%s: Failed to read start latency from parcel", __FUNCTION__); + return err; + } + + int maxHalBuffers = 0; + if ((err = parcel->readInt32(&maxHalBuffers)) != OK) { + ALOGE("%s: Failed to read max Hal buffers from parcel", __FUNCTION__); + return err; + } + + int maxAppBuffers = 0; + if ((err = parcel->readInt32(&maxAppBuffers)) != OK) { + ALOGE("%s: Failed to read max app buffers from parcel", __FUNCTION__); + return err; + } + + int histogramType = HISTOGRAM_TYPE_UNKNOWN; + if ((err = parcel->readInt32(&histogramType)) != OK) { + ALOGE("%s: Failed to read histogram type from parcel", __FUNCTION__); + return err; + } + + std::vector histogramBins; + if ((err = parcel->readFloatVector(&histogramBins)) != OK) { + ALOGE("%s: Failed to read histogram bins from parcel", __FUNCTION__); + return err; + } + + std::vector histogramCounts; + if ((err = parcel->readInt64Vector(&histogramCounts)) != OK) { + ALOGE("%s: Failed to read histogram counts from parcel", __FUNCTION__); + return err; + } + + mWidth = width; + mHeight = height; + mFormat = format; + mDataSpace = dataSpace; + mUsage = usage; + mRequestCount = requestCount; + mErrorCount = errorCount; + mStartLatencyMs = startLatencyMs; + mMaxHalBuffers = maxHalBuffers; + mMaxAppBuffers = maxAppBuffers; + mHistogramType = histogramType; + mHistogramBins = std::move(histogramBins); + mHistogramCounts = std::move(histogramCounts); + + return OK; +} + +status_t CameraStreamStats::writeToParcel(android::Parcel* parcel) const { + if (parcel == NULL) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + status_t err = OK; + + if ((err = parcel->writeInt32(mWidth)) != OK) { + ALOGE("%s: Failed to write stream width!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mHeight)) != OK) { + ALOGE("%s: Failed to write stream height!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mFormat)) != OK) { + ALOGE("%s: Failed to write stream format!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mDataSpace)) != OK) { + ALOGE("%s: Failed to write stream dataSpace!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mUsage)) != OK) { + ALOGE("%s: Failed to write stream usage!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mRequestCount)) != OK) { + ALOGE("%s: Failed to write stream request count!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mErrorCount)) != OK) { + ALOGE("%s: Failed to write stream error count!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mStartLatencyMs)) != OK) { + ALOGE("%s: Failed to write stream start latency!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mMaxHalBuffers)) != OK) { + ALOGE("%s: Failed to write max hal buffers", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mMaxAppBuffers)) != OK) { + ALOGE("%s: Failed to write max app buffers", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mHistogramType)) != OK) { + ALOGE("%s: Failed to write histogram type", __FUNCTION__); + return err; + } + + if ((err = parcel->writeFloatVector(mHistogramBins)) != OK) { + ALOGE("%s: Failed to write histogram bins!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64Vector(mHistogramCounts)) != OK) { + ALOGE("%s: Failed to write histogram counts!", __FUNCTION__); + return err; + } + + return OK; +} + +const int CameraSessionStats::CAMERA_STATE_OPEN = 0; +const int CameraSessionStats::CAMERA_STATE_ACTIVE = 1; +const int CameraSessionStats::CAMERA_STATE_IDLE = 2; +const int CameraSessionStats::CAMERA_STATE_CLOSED = 3; + +const int CameraSessionStats::CAMERA_FACING_BACK = 0; +const int CameraSessionStats::CAMERA_FACING_FRONT = 1; +const int CameraSessionStats::CAMERA_FACING_EXTERNAL = 2; + +const int CameraSessionStats::CAMERA_API_LEVEL_1 = 1; +const int CameraSessionStats::CAMERA_API_LEVEL_2 = 2; + +CameraSessionStats::CameraSessionStats() : + mFacing(CAMERA_FACING_BACK), + mNewCameraState(CAMERA_STATE_CLOSED), + mApiLevel(0), + mIsNdk(false), + mLatencyMs(-1), + mSessionType(0), + mInternalReconfigure(0), + mRequestCount(0), + mResultErrorCount(0), + mDeviceError(false) {} + +CameraSessionStats::CameraSessionStats(const String16& cameraId, + int facing, int newCameraState, const String16& clientName, + int apiLevel, bool isNdk, int32_t latencyMs) : + mCameraId(cameraId), + mFacing(facing), + mNewCameraState(newCameraState), + mClientName(clientName), + mApiLevel(apiLevel), + mIsNdk(isNdk), + mLatencyMs(latencyMs), + mSessionType(0), + mInternalReconfigure(0), + mRequestCount(0), + mResultErrorCount(0), + mDeviceError(0) {} + +status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) { + if (parcel == NULL) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + status_t err = OK; + + String16 id; + if ((err = parcel->readString16(&id)) != OK) { + ALOGE("%s: Failed to read camera id!", __FUNCTION__); + return BAD_VALUE; + } + + int facing = 0; + if ((err = parcel->readInt32(&facing)) != OK) { + ALOGE("%s: Failed to read camera facing from parcel", __FUNCTION__); + return err; + } + + int32_t newCameraState; + if ((err = parcel->readInt32(&newCameraState)) != OK) { + ALOGE("%s: Failed to read new camera state from parcel", __FUNCTION__); + return err; + } + + String16 clientName; + if ((err = parcel->readString16(&clientName)) != OK) { + ALOGE("%s: Failed to read client name!", __FUNCTION__); + return BAD_VALUE; + } + + int32_t apiLevel; + if ((err = parcel->readInt32(&apiLevel)) != OK) { + ALOGE("%s: Failed to read api level from parcel", __FUNCTION__); + return err; + } + + bool isNdk; + if ((err = parcel->readBool(&isNdk)) != OK) { + ALOGE("%s: Failed to read isNdk flag from parcel", __FUNCTION__); + return err; + } + + int32_t latencyMs; + if ((err = parcel->readInt32(&latencyMs)) != OK) { + ALOGE("%s: Failed to read latencyMs from parcel", __FUNCTION__); + return err; + } + + int32_t sessionType; + if ((err = parcel->readInt32(&sessionType)) != OK) { + ALOGE("%s: Failed to read session type from parcel", __FUNCTION__); + return err; + } + + int32_t internalReconfigure; + if ((err = parcel->readInt32(&internalReconfigure)) != OK) { + ALOGE("%s: Failed to read internal reconfigure count from parcel", __FUNCTION__); + return err; + } + + int64_t requestCount; + if ((err = parcel->readInt64(&requestCount)) != OK) { + ALOGE("%s: Failed to read request count from parcel", __FUNCTION__); + return err; + } + + int64_t resultErrorCount; + if ((err = parcel->readInt64(&resultErrorCount)) != OK) { + ALOGE("%s: Failed to read result error count from parcel", __FUNCTION__); + return err; + } + + bool deviceError; + if ((err = parcel->readBool(&deviceError)) != OK) { + ALOGE("%s: Failed to read device error flag from parcel", __FUNCTION__); + return err; + } + + std::vector streamStats; + if ((err = parcel->readParcelableVector(&streamStats)) != OK) { + ALOGE("%s: Failed to read stream state from parcel", __FUNCTION__); + return err; + } + + mCameraId = id; + mFacing = facing; + mNewCameraState = newCameraState; + mClientName = clientName; + mApiLevel = apiLevel; + mIsNdk = isNdk; + mLatencyMs = latencyMs; + mSessionType = sessionType; + mInternalReconfigure = internalReconfigure; + mRequestCount = requestCount; + mResultErrorCount = resultErrorCount; + mDeviceError = deviceError; + mStreamStats = std::move(streamStats); + + return OK; +} + +status_t CameraSessionStats::writeToParcel(android::Parcel* parcel) const { + if (parcel == NULL) { + ALOGE("%s: Null parcel", __FUNCTION__); + return BAD_VALUE; + } + + status_t err = OK; + + if ((err = parcel->writeString16(mCameraId)) != OK) { + ALOGE("%s: Failed to write camera id!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mFacing)) != OK) { + ALOGE("%s: Failed to write camera facing!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mNewCameraState)) != OK) { + ALOGE("%s: Failed to write new camera state!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeString16(mClientName)) != OK) { + ALOGE("%s: Failed to write client name!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mApiLevel)) != OK) { + ALOGE("%s: Failed to write api level!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeBool(mIsNdk)) != OK) { + ALOGE("%s: Failed to write isNdk flag!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mLatencyMs)) != OK) { + ALOGE("%s: Failed to write latency in Ms!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mSessionType)) != OK) { + ALOGE("%s: Failed to write session type!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mInternalReconfigure)) != OK) { + ALOGE("%s: Failed to write internal reconfigure count!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mRequestCount)) != OK) { + ALOGE("%s: Failed to write request count!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mResultErrorCount)) != OK) { + ALOGE("%s: Failed to write result error count!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeBool(mDeviceError)) != OK) { + ALOGE("%s: Failed to write device error flag!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeParcelableVector(mStreamStats)) != OK) { + ALOGE("%s: Failed to write stream states!", __FUNCTION__); + return err; + } + + return OK; +} + +} // namespace hardware +} // namesmpace android diff --git a/camera/ICameraRecordingProxy.cpp b/camera/ICameraRecordingProxy.cpp index bd6af75c0a142828b231200f2656b5d5e5ff6092..97523a52319c0aea3fa448012d7c7603073ba5dd 100644 --- a/camera/ICameraRecordingProxy.cpp +++ b/camera/ICameraRecordingProxy.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "ICameraRecordingProxy" #include #include -#include #include #include #include @@ -29,10 +28,7 @@ namespace android { enum { START_RECORDING = IBinder::FIRST_CALL_TRANSACTION, - STOP_RECORDING, - RELEASE_RECORDING_FRAME, - RELEASE_RECORDING_FRAME_HANDLE, - RELEASE_RECORDING_FRAME_HANDLE_BATCH, + STOP_RECORDING }; @@ -44,12 +40,11 @@ public: { } - status_t startRecording(const sp& listener) + status_t startRecording() { ALOGV("startRecording"); Parcel data, reply; data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(listener)); remote()->transact(START_RECORDING, data, &reply); return reply.readInt32(); } @@ -61,46 +56,6 @@ public: data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); remote()->transact(STOP_RECORDING, data, &reply); } - - void releaseRecordingFrame(const sp& mem) - { - ALOGV("releaseRecordingFrame"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(mem)); - remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); - } - - void releaseRecordingFrameHandle(native_handle_t *handle) { - ALOGV("releaseRecordingFrameHandle"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - data.writeNativeHandle(handle); - - remote()->transact(RELEASE_RECORDING_FRAME_HANDLE, data, &reply); - - // Close the native handle because camera received a dup copy. - native_handle_close(handle); - native_handle_delete(handle); - } - - void releaseRecordingFrameHandleBatch(const std::vector& handles) { - ALOGV("releaseRecordingFrameHandleBatch"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor()); - uint32_t n = handles.size(); - data.writeUint32(n); - for (auto& handle : handles) { - data.writeNativeHandle(handle); - } - remote()->transact(RELEASE_RECORDING_FRAME_HANDLE_BATCH, data, &reply); - - // Close the native handle because camera received a dup copy. - for (auto& handle : handles) { - native_handle_close(handle); - native_handle_delete(handle); - } - } }; IMPLEMENT_META_INTERFACE(CameraRecordingProxy, "android.hardware.ICameraRecordingProxy"); @@ -114,9 +69,7 @@ status_t BnCameraRecordingProxy::onTransact( case START_RECORDING: { ALOGV("START_RECORDING"); CHECK_INTERFACE(ICameraRecordingProxy, data, reply); - sp listener = - interface_cast(data.readStrongBinder()); - reply->writeInt32(startRecording(listener)); + reply->writeInt32(startRecording()); return NO_ERROR; } break; case STOP_RECORDING: { @@ -125,46 +78,6 @@ status_t BnCameraRecordingProxy::onTransact( stopRecording(); return NO_ERROR; } break; - case RELEASE_RECORDING_FRAME: { - ALOGV("RELEASE_RECORDING_FRAME"); - CHECK_INTERFACE(ICameraRecordingProxy, data, reply); - sp mem = interface_cast(data.readStrongBinder()); - releaseRecordingFrame(mem); - return NO_ERROR; - } break; - case RELEASE_RECORDING_FRAME_HANDLE: { - ALOGV("RELEASE_RECORDING_FRAME_HANDLE"); - CHECK_INTERFACE(ICameraRecordingProxy, data, reply); - - // releaseRecordingFrameHandle will be responsble to close the native handle. - releaseRecordingFrameHandle(data.readNativeHandle()); - return NO_ERROR; - } break; - case RELEASE_RECORDING_FRAME_HANDLE_BATCH: { - ALOGV("RELEASE_RECORDING_FRAME_HANDLE_BATCH"); - CHECK_INTERFACE(ICameraRecordingProxy, data, reply); - uint32_t n = 0; - status_t res = data.readUint32(&n); - if (res != OK) { - ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res); - return BAD_VALUE; - } - std::vector handles; - handles.reserve(n); - for (uint32_t i = 0; i < n; i++) { - native_handle_t* handle = data.readNativeHandle(); - if (handle == nullptr) { - ALOGE("%s: Received a null native handle at handles[%d]", - __FUNCTION__, i); - return BAD_VALUE; - } - handles.push_back(handle); - } - - // releaseRecordingFrameHandleBatch will be responsble to close the native handle. - releaseRecordingFrameHandleBatch(handles); - return NO_ERROR; - } break; default: return BBinder::onTransact(code, data, reply, flags); } @@ -173,4 +86,3 @@ status_t BnCameraRecordingProxy::onTransact( // ---------------------------------------------------------------------------- }; // namespace android - diff --git a/camera/ICameraRecordingProxyListener.cpp b/camera/ICameraRecordingProxyListener.cpp deleted file mode 100644 index 66faf8fdd6082fa55f5e673484dd0f43933f869f..0000000000000000000000000000000000000000 --- a/camera/ICameraRecordingProxyListener.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2011 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 "ICameraRecordingProxyListener" -#include -#include -#include -#include -#include -#include - -namespace android { - -enum { - DATA_CALLBACK_TIMESTAMP = IBinder::FIRST_CALL_TRANSACTION, - RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP, - RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH -}; - -class BpCameraRecordingProxyListener: public BpInterface -{ -public: - explicit BpCameraRecordingProxyListener(const sp& impl) - : BpInterface(impl) - { - } - - void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp& imageData) - { - ALOGV("dataCallback"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor()); - data.writeInt64(timestamp); - data.writeInt32(msgType); - data.writeStrongBinder(IInterface::asBinder(imageData)); - remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY); - } - - void recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, native_handle_t* handle) { - ALOGV("recordingFrameHandleCallbackTimestamp"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor()); - data.writeInt64(timestamp); - data.writeNativeHandle(handle); - remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP, data, &reply, - IBinder::FLAG_ONEWAY); - - // The native handle is dupped in ICameraClient so we need to free it here. - native_handle_close(handle); - native_handle_delete(handle); - } - - void recordingFrameHandleCallbackTimestampBatch( - const std::vector& timestamps, - const std::vector& handles) { - ALOGV("recordingFrameHandleCallbackTimestampBatch"); - Parcel data, reply; - data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor()); - - uint32_t n = timestamps.size(); - if (n != handles.size()) { - ALOGE("%s: size of timestamps(%zu) and handles(%zu) mismatch!", - __FUNCTION__, timestamps.size(), handles.size()); - return; - } - data.writeUint32(n); - for (auto ts : timestamps) { - data.writeInt64(ts); - } - for (auto& handle : handles) { - data.writeNativeHandle(handle); - } - remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH, data, &reply, - IBinder::FLAG_ONEWAY); - - // The native handle is dupped in ICameraClient so we need to free it here. - for (auto& handle : handles) { - native_handle_close(handle); - native_handle_delete(handle); - } - } -}; - -IMPLEMENT_META_INTERFACE(CameraRecordingProxyListener, "android.hardware.ICameraRecordingProxyListener"); - -// ---------------------------------------------------------------------- - -status_t BnCameraRecordingProxyListener::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case DATA_CALLBACK_TIMESTAMP: { - ALOGV("DATA_CALLBACK_TIMESTAMP"); - CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply); - nsecs_t timestamp = data.readInt64(); - int32_t msgType = data.readInt32(); - sp imageData = interface_cast(data.readStrongBinder()); - dataCallbackTimestamp(timestamp, msgType, imageData); - return NO_ERROR; - } break; - case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP: { - ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP"); - CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply); - nsecs_t timestamp; - status_t res = data.readInt64(×tamp); - if (res != OK) { - ALOGE("%s: Failed to read timestamp: %s (%d)", __FUNCTION__, strerror(-res), res); - return BAD_VALUE; - } - - native_handle_t* handle = data.readNativeHandle(); - if (handle == nullptr) { - ALOGE("%s: Received a null native handle", __FUNCTION__); - return BAD_VALUE; - } - // The native handle will be freed in - // BpCameraRecordingProxy::releaseRecordingFrameHandle. - recordingFrameHandleCallbackTimestamp(timestamp, handle); - return NO_ERROR; - } break; - case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH: { - ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH"); - CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply); - uint32_t n = 0; - status_t res = data.readUint32(&n); - if (res != OK) { - ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res); - return BAD_VALUE; - } - std::vector timestamps; - std::vector handles; - timestamps.reserve(n); - handles.reserve(n); - for (uint32_t i = 0; i < n; i++) { - nsecs_t t; - res = data.readInt64(&t); - if (res != OK) { - ALOGE("%s: Failed to read timestamp[%d]: %s (%d)", - __FUNCTION__, i, strerror(-res), res); - return BAD_VALUE; - } - timestamps.push_back(t); - } - for (uint32_t i = 0; i < n; i++) { - native_handle_t* handle = data.readNativeHandle(); - if (handle == nullptr) { - ALOGE("%s: Received a null native handle at handles[%d]", - __FUNCTION__, i); - return BAD_VALUE; - } - handles.push_back(handle); - } - // The native handle will be freed in - // BpCameraRecordingProxy::releaseRecordingFrameHandleBatch. - recordingFrameHandleCallbackTimestampBatch(timestamps, handles); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------------- - -}; // namespace android - diff --git a/camera/TEST_MAPPING b/camera/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..683e183d6dc0b9dc3bebed1f36a2344383010c82 --- /dev/null +++ b/camera/TEST_MAPPING @@ -0,0 +1,11 @@ +{ + "postsubmit": [ + { + "name": "CtsCameraTestCases" + }, + { + "name": "CtsCameraTestCases", + "keywords": ["primary-device"] + } + ] +} diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp index d713d2dd86ad3744a7734abc13151fa169dfdaac..24fa912d53abf6c95eaf7d91511d29073f2c5673 100644 --- a/camera/VendorTagDescriptor.cpp +++ b/camera/VendorTagDescriptor.cpp @@ -660,6 +660,16 @@ sp VendorTagDescriptorCache::getGlobalVendorTagCache() return sGlobalVendorTagDescriptorCache; } +bool VendorTagDescriptorCache::isVendorCachePresent(metadata_vendor_id_t vendorId) { + Mutex::Autolock al(sLock); + if ((sGlobalVendorTagDescriptorCache.get() != nullptr) && + (sGlobalVendorTagDescriptorCache->getVendorIdsAndTagDescriptors().find(vendorId) != + sGlobalVendorTagDescriptorCache->getVendorIdsAndTagDescriptors().end())) { + return true; + } + return false; +} + extern "C" { int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* /*v*/) { diff --git a/camera/aidl/android/hardware/CameraSessionStats.aidl b/camera/aidl/android/hardware/CameraSessionStats.aidl new file mode 100644 index 0000000000000000000000000000000000000000..a8e6774d0469c4aed35b60c07963fb996d8cf70d --- /dev/null +++ b/camera/aidl/android/hardware/CameraSessionStats.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +/** @hide */ +parcelable CameraSessionStats cpp_header "camera/CameraSessionStats.h"; diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl index ac7a35b9f24245f7746b1b477b8b386b74321305..78a77d4ea1e0bc38109223949306beec23f6ea78 100644 --- a/camera/aidl/android/hardware/ICameraService.aidl +++ b/camera/aidl/android/hardware/ICameraService.aidl @@ -20,6 +20,8 @@ import android.hardware.ICamera; import android.hardware.ICameraClient; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.ICameraDeviceCallbacks; +import android.hardware.camera2.ICameraInjectionCallback; +import android.hardware.camera2.ICameraInjectionSession; import android.hardware.camera2.params.VendorTagDescriptor; import android.hardware.camera2.params.VendorTagDescriptorCache; import android.hardware.camera2.utils.ConcurrentCameraIdCombination; @@ -69,7 +71,7 @@ interface ICameraService /** * Default UID/PID values for non-privileged callers of - * connect(), connectDevice(), and connectLegacy() + * connect() and connectDevice() */ const int USE_CALLING_UID = -1; const int USE_CALLING_PID = -1; @@ -80,7 +82,8 @@ interface ICameraService ICamera connect(ICameraClient client, int cameraId, String opPackageName, - int clientUid, int clientPid); + int clientUid, int clientPid, + int targetSdkVersion); /** * Open a camera device through the new camera API @@ -90,21 +93,8 @@ interface ICameraService String cameraId, String opPackageName, @nullable String featureId, - int clientUid); - - /** - * halVersion constant for connectLegacy - */ - const int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1; - - /** - * Open a camera device in legacy mode, if supported by the camera module HAL. - */ - ICamera connectLegacy(ICameraClient client, - int cameraId, - int halVersion, - String opPackageName, - int clientUid); + int clientUid, int oomScoreOffset, + int targetSdkVersion); /** * Add listener for changes to camera device and flashlight state. @@ -126,13 +116,15 @@ interface ICameraService * corresponding camera ids. * * @param sessions the set of camera id and session configuration pairs to be queried. + * @param targetSdkVersion the target sdk level of the application calling this function. * @return true - the set of concurrent camera id and stream combinations is supported. * false - the set of concurrent camera id and stream combinations is not supported * OR the method was called with a set of camera ids not returned by * getConcurrentCameraIds(). */ boolean isConcurrentSessionConfigurationSupported( - in CameraIdAndSessionConfiguration[] sessions); + in CameraIdAndSessionConfiguration[] sessions, + int targetSdkVersion); /** * Remove listener for changes to camera device and flashlight state. @@ -143,7 +135,7 @@ interface ICameraService * Read the static camera metadata for a camera device. * Only supported for device HAL versions >= 3.2 */ - CameraMetadataNative getCameraCharacteristics(String cameraId); + CameraMetadataNative getCameraCharacteristics(String cameraId, int targetSdkVersion); /** * Read in the vendor tag descriptors from the camera module HAL. @@ -175,6 +167,9 @@ interface ICameraService boolean supportsCameraApi(String cameraId, int apiVersion); // Determines if a cameraId is a hidden physical camera of a logical multi-camera. boolean isHiddenPhysicalCamera(String cameraId); + // Inject the external camera to replace the internal camera session. + ICameraInjectionSession injectCamera(String packageName, String internalCamId, + String externalCamId, in ICameraInjectionCallback CameraInjectionCallback); void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder); @@ -187,6 +182,13 @@ interface ICameraService const int EVENT_USER_SWITCHED = 1; // The argument is the set of new foreground user IDs. oneway void notifySystemEvent(int eventId, in int[] args); + /** + * Notify the camera service of a display configuration change. + * + * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission. + */ + oneway void notifyDisplayConfigurationChange(); + /** * Notify the camera service of a device physical status change. May only be called from * a privileged process. diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl index 75759489e774a04ce4607fc54b2d07167643d0dc..bbb02891f41c8ef30d4fb301c16927f83c41af79 100644 --- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl +++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl @@ -16,11 +16,11 @@ package android.hardware; +import android.hardware.CameraSessionStats; + /** * Binder interface for the camera service proxy running in system_server. * - * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h - * * @hide */ interface ICameraServiceProxy @@ -30,30 +30,15 @@ interface ICameraServiceProxy */ oneway void pingForUserUpdate(); - /** - * Values for notifyCameraState newCameraState - */ - const int CAMERA_STATE_OPEN = 0; - const int CAMERA_STATE_ACTIVE = 1; - const int CAMERA_STATE_IDLE = 2; - const int CAMERA_STATE_CLOSED = 3; /** - * Values for notifyCameraState facing - */ - const int CAMERA_FACING_BACK = 0; - const int CAMERA_FACING_FRONT = 1; - const int CAMERA_FACING_EXTERNAL = 2; - - /** - * Values for notifyCameraState api level + * Update the status of a camera device. */ - const int CAMERA_API_LEVEL_1 = 1; - const int CAMERA_API_LEVEL_2 = 2; + oneway void notifyCameraState(in CameraSessionStats cameraSessionStats); /** - * Update the status of a camera device. + * Reports whether the top activity needs a rotate and crop override. */ - oneway void notifyCameraState(String cameraId, int facing, int newCameraState, - String clientName, int apiLevel); + boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation, + int lensFacing); } diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl index b183ccc822360c016c677f2dc8214342fb239b4e..8e1fcc06ee7f3003f56fd74e78cbfa891be08c96 100644 --- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl @@ -83,9 +83,11 @@ interface ICameraDeviceUser * @param operatingMode The kind of session to create; either NORMAL_MODE or * CONSTRAINED_HIGH_SPEED_MODE. Must be a non-negative value. * @param sessionParams Session wide camera parameters + * @param startTimeMs The timestamp of session creation start, measured by + * SystemClock.uptimeMillis. * @return a list of stream ids that can be used in offline mode via "switchToOffline" */ - int[] endConfigure(int operatingMode, in CameraMetadataNative sessionParams); + int[] endConfigure(int operatingMode, in CameraMetadataNative sessionParams, long startTimeMs); /** * Check whether a particular session configuration has camera device @@ -117,10 +119,11 @@ interface ICameraDeviceUser * @param width Width of the input buffers * @param height Height of the input buffers * @param format Format of the input buffers. One of HAL_PIXEL_FORMAT_*. + * @param isMultiResolution Whether the input stream supports variable resolution image. * * @return new stream ID */ - int createInputStream(int width, int height, int format); + int createInputStream(int width, int height, int format, boolean isMultiResolution); /** * Get the surface of the input stream. diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl new file mode 100644 index 0000000000000000000000000000000000000000..9791352c2b99fa5543447e90fcf942de055d49e7 --- /dev/null +++ b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.hardware.camera2.ICameraInjectionSession; + +/** + * Binder interface used to call back the error state injected by the external camera, + * and camera service can be switched back to internal camera when binder signals process death. + * + * @hide + */ +interface ICameraInjectionCallback +{ + // Error codes for onInjectionError + // To indicate all invalid error codes + const int ERROR_INJECTION_INVALID_ERROR = -1; + // To indicate the camera injection session has encountered a fatal error, such as injection + // init failure, configure failure or injecting failure etc. + const int ERROR_INJECTION_SESSION = 0; + // To indicate the camera service has encountered a fatal error. + const int ERROR_INJECTION_SERVICE = 1; + // To indicate the injection camera does not support certain camera functions, such as + // unsupport stream format, no capture/record function or no multi-camera function etc. + // When this error occurs, the default processing is still in the inject state, and the app is + // notified to display an error message and a black screen. + const int ERROR_INJECTION_UNSUPPORTED = 2; + + oneway void onInjectionError(int errorCode); +} diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl new file mode 100644 index 0000000000000000000000000000000000000000..c31c30bcad746312313938eda55a3fc166df6720 --- /dev/null +++ b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +/** @hide */ +interface ICameraInjectionSession +{ + oneway void stopInjection(); +} diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp index 4e9b27d50dff44b38635b4625240e72514edf716..2bccd870b8a7cf9c28595748387f89cfab35e558 100644 --- a/camera/camera2/OutputConfiguration.cpp +++ b/camera/camera2/OutputConfiguration.cpp @@ -68,6 +68,14 @@ String16 OutputConfiguration::getPhysicalCameraId() const { return mPhysicalCameraId; } +bool OutputConfiguration::isMultiResolution() const { + return mIsMultiResolution; +} + +const std::vector &OutputConfiguration::getSensorPixelModesUsed() const { + return mSensorPixelModesUsed; +} + OutputConfiguration::OutputConfiguration() : mRotation(INVALID_ROTATION), mSurfaceSetID(INVALID_SET_ID), @@ -75,7 +83,8 @@ OutputConfiguration::OutputConfiguration() : mWidth(0), mHeight(0), mIsDeferred(false), - mIsShared(false) { + mIsShared(false), + mIsMultiResolution(false) { } OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) : @@ -145,6 +154,17 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { parcel->readString16(&mPhysicalCameraId); + int isMultiResolution = 0; + if ((err = parcel->readInt32(&isMultiResolution)) != OK) { + ALOGE("%s: Failed to read surface isMultiResolution flag from parcel", __FUNCTION__); + return err; + } + + std::vector sensorPixelModesUsed; + if ((err = parcel->readInt32Vector(&sensorPixelModesUsed)) != OK) { + ALOGE("%s: Failed to read sensor pixel mode(s) from parcel", __FUNCTION__); + return err; + } mRotation = rotation; mSurfaceSetID = setID; mSurfaceType = surfaceType; @@ -152,6 +172,7 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { mHeight = height; mIsDeferred = isDeferred != 0; mIsShared = isShared != 0; + mIsMultiResolution = isMultiResolution != 0; for (auto& surface : surfaceShims) { ALOGV("%s: OutputConfiguration: %p, name %s", __FUNCTION__, surface.graphicBufferProducer.get(), @@ -159,9 +180,11 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { mGbps.push_back(surface.graphicBufferProducer); } + mSensorPixelModesUsed = std::move(sensorPixelModesUsed); + ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d," - " physicalCameraId = %s", __FUNCTION__, mRotation, mSurfaceSetID, - mSurfaceType, String8(mPhysicalCameraId).string()); + " physicalCameraId = %s, isMultiResolution = %d", __FUNCTION__, mRotation, + mSurfaceSetID, mSurfaceType, String8(mPhysicalCameraId).string(), mIsMultiResolution); return err; } @@ -175,6 +198,7 @@ OutputConfiguration::OutputConfiguration(sp& gbp, int ro mIsDeferred = false; mIsShared = isShared; mPhysicalCameraId = physicalId; + mIsMultiResolution = false; } OutputConfiguration::OutputConfiguration( @@ -183,7 +207,7 @@ OutputConfiguration::OutputConfiguration( int width, int height, bool isShared) : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType), mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared), - mPhysicalCameraId(physicalCameraId) { } + mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false) { } status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const { @@ -224,24 +248,54 @@ status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const { err = parcel->writeString16(mPhysicalCameraId); if (err != OK) return err; + err = parcel->writeInt32(mIsMultiResolution ? 1 : 0); + if (err != OK) return err; + + err = parcel->writeParcelableVector(mSensorPixelModesUsed); + if (err != OK) return err; + return OK; } +template +static bool simpleVectorsEqual(T first, T second) { + if (first.size() != second.size()) { + return false; + } + + for (size_t i = 0; i < first.size(); i++) { + if (first[i] != second[i]) { + return false; + } + } + return true; +} + bool OutputConfiguration::gbpsEqual(const OutputConfiguration& other) const { const std::vector >& otherGbps = other.getGraphicBufferProducers(); + return simpleVectorsEqual(otherGbps, mGbps); +} - if (mGbps.size() != otherGbps.size()) { - return false; +bool OutputConfiguration::sensorPixelModesUsedEqual(const OutputConfiguration& other) const { + const std::vector& othersensorPixelModesUsed = other.getSensorPixelModesUsed(); + return simpleVectorsEqual(othersensorPixelModesUsed, mSensorPixelModesUsed); +} + +bool OutputConfiguration::sensorPixelModesUsedLessThan(const OutputConfiguration& other) const { + const std::vector& spms = other.getSensorPixelModesUsed(); + + if (mSensorPixelModesUsed.size() != spms.size()) { + return mSensorPixelModesUsed.size() < spms.size(); } - for (size_t i = 0; i < mGbps.size(); i++) { - if (mGbps[i] != otherGbps[i]) { - return false; + for (size_t i = 0; i < spms.size(); i++) { + if (mSensorPixelModesUsed[i] != spms[i]) { + return mSensorPixelModesUsed[i] < spms[i]; } } - return true; + return false; } bool OutputConfiguration::gbpsLessThan(const OutputConfiguration& other) const { diff --git a/camera/camera2/SessionConfiguration.cpp b/camera/camera2/SessionConfiguration.cpp index a431a33525cfb22eeaa38eb0c26156ddb4cc5044..7cf6087830b3fbcc8d7ed555e730708de399ff83 100644 --- a/camera/camera2/SessionConfiguration.cpp +++ b/camera/camera2/SessionConfiguration.cpp @@ -55,6 +55,12 @@ status_t SessionConfiguration::readFromParcel(const android::Parcel* parcel) { return err; } + bool inputIsMultiResolution = false; + if ((err = parcel->readBool(&inputIsMultiResolution)) != OK) { + ALOGE("%s: Failed to read input multi-resolution flag from parcel", __FUNCTION__); + return err; + } + std::vector outputStreams; if ((err = parcel->readParcelableVector(&outputStreams)) != OK) { ALOGE("%s: Failed to read output configurations from parcel", __FUNCTION__); @@ -65,6 +71,7 @@ status_t SessionConfiguration::readFromParcel(const android::Parcel* parcel) { mInputWidth = inputWidth; mInputHeight = inputHeight; mInputFormat = inputFormat; + mInputIsMultiResolution = inputIsMultiResolution; for (auto& stream : outputStreams) { mOutputStreams.push_back(stream); } @@ -90,6 +97,9 @@ status_t SessionConfiguration::writeToParcel(android::Parcel* parcel) const { err = parcel->writeInt32(mInputFormat); if (err != OK) return err; + err = parcel->writeBool(mInputIsMultiResolution); + if (err != OK) return err; + err = parcel->writeParcelableVector(mOutputStreams); if (err != OK) return err; diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp index f58f20c0963a90e71f7f7a7a96f4353dd522f58c..8ca892001251fcd5ac52ea36df3f0fd3549e16a5 100644 --- a/camera/cameraserver/Android.bp +++ b/camera/cameraserver/Android.bp @@ -42,11 +42,12 @@ cc_binary { "android.hardware.camera.provider@2.4", "android.hardware.camera.provider@2.5", "android.hardware.camera.provider@2.6", + "android.hardware.camera.provider@2.7", "android.hardware.camera.device@1.0", "android.hardware.camera.device@3.2", "android.hardware.camera.device@3.4", ], - compile_multilib: "prefer32", + compile_multilib: "first", cflags: [ "-Wall", "-Wextra", @@ -57,6 +58,6 @@ cc_binary { init_rc: ["cameraserver.rc"], vintf_fragments: [ - "manifest_android.frameworks.cameraservice.service@2.1.xml", + "manifest_android.frameworks.cameraservice.service@2.2.xml", ], } diff --git a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml similarity index 90% rename from camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml rename to camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml index 5a15b35b322ab53ffb4eb7f3ad355d24b078d695..eeafc91cbc6f4c1f3ce648e7a759f840f31c1e55 100644 --- a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml +++ b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml @@ -2,7 +2,7 @@ android.frameworks.cameraservice.service hwbinder - 2.1 + 2.2 ICameraService default diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h index 2cdb617e8193a20bbfbb86646a651862087043f1..58ccd699d2097fe484c088db3bee86684bc0acff 100644 --- a/camera/include/camera/Camera.h +++ b/camera/include/camera/Camera.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -59,7 +58,7 @@ struct CameraTraits typedef ::android::hardware::ICameraClient TCamCallbacks; typedef ::android::binder::Status(::android::hardware::ICameraService::*TCamConnectService) (const sp<::android::hardware::ICameraClient>&, - int, const String16&, int, int, + int, const String16&, int, int, int, /*out*/ sp<::android::hardware::ICamera>*); static TCamConnectService fnConnectService; @@ -82,11 +81,7 @@ public: static sp create(const sp<::android::hardware::ICamera>& camera); static sp connect(int cameraId, const String16& clientPackageName, - int clientUid, int clientPid); - - static status_t connectLegacy(int cameraId, int halVersion, - const String16& clientPackageName, - int clientUid, sp& camera); + int clientUid, int clientPid, int targetSdkVersion); virtual ~Camera(); @@ -154,7 +149,6 @@ public: status_t setVideoTarget(const sp& bufferProducer); void setListener(const sp& listener); - void setRecordingProxyListener(const sp& listener); // Configure preview callbacks to app. Only one of the older // callbacks or the callback surface can be active at the same time; @@ -187,12 +181,8 @@ public: explicit RecordingProxy(const sp& camera); // ICameraRecordingProxy interface - virtual status_t startRecording(const sp& listener); + virtual status_t startRecording(); virtual void stopRecording(); - virtual void releaseRecordingFrame(const sp& mem); - virtual void releaseRecordingFrameHandle(native_handle_t* handle); - virtual void releaseRecordingFrameHandleBatch( - const std::vector& handles); private: sp mCamera; @@ -203,8 +193,6 @@ protected: Camera(const Camera&); Camera& operator=(const Camera); - sp mRecordingProxyListener; - friend class CameraBase; }; diff --git a/camera/include/camera/CameraBase.h b/camera/include/camera/CameraBase.h index 499b0e6b0959797ef9b7854214bc1a21b0aca518..e156994798e10df296c37f5a5594f4f0030192f1 100644 --- a/camera/include/camera/CameraBase.h +++ b/camera/include/camera/CameraBase.h @@ -113,7 +113,7 @@ public: static sp connect(int cameraId, const String16& clientPackageName, - int clientUid, int clientPid); + int clientUid, int clientPid, int targetSdkVersion); virtual void disconnect(); void setListener(const sp& listener); diff --git a/camera/include/camera/CameraMetadata.h b/camera/include/camera/CameraMetadata.h index e883ffa651f8fd3ef9210d4856f7f6e07e6e87ae..c56ee6da8ba53a97af918d3bdad734d79bcc8db1 100644 --- a/camera/include/camera/CameraMetadata.h +++ b/camera/include/camera/CameraMetadata.h @@ -242,6 +242,11 @@ class CameraMetadata: public Parcelable { static status_t getTagFromName(const char *name, const VendorTagDescriptor* vTags, uint32_t *tag); + /** + * Return the current vendor tag id associated with this metadata. + */ + metadata_vendor_id_t getVendorId(); + private: camera_metadata_t *mBuffer; mutable bool mLocked; diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h new file mode 100644 index 0000000000000000000000000000000000000000..c398acaa02e4b65122b4537f1b7dba385293c58a --- /dev/null +++ b/camera/include/camera/CameraSessionStats.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H +#define ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H + +#include + +namespace android { +namespace hardware { + +/** + * Camera stream info and statistics + */ +class CameraStreamStats : public android::Parcelable { +public: + enum HistogramType { + HISTOGRAM_TYPE_UNKNOWN = 0, + HISTOGRAM_TYPE_CAPTURE_LATENCY = 1, + }; + + int mWidth; + int mHeight; + int mFormat; + int mDataSpace; + int64_t mUsage; + + // The number of requested buffers + int64_t mRequestCount; + // The number of buffer errors + int64_t mErrorCount; + + // The capture latency of 1st request for this stream + int32_t mStartLatencyMs; + + // Buffer count info + int mMaxHalBuffers; + int mMaxAppBuffers; + + // Histogram type. So far only capture latency histogram is supported. + int mHistogramType; + // The bounary values separating adjacent histogram bins. + // A vector of {h1, h2, h3} represents bins of [0, h1), [h1, h2), [h2, h3), + // and [h3, infinity) + std::vector mHistogramBins; + // The counts for all histogram bins. + // size(mHistogramBins) + 1 = size(mHistogramCounts) + std::vector mHistogramCounts; + + CameraStreamStats() : + mWidth(0), mHeight(0), mFormat(0), mDataSpace(0), mUsage(0), + mRequestCount(0), mErrorCount(0), mStartLatencyMs(0), + mMaxHalBuffers(0), mMaxAppBuffers(0), mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {} + CameraStreamStats(int width, int height, int format, int dataSpace, int64_t usage, + int maxHalBuffers, int maxAppBuffers) + : mWidth(width), mHeight(height), mFormat(format), mDataSpace(dataSpace), + mUsage(usage), mRequestCount(0), mErrorCount(0), mStartLatencyMs(0), + mMaxHalBuffers(maxHalBuffers), mMaxAppBuffers(maxAppBuffers), + mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {} + + virtual status_t readFromParcel(const android::Parcel* parcel) override; + virtual status_t writeToParcel(android::Parcel* parcel) const override; +}; + +/** + * Camera session statistics + * + * This includes session wide info and stream statistics. + */ +class CameraSessionStats : public android::Parcelable { +public: + /** + * Values for notifyCameraState newCameraState + */ + static const int CAMERA_STATE_OPEN; + static const int CAMERA_STATE_ACTIVE; + static const int CAMERA_STATE_IDLE; + static const int CAMERA_STATE_CLOSED; + + /** + * Values for notifyCameraState facing + */ + static const int CAMERA_FACING_BACK; + static const int CAMERA_FACING_FRONT; + static const int CAMERA_FACING_EXTERNAL; + + /** + * Values for notifyCameraState api level + */ + static const int CAMERA_API_LEVEL_1; + static const int CAMERA_API_LEVEL_2; + + String16 mCameraId; + int mFacing; + int mNewCameraState; + String16 mClientName; + int mApiLevel; + bool mIsNdk; + // latency in ms for camera open, close, or session creation. + int mLatencyMs; + + // Session info and statistics + int mSessionType; + int mInternalReconfigure; + // The number of capture requests + int64_t mRequestCount; + // The number of result error + int64_t mResultErrorCount; + // Whether the device runs into an error state + bool mDeviceError; + std::vector mStreamStats; + + // Constructors + CameraSessionStats(); + CameraSessionStats(const String16& cameraId, int facing, int newCameraState, + const String16& clientName, int apiLevel, bool isNdk, int32_t latencyMs); + + virtual status_t readFromParcel(const android::Parcel* parcel) override; + virtual status_t writeToParcel(android::Parcel* parcel) const override; +}; + +}; // namespace hardware +}; // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H diff --git a/camera/include/camera/ICameraRecordingProxy.h b/camera/include/camera/ICameraRecordingProxy.h index 02af2f360406cc5dc0e2dcc24a38132c5a3ca02f..4306dc182ca1d42d8774455de046b6a9af525b5d 100644 --- a/camera/include/camera/ICameraRecordingProxy.h +++ b/camera/include/camera/ICameraRecordingProxy.h @@ -24,13 +24,11 @@ namespace android { -class ICameraRecordingProxyListener; -class IMemory; class Parcel; /* - * The purpose of ICameraRecordingProxy and ICameraRecordingProxyListener is to - * allow applications using the camera during recording. + * The purpose of ICameraRecordingProxy is to + * allow applications to use the camera during recording with the old camera API. * * Camera service allows only one client at a time. Since camcorder application * needs to own the camera to do things like zoom, the media recorder cannot @@ -42,35 +40,29 @@ class Parcel; * ICameraRecordingProxy * startRecording() * stopRecording() - * releaseRecordingFrame() * - * ICameraRecordingProxyListener - * dataCallbackTimestamp() - * The camcorder app opens the camera and starts the preview. The app passes * ICamera and ICameraRecordingProxy to the media recorder by * MediaRecorder::setCamera(). The recorder uses ICamera to setup the camera in * MediaRecorder::start(). After setup, the recorder disconnects from camera - * service. The recorder calls ICameraRecordingProxy::startRecording() and - * passes a ICameraRecordingProxyListener to the app. The app connects back to - * camera service and starts the recording. The app owns the camera and can do - * things like zoom. The media recorder receives the video frames from the - * listener and releases them by ICameraRecordingProxy::releaseRecordingFrame. - * The recorder calls ICameraRecordingProxy::stopRecording() to stop the - * recording. + * service. The recorder calls ICameraRecordingProxy::startRecording() and The + * app owns the camera and can do things like zoom. The media recorder receives + * the video frames via a buffer queue. The recorder calls + * ICameraRecordingProxy::stopRecording() to stop the recording. * * The call sequences are as follows: * 1. The app: Camera.unlock(). * 2. The app: MediaRecorder.setCamera(). * 3. Start recording * (1) The app: MediaRecorder.start(). - * (2) The recorder: ICamera.unlock() and ICamera.disconnect(). - * (3) The recorder: ICameraRecordingProxy.startRecording(). - * (4) The app: ICamera.reconnect(). - * (5) The app: ICamera.startRecording(). + * (2) The recorder: ICamera.setVideoTarget(buffer queue). + * (3) The recorder: ICamera.unlock() and ICamera.disconnect(). + * (4) The recorder: ICameraRecordingProxy.startRecording(). + * (5) The app: ICamera.reconnect(). + * (6) The app: ICamera.startRecording(). * 4. During recording - * (1) The recorder: receive frames from ICameraRecordingProxyListener.dataCallbackTimestamp() - * (2) The recorder: release frames by ICameraRecordingProxy.releaseRecordingFrame(). + * (1) The recorder: receive frames via a buffer queue + * (2) The recorder: release frames via a buffer queue * 5. Stop recording * (1) The app: MediaRecorder.stop() * (2) The recorder: ICameraRecordingProxy.stopRecording(). @@ -82,12 +74,8 @@ class ICameraRecordingProxy: public IInterface public: DECLARE_META_INTERFACE(CameraRecordingProxy); - virtual status_t startRecording(const sp& listener) = 0; + virtual status_t startRecording() = 0; virtual void stopRecording() = 0; - virtual void releaseRecordingFrame(const sp& mem) = 0; - virtual void releaseRecordingFrameHandle(native_handle_t *handle) = 0; - virtual void releaseRecordingFrameHandleBatch( - const std::vector& handles) = 0; }; // ---------------------------------------------------------------------------- diff --git a/camera/include/camera/ICameraRecordingProxyListener.h b/camera/include/camera/ICameraRecordingProxyListener.h deleted file mode 100644 index da03c56b6f512661c53aa72ca6343b6999f418f2..0000000000000000000000000000000000000000 --- a/camera/include/camera/ICameraRecordingProxyListener.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H -#define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H - -#include -#include -#include -#include -#include -#include - -namespace android { - -class Parcel; -class IMemory; - -class ICameraRecordingProxyListener: public IInterface -{ -public: - DECLARE_META_INTERFACE(CameraRecordingProxyListener); - - virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp& data) = 0; - - virtual void recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, - native_handle_t* handle) = 0; - - virtual void recordingFrameHandleCallbackTimestampBatch( - const std::vector& timestamps, - const std::vector& handles) = 0; -}; - -// ---------------------------------------------------------------------------- - -class BnCameraRecordingProxyListener: public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -}; // namespace android - -#endif diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h index b2fbf3a923f9234cae47a5dbb5a456ac02c42290..b3440d57895392f2d2c240e67df91ad61f0a7030 100644 --- a/camera/include/camera/VendorTagDescriptor.h +++ b/camera/include/camera/VendorTagDescriptor.h @@ -249,6 +249,12 @@ class VendorTagDescriptorCache : */ static void clearGlobalVendorTagCache(); + /** + * Return true if given vendor id is present in the vendor tag caches, return + * false otherwise. + */ + static bool isVendorCachePresent(metadata_vendor_id_t vendorId); + }; } /* namespace android */ diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h index 95c4f39d91869dd658344dcac45644f9bd28bef5..f80ed3a8e6787fc1b571db003bd49e025439681b 100644 --- a/camera/include/camera/camera2/OutputConfiguration.h +++ b/camera/include/camera/camera2/OutputConfiguration.h @@ -47,6 +47,10 @@ public: bool isDeferred() const; bool isShared() const; String16 getPhysicalCameraId() const; + bool isMultiResolution() const; + + // set of sensor pixel mode resolutions allowed {MAX_RESOLUTION, DEFAULT_MODE}; + const std::vector& getSensorPixelModesUsed() const; /** * Keep impl up-to-date with OutputConfiguration.java in frameworks/base */ @@ -83,7 +87,9 @@ public: mIsDeferred == other.mIsDeferred && mIsShared == other.mIsShared && gbpsEqual(other) && - mPhysicalCameraId == other.mPhysicalCameraId ); + mPhysicalCameraId == other.mPhysicalCameraId && + mIsMultiResolution == other.mIsMultiResolution && + sensorPixelModesUsedEqual(other)); } bool operator != (const OutputConfiguration& other) const { return !(*this == other); @@ -114,13 +120,22 @@ public: if (mPhysicalCameraId != other.mPhysicalCameraId) { return mPhysicalCameraId < other.mPhysicalCameraId; } + if (mIsMultiResolution != other.mIsMultiResolution) { + return mIsMultiResolution < other.mIsMultiResolution; + } + if (!sensorPixelModesUsedEqual(other)) { + return sensorPixelModesUsedLessThan(other); + } return gbpsLessThan(other); } + bool operator > (const OutputConfiguration& other) const { return (*this != other && !(*this < other)); } bool gbpsEqual(const OutputConfiguration& other) const; + bool sensorPixelModesUsedEqual(const OutputConfiguration& other) const; + bool sensorPixelModesUsedLessThan(const OutputConfiguration& other) const; bool gbpsLessThan(const OutputConfiguration& other) const; void addGraphicProducer(sp gbp) {mGbps.push_back(gbp);} private: @@ -133,6 +148,8 @@ private: bool mIsDeferred; bool mIsShared; String16 mPhysicalCameraId; + bool mIsMultiResolution; + std::vector mSensorPixelModesUsed; }; } // namespace params } // namespace camera2 diff --git a/camera/include/camera/camera2/SessionConfiguration.h b/camera/include/camera/camera2/SessionConfiguration.h index 64288ed1bba627e27d6bead3c8a3f20173afacfc..29913f6f0b89a4a85a622cab0ff670b6ae0cb78e 100644 --- a/camera/include/camera/camera2/SessionConfiguration.h +++ b/camera/include/camera/camera2/SessionConfiguration.h @@ -38,6 +38,7 @@ public: int getInputHeight() const { return mInputHeight; } int getInputFormat() const { return mInputFormat; } int getOperatingMode() const { return mOperatingMode; } + bool inputIsMultiResolution() const { return mInputIsMultiResolution; } virtual status_t writeToParcel(android::Parcel* parcel) const override; virtual status_t readFromParcel(const android::Parcel* parcel) override; @@ -61,7 +62,8 @@ public: mInputWidth == other.mInputWidth && mInputHeight == other.mInputHeight && mInputFormat == other.mInputFormat && - mOperatingMode == other.mOperatingMode); + mOperatingMode == other.mOperatingMode && + mInputIsMultiResolution == other.mInputIsMultiResolution); } bool operator != (const SessionConfiguration& other) const { @@ -83,6 +85,10 @@ public: return mInputFormat < other.mInputFormat; } + if (mInputIsMultiResolution != other.mInputIsMultiResolution) { + return mInputIsMultiResolution < other.mInputIsMultiResolution; + } + if (mOperatingMode != other.mOperatingMode) { return mOperatingMode < other.mOperatingMode; } @@ -104,6 +110,7 @@ private: std::vector mOutputStreams; int mInputWidth, mInputHeight, mInputFormat, mOperatingMode; + bool mInputIsMultiResolution = false; }; } // namespace params } // namespace camera2 diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp index 24eaba65e7e14da80b762db5838df4cd245a07be..fef873b4c4467805f00e53893030981e59e90e25 100644 --- a/camera/ndk/Android.bp +++ b/camera/ndk/Android.bp @@ -93,7 +93,7 @@ cc_library_shared { ], cflags: [ "-fvisibility=hidden", - "-DEXPORT=__attribute__ ((visibility (\"default\")))", + "-DEXPORT=__attribute__((visibility(\"default\")))", "-Wall", "-Wextra", "-Werror", @@ -152,9 +152,11 @@ cc_library_shared { "libcamera_metadata", "libmediandk", "android.frameworks.cameraservice.device@2.0", + "android.frameworks.cameraservice.device@2.1", "android.frameworks.cameraservice.common@2.0", "android.frameworks.cameraservice.service@2.0", "android.frameworks.cameraservice.service@2.1", + "android.frameworks.cameraservice.service@2.2", ], static_libs: [ "android.hardware.camera.common@1.0-helper", diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 78974ae7fc337bc114cdc884c59234629b2b824f..dd652c70c52ea4661690abafdc8af6337bd8834d 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -186,6 +186,7 @@ CameraDevice::createCaptureSession( const ACaptureRequest* sessionParameters, const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session) { + nsecs_t startTimeNs = systemTime(); sp currentSession = mCurrentSession.promote(); Mutex::Autolock _l(mDeviceLock); camera_status_t ret = checkCameraClosedOrErrorLocked(); @@ -199,7 +200,7 @@ CameraDevice::createCaptureSession( } // Create new session - ret = configureStreamsLocked(outputs, sessionParameters); + ret = configureStreamsLocked(outputs, sessionParameters, startTimeNs); if (ret != ACAMERA_OK) { ALOGE("Fail to create new session. cannot configure streams"); return ret; @@ -450,7 +451,11 @@ CameraDevice::notifySessionEndOfLifeLocked(ACameraCaptureSession* session) { } // No new session, unconfigure now - camera_status_t ret = configureStreamsLocked(nullptr, nullptr); + // Note: The unconfiguration of session won't be accounted for session + // latency because a stream configuration with 0 streams won't ever become + // active. + nsecs_t startTimeNs = systemTime(); + camera_status_t ret = configureStreamsLocked(nullptr, nullptr, startTimeNs); if (ret != ACAMERA_OK) { ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret); } @@ -609,7 +614,7 @@ CameraDevice::getSurfaceFromANativeWindow( camera_status_t CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, - const ACaptureRequest* sessionParameters) { + const ACaptureRequest* sessionParameters, nsecs_t startTimeNs) { ACaptureSessionOutputContainer emptyOutput; if (outputs == nullptr) { outputs = &emptyOutput; @@ -711,7 +716,8 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu params.append(sessionParameters->settings->getInternalData()); } std::vector offlineStreamIds; - remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params, &offlineStreamIds); + remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params, + ns2ms(startTimeNs), &offlineStreamIds); if (remoteRet.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT) { ALOGE("Camera device %s cannnot support app output configuration: %s", getId(), remoteRet.toString8().string()); diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 3073dfb023bae0571c5635b9bcbe0add5e51ea93..344d9644183349dbeb2b5357f26e8d86aae0d47f 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -167,7 +167,7 @@ class CameraDevice final : public RefBase { void notifySessionEndOfLifeLocked(ACameraCaptureSession* session); camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, - const ACaptureRequest* sessionParameters); + const ACaptureRequest* sessionParameters, nsecs_t startTimeNs); // Input message will be posted and cleared after this returns void postSessionMsgAndCleanup(sp& msg); diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index 73cabbfcc94fc78fe9b2d8ba24c53f5ce787c932..95ef2b2af1f828c9901bb42f9ff780adb87c4fee 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -689,7 +689,9 @@ camera_status_t ACameraManager::getCameraCharacteristics( return ACAMERA_ERROR_CAMERA_DISCONNECTED; } CameraMetadata rawMetadata; - binder::Status serviceRet = cs->getCameraCharacteristics(String16(cameraIdStr), &rawMetadata); + int targetSdkVersion = android_get_application_target_sdk_version(); + binder::Status serviceRet = cs->getCameraCharacteristics(String16(cameraIdStr), + targetSdkVersion, &rawMetadata); if (!serviceRet.isOk()) { switch(serviceRet.serviceSpecificErrorCode()) { case hardware::ICameraService::ERROR_DISCONNECTED: @@ -735,11 +737,13 @@ ACameraManager::openCamera( sp callbacks = device->getServiceCallback(); sp deviceRemote; + int targetSdkVersion = android_get_application_target_sdk_version(); // No way to get package name from native. // Send a zero length package name and let camera service figure it out from UID binder::Status serviceRet = cs->connectDevice( callbacks, String16(cameraId), String16(""), {}, - hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote); + hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/0, + targetSdkVersion, /*out*/&deviceRemote); if (!serviceRet.isOk()) { ALOGE("%s: connect camera device failed: %s", __FUNCTION__, serviceRet.toString8().string()); diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index bfa60d98c64a7205e6bec3e35d97fc10ef2b3a63..dab2fefc5ca5f962ecd5fd7f446095375f4f1a4a 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -24,6 +24,28 @@ using namespace android; +// Formats not listed in the public API, but still available to AImageReader +// Enum value must match corresponding enum in ui/PublicFormat.h (which is not +// available to VNDK) +enum AIMAGE_PRIVATE_FORMATS { + /** + * Unprocessed implementation-dependent raw + * depth measurements, opaque with 16 bit + * samples. + * + */ + + AIMAGE_FORMAT_RAW_DEPTH = 0x1002, + + /** + * Device specific 10 bits depth RAW image format. + * + *

Unprocessed implementation-dependent raw depth measurements, opaque with 10 bit samples + * and device specific bit layout.

+ */ + AIMAGE_FORMAT_RAW_DEPTH10 = 0x1003, +}; + /** * ACameraMetadata Implementation */ @@ -290,6 +312,10 @@ ACameraMetadata::filterStreamConfigurations() { format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD; } else if (format == HAL_PIXEL_FORMAT_Y16) { format = AIMAGE_FORMAT_DEPTH16; + } else if (format == HAL_PIXEL_FORMAT_RAW16) { + format = static_cast(AIMAGE_FORMAT_RAW_DEPTH); + } else if (format == HAL_PIXEL_FORMAT_RAW10) { + format = static_cast(AIMAGE_FORMAT_RAW_DEPTH10); } filteredDepthStreamConfigs.push_back(format); @@ -426,6 +452,7 @@ ACameraMetadata::getTags(/*out*/int32_t* numTags, camera_metadata_ro_entry_t entry; int ret = get_camera_metadata_ro_entry(rawMetadata, i, &entry); if (ret != 0) { + mData->unlock(rawMetadata); ALOGE("%s: error reading metadata index %zu", __FUNCTION__, i); return ACAMERA_ERROR_UNKNOWN; } @@ -527,11 +554,13 @@ ACameraMetadata::isCaptureRequestTag(const uint32_t tag) { case ACAMERA_LENS_OPTICAL_STABILIZATION_MODE: case ACAMERA_NOISE_REDUCTION_MODE: case ACAMERA_SCALER_CROP_REGION: + case ACAMERA_SCALER_ROTATE_AND_CROP: case ACAMERA_SENSOR_EXPOSURE_TIME: case ACAMERA_SENSOR_FRAME_DURATION: case ACAMERA_SENSOR_SENSITIVITY: case ACAMERA_SENSOR_TEST_PATTERN_DATA: case ACAMERA_SENSOR_TEST_PATTERN_MODE: + case ACAMERA_SENSOR_PIXEL_MODE: case ACAMERA_SHADING_MODE: case ACAMERA_STATISTICS_FACE_DETECT_MODE: case ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE: @@ -582,6 +611,7 @@ std::unordered_set ACameraMetadata::sSystemTags ({ ANDROID_SENSOR_PROFILE_HUE_SAT_MAP, ANDROID_SENSOR_PROFILE_TONE_CURVE, ANDROID_SENSOR_OPAQUE_RAW_SIZE, + ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION, ANDROID_SHADING_STRENGTH, ANDROID_STATISTICS_HISTOGRAM_MODE, ANDROID_STATISTICS_SHARPNESS_MAP_MODE, diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 6c1cf3395aabb7358545fe891443f122869f5e57..2b7f040be449c3884795ed41b1aa6dc6d027c0ee 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -61,6 +61,10 @@ typedef struct ACameraCaptureSession ACameraCaptureSession; */ typedef void (*ACameraCaptureSession_stateCallback)(void* context, ACameraCaptureSession *session); +/** + * Capture session state callbacks used in {@link ACameraDevice_createCaptureSession} and + * {@link ACameraDevice_createCaptureSessionWithSessionParameters} + */ typedef struct ACameraCaptureSession_stateCallbacks { /// optional application context. void* context; @@ -246,6 +250,10 @@ typedef void (*ACameraCaptureSession_captureCallback_bufferLost)( void* context, ACameraCaptureSession* session, ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber); +/** + * ACaptureCaptureSession_captureCallbacks structure used in + * {@link ACameraCaptureSession_capture} and {@link ACameraCaptureSession_setRepeatingRequest}. + */ typedef struct ACameraCaptureSession_captureCallbacks { /// optional application context. void* context; @@ -413,7 +421,10 @@ enum { */ void ACameraCaptureSession_close(ACameraCaptureSession* session); -struct ACameraDevice; +/** + * ACameraDevice is opaque type that provides access to a camera device. + * A pointer can be obtained using {@link ACameraManager_openCamera} method. + */ typedef struct ACameraDevice ACameraDevice; /** @@ -591,6 +602,10 @@ camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* sessi camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session) __INTRODUCED_IN(24); +/** + * Opaque object for capture session output, use {@link ACaptureSessionOutput_create} or + * {@link ACaptureSessionSharedOutput_create} to create an instance. + */ typedef struct ACaptureSessionOutput ACaptureSessionOutput; /** @@ -604,9 +619,9 @@ typedef struct ACaptureSessionOutput ACaptureSessionOutput; * *

Native windows that get removed must not be part of any active repeating or single/burst * request or have any pending results. Consider updating repeating requests via - * {@link ACaptureSessionOutput_setRepeatingRequest} and then wait for the last frame number + * {@link ACameraCaptureSession_setRepeatingRequest} and then wait for the last frame number * when the sequence completes - * {@link ACameraCaptureSession_captureCallback#onCaptureSequenceCompleted}.

+ * {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}.

* *

Native windows that get added must not be part of any other registered ACaptureSessionOutput * and must be compatible. Compatible windows must have matching format, rotation and @@ -713,7 +728,15 @@ typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks { * Same as ACameraCaptureSession_captureCallbacks */ void* context; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureStarted}. + */ ACameraCaptureSession_captureCallback_start onCaptureStarted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureProgressed}. + */ ACameraCaptureSession_captureCallback_result onCaptureProgressed; /** @@ -751,10 +774,18 @@ typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks { ACameraCaptureSession_logicalCamera_captureCallback_failed onLogicalCameraCaptureFailed; /** - * Same as ACameraCaptureSession_captureCallbacks + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}. */ ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceAborted}. + */ ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureBufferLost}. + */ ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost; } ACameraCaptureSession_logicalCamera_captureCallbacks; diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index f72fe8d8181a5036e28969551ab8c272dbb3791c..7be4bd391c0c4545e9f5e474f7b4de01afad99ee 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -124,6 +124,10 @@ typedef void (*ACameraDevice_StateCallback)(void* context, ACameraDevice* device */ typedef void (*ACameraDevice_ErrorStateCallback)(void* context, ACameraDevice* device, int error); +/** + * Applications' callbacks for camera device state changes, register with + * {@link ACameraManager_openCamera}. + */ typedef struct ACameraDevice_StateCallbacks { /// optional application context. void* context; @@ -198,6 +202,10 @@ camera_status_t ACameraDevice_close(ACameraDevice* device) __INTRODUCED_IN(24); */ const char* ACameraDevice_getId(const ACameraDevice* device) __INTRODUCED_IN(24); +/** + * Capture request pre-defined template types, used in {@link ACameraDevice_createCaptureRequest} + * and {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + */ typedef enum { /** * Create a request suitable for a camera preview window. Specifically, this @@ -301,10 +309,12 @@ camera_status_t ACameraDevice_createCaptureRequest( const ACameraDevice* device, ACameraDevice_request_template templateId, /*out*/ACaptureRequest** request) __INTRODUCED_IN(24); - +/** + * Opaque object for CaptureSessionOutput container, use + * {@link ACaptureSessionOutputContainer_create} to create an instance. + */ typedef struct ACaptureSessionOutputContainer ACaptureSessionOutputContainer; -typedef struct ACaptureSessionOutput ACaptureSessionOutput; /** * Create a capture session output container. @@ -844,7 +854,7 @@ camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds( /*out*/ACaptureRequest** request) __INTRODUCED_IN(29); /** - * Check whether a particular {@ACaptureSessionOutputContainer} is supported by + * Check whether a particular {@link ACaptureSessionOutputContainer} is supported by * the camera device. * *

This method performs a runtime check of a given {@link @@ -875,6 +885,7 @@ camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds( * device. *

  • {@link ACAMERA_ERROR_UNSUPPORTED_OPERATION} if the query operation is not * supported by the camera device.
  • + * */ camera_status_t ACameraDevice_isSessionConfigurationSupported( const ACameraDevice* device, diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h index 9d77eb4f6b57bd9f2e82b7b8118eefac7a051643..26db7f24fb01140fc35aa162d3e53e7e55cd35cf 100644 --- a/camera/ndk/include/camera/NdkCameraError.h +++ b/camera/ndk/include/camera/NdkCameraError.h @@ -40,7 +40,13 @@ __BEGIN_DECLS +/** + * Camera status enum types. + */ typedef enum { + /** + * Camera operation has succeeded. + */ ACAMERA_OK = 0, ACAMERA_ERROR_BASE = -10000, diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index be32b11479afab3c8eacac5751bb0c1c32cd9e90..729182e965ac246b72fbdf7fe11005b6e4e11ffc 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -326,7 +326,7 @@ typedef void (*ACameraManager_AccessPrioritiesChangedCallback)(void* context); * @see ACameraManager_registerExtendedAvailabilityCallback */ typedef struct ACameraManager_ExtendedAvailabilityListener { - /// + /// Called when a camera becomes available or unavailable ACameraManager_AvailabilityCallbacks availabilityCallbacks; /// Called when there is camera access permission change diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h index 0d5e6c406ced797469e8fbe2bd841c64f7705b13..b331d501d09a20f2ab402d3e6ae0bb14ce0e3a91 100644 --- a/camera/ndk/include/camera/NdkCameraMetadata.h +++ b/camera/ndk/include/camera/NdkCameraMetadata.h @@ -256,10 +256,12 @@ bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata, /** * Return a {@link ACameraMetadata} that references the same data as - * {@link cameraMetadata}, which is an instance of - * {@link android.hardware.camera2.CameraMetadata} (e.g., a - * {@link android.hardware.camera2.CameraCharacteristics} or - * {@link android.hardware.camera2.CaptureResult}). + * + * android.hardware.camera2.CameraMetadata from Java API. (e.g., a + * + * android.hardware.camera2.CameraCharacteristics + * or + * android.hardware.camera2.CaptureResult). * *

    The returned ACameraMetadata must be freed by the application by {@link ACameraMetadata_free} * after application is done using it.

    @@ -269,11 +271,13 @@ bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata, * the Java metadata is garbage collected. * * @param env the JNI environment. - * @param cameraMetadata the source {@link android.hardware.camera2.CameraMetadata} from which the + * @param cameraMetadata the source + android.hardware.camera2.CameraMetadata from which the * returned {@link ACameraMetadata} is a view. * - * @return a valid ACameraMetadata pointer or NULL if {@link cameraMetadata} is null or not a valid - * instance of {@link android.hardware.camera2.CameraMetadata}. + * @return a valid ACameraMetadata pointer or NULL if cameraMetadata is null or not a valid + * instance of + * android.hardware.camera2.CameraMetadata. * */ ACameraMetadata* ACameraMetadata_fromCameraMetadata(JNIEnv* env, jobject cameraMetadata) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index bc33d6a1144cbb80f050848d0853fc2fbb4407bc..86781e5373286cfa3b5bac289fe551e9ba8cb562 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -40,6 +40,7 @@ __BEGIN_DECLS + typedef enum acamera_metadata_section { ACAMERA_COLOR_CORRECTION, ACAMERA_CONTROL, @@ -526,6 +527,13 @@ typedef enum acamera_metadata_tag { * scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction * mode.

    + *

    For camera devices with the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability, + * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION / + * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the + * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    *

    The data representation is int[5 * area_count]. * Every five elements represent a metering region of (xmin, ymin, xmax, ymax, weight). * The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and @@ -535,7 +543,10 @@ typedef enum acamera_metadata_tag { * @see ACAMERA_DISTORTION_CORRECTION_MODE * @see ACAMERA_SCALER_CROP_REGION * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_PIXEL_MODE */ ACAMERA_CONTROL_AE_REGIONS = // int32[5*area_count] ACAMERA_CONTROL_START + 4, @@ -717,6 +728,12 @@ typedef enum acamera_metadata_tag { * scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction * mode.

    + *

    For camera devices with the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION / + * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the + * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    *

    The data representation is int[5 * area_count]. * Every five elements represent a metering region of (xmin, ymin, xmax, ymax, weight). * The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and @@ -726,7 +743,10 @@ typedef enum acamera_metadata_tag { * @see ACAMERA_DISTORTION_CORRECTION_MODE * @see ACAMERA_SCALER_CROP_REGION * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_PIXEL_MODE */ ACAMERA_CONTROL_AF_REGIONS = // int32[5*area_count] ACAMERA_CONTROL_START + 8, @@ -814,7 +834,7 @@ typedef enum acamera_metadata_tag { *

    * *

    This control is only effective if ACAMERA_CONTROL_MODE is AUTO.

    - *

    When set to the ON mode, the camera device's auto-white balance + *

    When set to the AUTO mode, the camera device's auto-white balance * routine is enabled, overriding the application's selected * ACAMERA_COLOR_CORRECTION_TRANSFORM, ACAMERA_COLOR_CORRECTION_GAINS and * ACAMERA_COLOR_CORRECTION_MODE. Note that when ACAMERA_CONTROL_AE_MODE @@ -903,6 +923,12 @@ typedef enum acamera_metadata_tag { * the scene as they do before. See ACAMERA_CONTROL_ZOOM_RATIO for details. Whether to use * activeArraySize or preCorrectionActiveArraySize still depends on distortion correction * mode.

    + *

    For camera devices with the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION / + * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the + * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    *

    The data representation is int[5 * area_count]. * Every five elements represent a metering region of (xmin, ymin, xmax, ymax, weight). * The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and @@ -912,7 +938,10 @@ typedef enum acamera_metadata_tag { * @see ACAMERA_DISTORTION_CORRECTION_MODE * @see ACAMERA_SCALER_CROP_REGION * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_PIXEL_MODE */ ACAMERA_CONTROL_AWB_REGIONS = // int32[5*area_count] ACAMERA_CONTROL_START + 12, @@ -1839,7 +1868,7 @@ typedef enum acamera_metadata_tag { *

  • If the camera device has BURST_CAPTURE capability, the frame rate requirement of * BURST_CAPTURE must still be met.
  • *
  • All streams not larger than the maximum streaming dimension for BOKEH_STILL_CAPTURE mode - * (queried via {@link ACAMERA_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_CAPABILITIES }) + * (queried via {@link ACAMERA_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_MAX_SIZES }) * will have preview bokeh effect applied.
  • * *

    When set to BOKEH_CONTINUOUS mode, configured streams dimension should not exceed this mode's @@ -1959,6 +1988,16 @@ typedef enum acamera_metadata_tag { * ACAMERA_CONTROL_ZOOM_RATIO is not 1.0, and ACAMERA_SCALER_CROP_REGION is set to be * windowboxing, the camera framework will override the ACAMERA_SCALER_CROP_REGION to be * the active array.

    + *

    In the capture request, if the application sets ACAMERA_CONTROL_ZOOM_RATIO to a + * value != 1.0, the ACAMERA_CONTROL_ZOOM_RATIO tag in the capture result reflects the + * effective zoom ratio achieved by the camera device, and the ACAMERA_SCALER_CROP_REGION + * adjusts for additional crops that are not zoom related. Otherwise, if the application + * sets ACAMERA_CONTROL_ZOOM_RATIO to 1.0, or does not set it at all, the + * ACAMERA_CONTROL_ZOOM_RATIO tag in the result metadata will also be 1.0.

    + *

    When the application requests a physical stream for a logical multi-camera, the + * ACAMERA_CONTROL_ZOOM_RATIO in the physical camera result metadata will be 1.0, and + * the ACAMERA_SCALER_CROP_REGION tag reflects the amount of zoom and crop done by the + * physical camera device.

    * * @see ACAMERA_CONTROL_AE_REGIONS * @see ACAMERA_CONTROL_ZOOM_RATIO @@ -2800,6 +2839,51 @@ typedef enum acamera_metadata_tag { */ ACAMERA_LENS_DISTORTION = // float[5] ACAMERA_LENS_START + 13, + /** + *

    The correction coefficients to correct for this camera device's + * radial and tangential lens distortion for a + * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

    Type: float[5]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    Analogous to ACAMERA_LENS_DISTORTION, when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_LENS_DISTORTION + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_LENS_DISTORTION_MAXIMUM_RESOLUTION = // float[5] + ACAMERA_LENS_START + 14, + /** + *

    The parameters for this camera device's intrinsic + * calibration when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

    Type: float[5]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    Analogous to ACAMERA_LENS_INTRINSIC_CALIBRATION, when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_LENS_INTRINSIC_CALIBRATION + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION = // float[5] + ACAMERA_LENS_START + 15, ACAMERA_LENS_END, /** @@ -3427,6 +3511,12 @@ typedef enum acamera_metadata_tag { * coordinate system is post-zoom, meaning that the activeArraySize or * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See * ACAMERA_CONTROL_ZOOM_RATIO for details.

    + *

    For camera devices with the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION / + * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the + * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    *

    The data representation is int[4], which maps to (left, top, width, height).

    * * @see ACAMERA_CONTROL_AE_TARGET_FPS_RANGE @@ -3435,7 +3525,10 @@ typedef enum acamera_metadata_tag { * @see ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM * @see ACAMERA_SCALER_CROPPING_TYPE * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_PIXEL_MODE */ ACAMERA_SCALER_CROP_REGION = // int32[4] ACAMERA_SCALER_START, @@ -3493,8 +3586,8 @@ typedef enum acamera_metadata_tag { *

    Not all output formats may be supported in a configuration with * an input stream of a particular format. For more details, see * android.scaler.availableInputOutputFormatsMap.

    - *

    The following table describes the minimum required output stream - * configurations based on the hardware level + *

    For applications targeting SDK version older than 31, the following table + * describes the minimum required output stream configurations based on the hardware level * (ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL):

    *

    Format | Size | Hardware Level | Notes * :-------------:|:--------------------------------------------:|:--------------:|:--------------: @@ -3506,6 +3599,28 @@ typedef enum acamera_metadata_tag { * YUV_420_888 | all output sizes available for JPEG | FULL | * YUV_420_888 | all output sizes available for JPEG, up to the maximum video size | LIMITED | * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |

    + *

    For applications targeting SDK version 31 or newer, if the mobile device declares to be + * media performance class S, + * the primary camera devices (first rear/front camera in the camera ID list) will not + * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream + * smaller than 1080p, the camera device will round up the JPEG image size to at least + * 1080p. The requirements for IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. + * This new minimum required output stream configurations are illustrated by the table below:

    + *

    Format | Size | Hardware Level | Notes + * :-------------:|:--------------------------------------------:|:--------------:|:--------------: + * JPEG | ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE | Any | + * JPEG | 1920x1080 (1080p) | Any | if 1080p <= activeArraySize + * YUV_420_888 | ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE | FULL | + * YUV_420_888 | 1920x1080 (1080p) | FULL | if 1080p <= activeArraySize + * YUV_420_888 | 1280x720 (720) | FULL | if 720p <= activeArraySize + * YUV_420_888 | 640x480 (480p) | FULL | if 480p <= activeArraySize + * YUV_420_888 | 320x240 (240p) | FULL | if 240p <= activeArraySize + * YUV_420_888 | all output sizes available for FULL hardware level, up to the maximum video size | LIMITED | + * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |

    + *

    For applications targeting SDK version 31 or newer, if the mobile device doesn't declare + * to be media performance class S, or if the camera device isn't a primary rear/front + * camera, the minimum required output stream configurations are the same as for applications + * targeting SDK version older than 31.

    *

    Refer to ACAMERA_REQUEST_AVAILABLE_CAPABILITIES for additional * mandatory stream configurations on a per-capability basis.

    *

    Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for @@ -3537,8 +3652,6 @@ typedef enum acamera_metadata_tag { * set to either OFF or FAST.

    *

    When multiple streams are used in a request, the minimum frame * duration will be max(individual stream min durations).

    - *

    The minimum frame duration of a stream (of a particular format, size) - * is the same regardless of whether the stream is input or output.

    *

    See ACAMERA_SENSOR_FRAME_DURATION and * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for more details about * calculating the max frame rate.

    @@ -3739,6 +3852,278 @@ typedef enum acamera_metadata_tag { ACAMERA_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP = // int32 ACAMERA_SCALER_START + 15, + /** + *

    List of rotate-and-crop modes for ACAMERA_SCALER_ROTATE_AND_CROP that are supported by this camera device.

    + * + * @see ACAMERA_SCALER_ROTATE_AND_CROP + * + *

    Type: byte[n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This entry lists the valid modes for ACAMERA_SCALER_ROTATE_AND_CROP for this camera device.

    + *

    Starting with API level 30, all devices will list at least ROTATE_AND_CROP_NONE. + * Devices with support for rotate-and-crop will additionally list at least + * ROTATE_AND_CROP_AUTO and ROTATE_AND_CROP_90.

    + * + * @see ACAMERA_SCALER_ROTATE_AND_CROP + */ + ACAMERA_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES = // byte[n] + ACAMERA_SCALER_START + 16, + /** + *

    Whether a rotation-and-crop operation is applied to processed + * outputs from the camera.

    + * + *

    Type: byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks
    • + *
    • ACaptureRequest
    • + *

    + * + *

    This control is primarily intended to help camera applications with no support for + * multi-window modes to work correctly on devices where multi-window scenarios are + * unavoidable, such as foldables or other devices with variable display geometry or more + * free-form window placement (such as laptops, which often place portrait-orientation apps + * in landscape with pillarboxing).

    + *

    If supported, the default value is ROTATE_AND_CROP_AUTO, which allows the camera API + * to enable backwards-compatibility support for applications that do not support resizing + * / multi-window modes, when the device is in fact in a multi-window mode (such as inset + * portrait on laptops, or on a foldable device in some fold states). In addition, + * ROTATE_AND_CROP_NONE and ROTATE_AND_CROP_90 will always be available if this control + * is supported by the device. If not supported, devices API level 30 or higher will always + * list only ROTATE_AND_CROP_NONE.

    + *

    When CROP_AUTO is in use, and the camera API activates backward-compatibility mode, + * several metadata fields will also be parsed differently to ensure that coordinates are + * correctly handled for features like drawing face detection boxes or passing in + * tap-to-focus coordinates. The camera API will convert positions in the active array + * coordinate system to/from the cropped-and-rotated coordinate system to make the + * operation transparent for applications. The following controls are affected:

    + *
      + *
    • ACAMERA_CONTROL_AE_REGIONS
    • + *
    • ACAMERA_CONTROL_AF_REGIONS
    • + *
    • ACAMERA_CONTROL_AWB_REGIONS
    • + *
    • android.statistics.faces
    • + *
    + *

    Capture results will contain the actual value selected by the API; + * ROTATE_AND_CROP_AUTO will never be seen in a capture result.

    + *

    Applications can also select their preferred cropping mode, either to opt out of the + * backwards-compatibility treatment, or to use the cropping feature themselves as needed. + * In this case, no coordinate translation will be done automatically, and all controls + * will continue to use the normal active array coordinates.

    + *

    Cropping and rotating is done after the application of digital zoom (via either + * ACAMERA_SCALER_CROP_REGION or ACAMERA_CONTROL_ZOOM_RATIO), but before each individual + * output is further cropped and scaled. It only affects processed outputs such as + * YUV, PRIVATE, and JPEG. It has no effect on RAW outputs.

    + *

    When CROP_90 or CROP_270 are selected, there is a significant loss to the field of + * view. For example, with a 4:3 aspect ratio output of 1600x1200, CROP_90 will still + * produce 1600x1200 output, but these buffers are cropped from a vertical 3:4 slice at the + * center of the 4:3 area, then rotated to be 4:3, and then upscaled to 1600x1200. Only + * 56.25% of the original FOV is still visible. In general, for an aspect ratio of w:h, + * the crop and rotate operation leaves (h/w)^2 of the field of view visible. For 16:9, + * this is ~31.6%.

    + *

    As a visual example, the figure below shows the effect of ROTATE_AND_CROP_90 on the + * outputs for the following parameters:

    + *
      + *
    • Sensor active array: 2000x1500
    • + *
    • Crop region: top-left: (500, 375), size: (1000, 750) (4:3 aspect ratio)
    • + *
    • Output streams: YUV 640x480 and YUV 1280x720
    • + *
    • ROTATE_AND_CROP_90
    • + *
    + *

    Effect of ROTATE_AND_CROP_90

    + *

    With these settings, the regions of the active array covered by the output streams are:

    + *
      + *
    • 640x480 stream crop: top-left: (219, 375), size: (562, 750)
    • + *
    • 1280x720 stream crop: top-left: (289, 375), size: (422, 750)
    • + *
    + *

    Since the buffers are rotated, the buffers as seen by the application are:

    + *
      + *
    • 640x480 stream: top-left: (781, 375) on active array, size: (640, 480), downscaled 1.17x from sensor pixels
    • + *
    • 1280x720 stream: top-left: (711, 375) on active array, size: (1280, 720), upscaled 1.71x from sensor pixels
    • + *
    + * + * @see ACAMERA_CONTROL_AE_REGIONS + * @see ACAMERA_CONTROL_AF_REGIONS + * @see ACAMERA_CONTROL_AWB_REGIONS + * @see ACAMERA_CONTROL_ZOOM_RATIO + * @see ACAMERA_SCALER_CROP_REGION + */ + ACAMERA_SCALER_ROTATE_AND_CROP = // byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t) + ACAMERA_SCALER_START + 17, + /** + *

    Default YUV/PRIVATE size to use for requesting secure image buffers.

    + * + *

    Type: int32[2]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This entry lists the default size supported in the secure camera mode. This entry is + * optional on devices support the SECURE_IMAGE_DATA capability. This entry will be null + * if the camera device does not list SECURE_IMAGE_DATA capability.

    + *

    When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed + * to be supported by the camera HAL in the secure camera mode. Any other format or + * resolutions might not be supported. Use + * {@link ACameraDevice_isSessionConfigurationSupported } + * API to query if a secure session configuration is supported if the device supports this + * API.

    + *

    If this key returns null on a device with SECURE_IMAGE_DATA capability, the application + * can assume all output sizes listed in the + * {@link ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS } + * are supported.

    + */ + ACAMERA_SCALER_DEFAULT_SECURE_IMAGE_SIZE = // int32[2] + ACAMERA_SCALER_START + 18, + /** + *

    The available multi-resolution stream configurations that this + * physical camera device supports + * (i.e. format, width, height, output/input stream).

    + * + *

    Type: int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This list contains a subset of the parent logical camera's multi-resolution stream + * configurations which belong to this physical camera, and it will advertise and will only + * advertise the maximum supported resolutions for a particular format.

    + *

    If this camera device isn't a physical camera device constituting a logical camera, + * but a standalone CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * camera, this field represents the multi-resolution input/output stream configurations of + * default mode and max resolution modes. The sizes will be the maximum resolution of a + * particular format for default mode and max resolution mode.

    + *

    This field will only be advertised if the device is a physical camera of a + * logical multi-camera device or an ultra high resolution sensor camera. For a logical + * multi-camera, the camera API will derive the logical camera’s multi-resolution stream + * configurations from all physical cameras. For an ultra high resolution sensor camera, this + * is used directly as the camera’s multi-resolution stream configurations.

    + */ + ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS = + // int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t) + ACAMERA_SCALER_START + 19, + /** + *

    The available stream configurations that this + * camera device supports (i.e. format, width, height, output/input stream) for a + * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

    Type: int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    Analogous to ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + *

    Not all output formats may be supported in a configuration with + * an input stream of a particular format. For more details, see + * android.scaler.availableInputOutputFormatsMapMaximumResolution.

    + * + * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = + // int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t) + ACAMERA_SCALER_START + 20, + /** + *

    This lists the minimum frame duration for each + * format/size combination when the camera device is sent a CaptureRequest with + * ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + *

    When multiple streams are used in a request (if supported, when ACAMERA_SENSOR_PIXEL_MODE + * is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION), the + * minimum frame duration will be max(individual stream min durations).

    + *

    See ACAMERA_SENSOR_FRAME_DURATION and + * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about + * calculating the max frame rate.

    + * + * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_FRAME_DURATION + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_SCALER_START + 21, + /** + *

    This lists the maximum stall duration for each + * output format/size combination when CaptureRequests are submitted with + * ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION

    + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

    + * + * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_SCALER_START + 22, + /** + *

    Whether the camera device supports multi-resolution input or output streams

    + * + *

    Type: byte (acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    A logical multi-camera or an ultra high resolution camera may support multi-resolution + * input or output streams. With multi-resolution output streams, the camera device is able + * to output different resolution images depending on the current active physical camera or + * pixel mode. With multi-resolution input streams, the camera device can reprocess images + * of different resolutions from different physical cameras or sensor pixel modes.

    + *

    When set to TRUE: + * * For a logical multi-camera, the camera framework derives + * android.scaler.multiResolutionStreamConfigurationMap by combining the + * ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS from its physical + * cameras. + * * For an ultra-high resolution sensor camera, the camera framework directly copies + * the value of ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS to + * android.scaler.multiResolutionStreamConfigurationMap.

    + * + * @see ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS + */ + ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED = // byte (acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t) + ACAMERA_SCALER_START + 24, ACAMERA_SCALER_END, /** @@ -4525,6 +4910,67 @@ typedef enum acamera_metadata_tag { */ ACAMERA_SENSOR_DYNAMIC_WHITE_LEVEL = // int32 ACAMERA_SENSOR_START + 29, + /** + *

    Switches sensor pixel mode between maximum resolution mode and default mode.

    + * + *

    Type: byte (acamera_metadata_enum_android_sensor_pixel_mode_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks
    • + *
    • ACaptureRequest
    • + *

    + * + *

    This key controls whether the camera sensor operates in + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION + * mode or not. By default, all camera devices operate in + * CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT mode. + * When operating in + * CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT mode, sensors + * with CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability would typically perform pixel binning in order to improve low light + * performance, noise reduction etc. However, in + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION + * mode (supported only + * by CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * sensors), sensors typically operate in unbinned mode allowing for a larger image size. + * The stream configurations supported in + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION + * mode are also different from those of + * CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT mode. + * They can be queried through + * CameraCharacteristics#get with + * CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION). + * Unless reported by both + * StreamConfigurationMaps, the outputs from + * android.scaler.streamConfigurationMapMaximumResolution and + * android.scaler.streamConfigurationMap + * must not be mixed in the same CaptureRequest. In other words, these outputs are + * exclusive to each other. + * This key does not need to be set for reprocess requests.

    + */ + ACAMERA_SENSOR_PIXEL_MODE = // byte (acamera_metadata_enum_android_sensor_pixel_mode_t) + ACAMERA_SENSOR_START + 32, + /** + *

    Whether RAW images requested have their bayer pattern as described by + * ACAMERA_SENSOR_INFO_BINNING_FACTOR.

    + * + * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR + * + *

    Type: byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks
    • + *

    + * + *

    This key will only be present in devices advertisting the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability which also advertise REMOSAIC_REPROCESSING capability. On all other devices + * RAW targets will have a regular bayer pattern.

    + */ + ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED = // byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t) + ACAMERA_SENSOR_START + 33, ACAMERA_SENSOR_END, /** @@ -4782,7 +5228,7 @@ typedef enum acamera_metadata_tag { * rectangle, and cropping to the rectangle given in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.

    *

    E.g. to calculate position of a pixel, (x,y), in a processed YUV output image with the * dimensions in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE given the position of a pixel, - * (x', y'), in the raw pixel array with dimensions give in + * (x', y'), in the raw pixel array with dimensions given in * ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE:

    *
      *
    1. Choose a pixel (x', y') within the active array region of the raw buffer given in @@ -4826,6 +5272,120 @@ typedef enum acamera_metadata_tag { */ ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE = // int32[4] ACAMERA_SENSOR_INFO_START + 10, + /** + *

      The area of the image sensor which corresponds to active pixels after any geometric + * distortion correction has been applied, when the sensor runs in maximum resolution mode.

      + * + *

      Type: int32[4]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE + * is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION. + * Refer to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE for details, with sensor array related keys + * replaced with their + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION + * counterparts. + * This key will only be present for devices which advertise the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability.

      + *

      The data representation is int[4], which maps to (left, top, width, height).

      + * + * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[4] + ACAMERA_SENSOR_INFO_START + 11, + /** + *

      Dimensions of the full pixel array, possibly + * including black calibration pixels, when the sensor runs in maximum resolution mode. + * Analogous to ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE is + * set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int32[2]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      The pixel count of the full pixel array of the image sensor, which covers + * ACAMERA_SENSOR_INFO_PHYSICAL_SIZE area. This represents the full pixel dimensions of + * the raw buffers produced by this sensor, when it runs in maximum resolution mode. That + * is, when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION. + * This key will only be present for devices which advertise the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability.

      + * + * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[2] + ACAMERA_SENSOR_INFO_START + 12, + /** + *

      The area of the image sensor which corresponds to active pixels prior to the + * application of any geometric distortion correction, when the sensor runs in maximum + * resolution mode. This key must be used for crop / metering regions, only when + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int32[4]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, + * when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION. + * This key will only be present for devices which advertise the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability.

      + *

      The data representation is int[4], which maps to (left, top, width, height).

      + * + * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION = + // int32[4] + ACAMERA_SENSOR_INFO_START + 13, + /** + *

      Dimensions of the group of pixels which are under the same color filter. + * This specifies the width and height (pair of integers) of the group of pixels which fall + * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.

      + * + *

      Type: int32[2]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Sensors can have pixels grouped together under the same color filter in order + * to improve various aspects of imaging such as noise reduction, low light + * performance etc. These groups can be of various sizes such as 2X2 (quad bayer), + * 3X3 (nona-bayer). This key specifies the length and width of the pixels grouped under + * the same color filter.

      + *

      This key will not be present if REMOSAIC_REPROCESSING is not supported, since RAW images + * will have a regular bayer pattern.

      + *

      This key will not be present for sensors which don't have the + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + * capability.

      + */ + ACAMERA_SENSOR_INFO_BINNING_FACTOR = // int32[2] + ACAMERA_SENSOR_INFO_START + 14, ACAMERA_SENSOR_INFO_END, /** @@ -5226,7 +5786,7 @@ typedef enum acamera_metadata_tag { *

      * *

      Since optical image stabilization generally involves motion much faster than the duration - * of individualq image exposure, multiple OIS samples can be included for a single capture + * of individual image exposure, multiple OIS samples can be included for a single capture * result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating * at 30fps may have 6-7 OIS samples per capture result. This information can be combined * with the rolling shutter skew to account for lens motion during image exposure in @@ -6031,6 +6591,162 @@ typedef enum acamera_metadata_tag { */ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS = // int64[4*n] ACAMERA_DEPTH_START + 8, + /** + *

      The available depth dataspace stream + * configurations that this camera device supports + * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with + * ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t)

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, for configurations which + * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = + // int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t) + ACAMERA_DEPTH_START + 9, + /** + *

      This lists the minimum frame duration for each + * format/size combination for depth output formats when a CaptureRequest is submitted with + * ACAMERA_SENSOR_PIXEL_MODE set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, for configurations which + * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + *

      See ACAMERA_SENSOR_FRAME_DURATION and + * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about + * calculating the max frame rate.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS + * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION + * @see ACAMERA_SENSOR_FRAME_DURATION + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_DEPTH_START + 10, + /** + *

      This lists the maximum stall duration for each + * output format/size combination for depth streams for CaptureRequests where + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, for configurations which + * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_DEPTH_START + 11, + /** + *

      The available dynamic depth dataspace stream + * configurations that this camera device supports (i.e. format, width, height, + * output/input stream) for CaptureRequests where ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t)

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = + // int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t) + ACAMERA_DEPTH_START + 12, + /** + *

      This lists the minimum frame duration for each + * format/size combination for dynamic depth output streams for CaptureRequests where + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_DEPTH_START + 13, + /** + *

      This lists the maximum stall duration for each + * output format/size combination for dynamic depth streams for CaptureRequests where + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, for configurations + * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS + * @see ACAMERA_SENSOR_PIXEL_MODE + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_DEPTH_START + 14, ACAMERA_DEPTH_END, /** @@ -6251,6 +6967,71 @@ typedef enum acamera_metadata_tag { */ ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS = // int64[4*n] ACAMERA_HEIC_START + 2, + /** + *

      The available HEIC (ISO/IEC 23008-12) stream + * configurations that this camera device supports + * (i.e. format, width, height, output/input stream).

      + * + *

      Type: int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t)

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS for details.

      + *

      All the configuration tuples (format, width, height, input?) will contain + * AIMAGE_FORMAT_HEIC format as OUTPUT only.

      + * + * @see ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS + */ + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = + // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t) + ACAMERA_HEIC_START + 3, + /** + *

      This lists the minimum frame duration for each + * format/size combination for HEIC output formats for CaptureRequests where + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Refer to ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS for details.

      + * + * @see ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS + */ + ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_HEIC_START + 4, + /** + *

      This lists the maximum stall duration for each + * output format/size combination for HEIC streams for CaptureRequests where + * ACAMERA_SENSOR_PIXEL_MODE is set to + * CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION.

      + * + * @see ACAMERA_SENSOR_PIXEL_MODE + * + *

      Type: int64[4*n]

      + * + *

      This tag may appear in: + *

        + *
      • ACameraMetadata from ACameraManager_getCameraCharacteristics
      • + *

      + * + *

      Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS for details.

      + * + * @see ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS + */ + ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION = + // int64[4*n] + ACAMERA_HEIC_START + 5, ACAMERA_HEIC_END, } acamera_metadata_tag_t; @@ -8127,13 +8908,27 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * camera's crop region is set to maximum size, the FOV of the physical streams for the * ultrawide lens will be the same as the logical stream, by making the crop region * smaller than its active array size to compensate for the smaller focal length.

      - *

      Even if the underlying physical cameras have different RAW characteristics (such as - * size or CFA pattern), a logical camera can still advertise RAW capability. In this - * case, when the application configures a RAW stream, the camera device will make sure - * the active physical camera will remain active to ensure consistent RAW output - * behavior, and not switch to other physical cameras.

      + *

      There are two ways for the application to capture RAW images from a logical camera + * with RAW capability:

      + *
        + *
      • Because the underlying physical cameras may have different RAW capabilities (such + * as resolution or CFA pattern), to maintain backward compatibility, when a RAW stream + * is configured, the camera device makes sure the default active physical camera remains + * active and does not switch to other physical cameras. (One exception is that, if the + * logical camera consists of identical image sensors and advertises multiple focalLength + * due to different lenses, the camera device may generate RAW images from different + * physical cameras based on the focalLength being set by the application.) This + * backward-compatible approach usually results in loss of optical zoom, to telephoto + * lens or to ultrawide lens.
      • + *
      • Alternatively, to take advantage of the full zoomRatio range of the logical camera, + * the application should use MultiResolutionImageReader + * to capture RAW images from the currently active physical camera. Because different + * physical camera may have different RAW characteristics, the application needs to use + * the characteristics and result metadata of the active physical camera for the + * relevant RAW metadata.
      • + *
      *

      The capture request and result metadata tags required for backward compatible camera - * functionalities will be solely based on the logical camera capabiltity. On the other + * functionalities will be solely based on the logical camera capability. On the other * hand, the use of manual capture controls (sensor or post-processing) with a * logical camera may result in unexpected behavior when the HAL decides to switch * between physical cameras with different characteristics under the hood. For example, @@ -8201,6 +8996,25 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { */ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14, + /** + *

      This camera device is capable of producing ultra high resolution images in + * addition to the image sizes described in the + * android.scaler.streamConfigurationMap. + * It can operate in 'default' mode and 'max resolution' mode. It generally does this + * by binning pixels in 'default' mode and not binning them in 'max resolution' mode. + * android.scaler.streamConfigurationMap describes the streams supported in 'default' + * mode. + * The stream configurations supported in 'max resolution' mode are described by + * android.scaler.streamConfigurationMapMaximumResolution. + * The maximum resolution mode pixel array size of a camera device + * (ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE) with this capability, + * will be at least 24 megapixels.

      + * + * @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE + */ + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + = 16, + } acamera_metadata_enum_android_request_available_capabilities_t; @@ -8301,6 +9115,79 @@ typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_c } acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t; +// ACAMERA_SCALER_ROTATE_AND_CROP +typedef enum acamera_metadata_enum_acamera_scaler_rotate_and_crop { + /** + *

      No rotate and crop is applied. Processed outputs are in the sensor orientation.

      + */ + ACAMERA_SCALER_ROTATE_AND_CROP_NONE = 0, + + /** + *

      Processed images are rotated by 90 degrees clockwise, and then cropped + * to the original aspect ratio.

      + */ + ACAMERA_SCALER_ROTATE_AND_CROP_90 = 1, + + /** + *

      Processed images are rotated by 180 degrees. Since the aspect ratio does not + * change, no cropping is performed.

      + */ + ACAMERA_SCALER_ROTATE_AND_CROP_180 = 2, + + /** + *

      Processed images are rotated by 270 degrees clockwise, and then cropped + * to the original aspect ratio.

      + */ + ACAMERA_SCALER_ROTATE_AND_CROP_270 = 3, + + /** + *

      The camera API automatically selects the best concrete value for + * rotate-and-crop based on the application's support for resizability and the current + * multi-window mode.

      + *

      If the application does not support resizing but the display mode for its main + * Activity is not in a typical orientation, the camera API will set ROTATE_AND_CROP_90 + * or some other supported rotation value, depending on device configuration, + * to ensure preview and captured images are correctly shown to the user. Otherwise, + * ROTATE_AND_CROP_NONE will be selected.

      + *

      When a value other than NONE is selected, several metadata fields will also be parsed + * differently to ensure that coordinates are correctly handled for features like drawing + * face detection boxes or passing in tap-to-focus coordinates. The camera API will + * convert positions in the active array coordinate system to/from the cropped-and-rotated + * coordinate system to make the operation transparent for applications.

      + *

      No coordinate mapping will be done when the application selects a non-AUTO mode.

      + */ + ACAMERA_SCALER_ROTATE_AND_CROP_AUTO = 4, + +} acamera_metadata_enum_android_scaler_rotate_and_crop_t; + +// ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS +typedef enum acamera_metadata_enum_acamera_scaler_physical_camera_multi_resolution_stream_configurations { + ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_OUTPUT + = 0, + + ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_INPUT + = 1, + +} acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t; + +// ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION +typedef enum acamera_metadata_enum_acamera_scaler_available_stream_configurations_maximum_resolution { + ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT + = 0, + + ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT + = 1, + +} acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t; + +// ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED +typedef enum acamera_metadata_enum_acamera_scaler_multi_resolution_stream_supported { + ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_FALSE = 0, + + ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_TRUE = 1, + +} acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t; + // ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 { @@ -8373,10 +9260,10 @@ typedef enum acamera_metadata_enum_acamera_sensor_test_pattern_mode { * respective color channel provided in * ACAMERA_SENSOR_TEST_PATTERN_DATA.

      *

      For example:

      - *
      android.testPatternData = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
      +     * 
      ACAMERA_SENSOR_TEST_PATTERN_DATA = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
            * 
      *

      All green pixels are 100% green. All red/blue pixels are black.

      - *
      android.testPatternData = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
      +     * 
      ACAMERA_SENSOR_TEST_PATTERN_DATA = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
            * 
      *

      All red pixels are 100% red. Only the odd green pixels * are 100% green. All blue pixels are 100% black.

      @@ -8459,6 +9346,42 @@ typedef enum acamera_metadata_enum_acamera_sensor_test_pattern_mode { } acamera_metadata_enum_android_sensor_test_pattern_mode_t; +// ACAMERA_SENSOR_PIXEL_MODE +typedef enum acamera_metadata_enum_acamera_sensor_pixel_mode { + /** + *

      This is the default sensor pixel mode. This is the only sensor pixel mode + * supported unless a camera device advertises + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR.

      + */ + ACAMERA_SENSOR_PIXEL_MODE_DEFAULT = 0, + + /** + *

      This sensor pixel mode is offered by devices with capability + * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR. + * In this mode, sensors typically do not bin pixels, as a result can offer larger + * image sizes.

      + */ + ACAMERA_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1, + +} acamera_metadata_enum_android_sensor_pixel_mode_t; + +// ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED +typedef enum acamera_metadata_enum_acamera_sensor_raw_binning_factor_used { + /** + *

      The RAW targets in this capture have ACAMERA_SENSOR_INFO_BINNING_FACTOR as the + * bayer pattern.

      + * + * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR + */ + ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_TRUE = 0, + + /** + *

      The RAW targets have a regular bayer pattern in this capture.

      + */ + ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_FALSE = 1, + +} acamera_metadata_enum_android_sensor_raw_binning_factor_used_t; + // ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT typedef enum acamera_metadata_enum_acamera_sensor_info_color_filter_arrangement { @@ -8943,6 +9866,26 @@ typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_ } acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t; +// ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION +typedef enum acamera_metadata_enum_acamera_depth_available_depth_stream_configurations_maximum_resolution { + ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT + = 0, + + ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT + = 1, + +} acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t; + +// ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION +typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_configurations_maximum_resolution { + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT + = 0, + + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT + = 1, + +} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t; + // ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type { @@ -8994,6 +9937,17 @@ typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurat } acamera_metadata_enum_android_heic_available_heic_stream_configurations_t; +// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION +typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations_maximum_resolution { + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT + = 0, + + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT + = 1, + +} acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t; + + __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h index 99f67e9a5a59d185da40e9db29cac55009f64752..0838fbad21d589e91787fba20d3079097ef1b534 100644 --- a/camera/ndk/include/camera/NdkCameraWindowType.h +++ b/camera/ndk/include/camera/NdkCameraWindowType.h @@ -44,10 +44,12 @@ */ #ifdef __ANDROID_VNDK__ #include -typedef native_handle_t ACameraWindowType; +typedef const native_handle_t ACameraWindowType; #else #include typedef ANativeWindow ACameraWindowType; #endif +/** @} */ + #endif //_NDK_CAMERA_WINDOW_TYPE_H diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h index a4dc374d376c6f0642bf1fabd4fafe24f58dad8a..d83c5b3fa7c92b70b1535779c428f51033ad8b0d 100644 --- a/camera/ndk/include/camera/NdkCaptureRequest.h +++ b/camera/ndk/include/camera/NdkCaptureRequest.h @@ -44,10 +44,10 @@ __BEGIN_DECLS -// Container for output targets +/** Container for output targets */ typedef struct ACameraOutputTargets ACameraOutputTargets; -// Container for a single output target +/** Container for a single output target */ typedef struct ACameraOutputTarget ACameraOutputTarget; /** @@ -383,10 +383,10 @@ camera_status_t ACaptureRequest_getConstEntry_physicalCamera( * Set/change a camera capture control entry with unsigned 8 bits data type for * a physical camera backing a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_u8, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_u8, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * @@ -413,10 +413,10 @@ camera_status_t ACaptureRequest_setEntry_physicalCamera_u8( * Set/change a camera capture control entry with signed 32 bits data type for * a physical camera of a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_i32, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_i32, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * @@ -443,10 +443,10 @@ camera_status_t ACaptureRequest_setEntry_physicalCamera_i32( * Set/change a camera capture control entry with float data type for * a physical camera of a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_float, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_float, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * @@ -473,10 +473,10 @@ camera_status_t ACaptureRequest_setEntry_physicalCamera_float( * Set/change a camera capture control entry with signed 64 bits data type for * a physical camera of a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_i64, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_i64, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * @@ -503,10 +503,10 @@ camera_status_t ACaptureRequest_setEntry_physicalCamera_i64( * Set/change a camera capture control entry with double data type for * a physical camera of a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_double, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_double, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * @@ -533,10 +533,10 @@ camera_status_t ACaptureRequest_setEntry_physicalCamera_double( * Set/change a camera capture control entry with rational data type for * a physical camera of a logical multi-camera device. * - *

      Same as ACaptureRequest_setEntry_rational, except that if {@link tag} is contained + *

      Same as ACaptureRequest_setEntry_rational, except that if tag is contained * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function * sets the entry for a particular physical sub-camera backing the logical multi-camera. - * If {@link tag} is not contained in + * If tag is not contained in * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored * by the camera device.

      * diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h index e1af8c142168c38c4137a6793d1d27e66cd6239a..5a1af797a94510c94f820f5d07cfae66443329ca 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h +++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h @@ -18,7 +18,7 @@ #include "utils.h" struct ACaptureSessionOutput { - explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false, + explicit ACaptureSessionOutput(const native_handle_t* window, bool isShared = false, const char* physicalCameraId = "") : mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {}; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index e511a3f81d9f6eb98347914b47e5d8ab2dc5992a..9f63099152d97e3ab6028e1b816e80a478fb8280 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -180,6 +180,7 @@ CameraDevice::createCaptureSession( const ACaptureRequest* sessionParameters, const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session) { + nsecs_t startTimeNs = systemTime(); sp currentSession = mCurrentSession.promote(); Mutex::Autolock _l(mDeviceLock); camera_status_t ret = checkCameraClosedOrErrorLocked(); @@ -193,7 +194,7 @@ CameraDevice::createCaptureSession( } // Create new session - ret = configureStreamsLocked(outputs, sessionParameters); + ret = configureStreamsLocked(outputs, sessionParameters, startTimeNs); if (ret != ACAMERA_OK) { ALOGE("Fail to create new session. cannot configure streams"); return ret; @@ -355,7 +356,7 @@ CameraDevice::allocateCaptureRequestLocked( std::vector requestStreamIdxList; std::vector requestSurfaceIdxList; for (auto outputTarget : request->targets->mOutputs) { - native_handle_t* anw = outputTarget.mWindow; + const native_handle_t* anw = outputTarget.mWindow; bool found = false; req->mSurfaceList.push_back(anw); // lookup stream/surface ID @@ -434,7 +435,7 @@ CameraDevice::allocateACaptureRequest(sp& req, const char* devic } pRequest->targets = new ACameraOutputTargets(); for (size_t i = 0; i < req->mSurfaceList.size(); i++) { - native_handle_t* anw = req->mSurfaceList[i]; + const native_handle_t* anw = req->mSurfaceList[i]; ACameraOutputTarget outputTarget(anw); pRequest->targets->mOutputs.insert(outputTarget); } @@ -472,7 +473,11 @@ CameraDevice::notifySessionEndOfLifeLocked(ACameraCaptureSession* session) { } // No new session, unconfigure now - camera_status_t ret = configureStreamsLocked(nullptr, nullptr); + // Note: The unconfiguration of session won't be accounted for session + // latency because a stream configuration with 0 streams won't ever become + // active. + nsecs_t startTimeNs = systemTime(); + camera_status_t ret = configureStreamsLocked(nullptr, nullptr, startTimeNs); if (ret != ACAMERA_OK) { ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret); } @@ -598,7 +603,7 @@ CameraDevice::waitUntilIdleLocked() { camera_status_t CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, - const ACaptureRequest* sessionParameters) { + const ACaptureRequest* sessionParameters, nsecs_t startTimeNs) { ACaptureSessionOutputContainer emptyOutput; if (outputs == nullptr) { outputs = &emptyOutput; @@ -611,7 +616,7 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu std::set> outputSet; for (auto outConfig : outputs->mOutputs) { - native_handle_t* anw = outConfig.mWindow; + const native_handle_t* anw = outConfig.mWindow; OutputConfigurationWrapper outConfigInsertW; OutputConfiguration &outConfigInsert = outConfigInsertW.mOutputConfiguration; outConfigInsert.rotation = utils::convertToHidl(outConfig.mRotation); @@ -697,7 +702,8 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu utils::convertToHidl(params_metadata, &hidlParams); params.unlock(params_metadata); } - remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams); + remoteRet = mRemote->endConfigure_2_1(StreamConfigurationMode::NORMAL_MODE, + hidlParams, startTimeNs); CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "endConfigure()") return ACAMERA_OK; } @@ -846,8 +852,7 @@ CameraDevice::onCaptureErrorLocked( for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) { int32_t windowId = streamAndWindowId.windowId; if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) { - native_handle_t* anw = - const_cast(windowHandles[windowId].getNativeHandle()); + const native_handle_t* anw = windowHandles[windowId].getNativeHandle(); ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64, getId(), anw, frameNumber); @@ -1244,7 +1249,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } - native_handle_t* anw; + const native_handle_t* anw; found = msg->findPointer(kAnwKey, (void**) &anw); if (!found) { ALOGE("%s: Cannot find native_handle_t!", __FUNCTION__); diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 7fc699e16e3abb7e6fe81a5339b1199399c19507..0b6c7c83af00fa7d000a2059d2fffa032d4d5a6b 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,8 @@ namespace android { namespace acam { using ICameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback; -using ICameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser; +using ICameraDeviceUser_2_0 = frameworks::cameraservice::device::V2_0::ICameraDeviceUser; +using ICameraDeviceUser = frameworks::cameraservice::device::V2_1::ICameraDeviceUser; using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras; using PhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo; using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings; @@ -201,7 +202,7 @@ class CameraDevice final : public RefBase { void notifySessionEndOfLifeLocked(ACameraCaptureSession* session); camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, - const ACaptureRequest* sessionParameters); + const ACaptureRequest* sessionParameters, nsecs_t startTimeNs); // Input message will be posted and cleared after this returns void postSessionMsgAndCleanup(sp& msg); diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp index 5aa9c467fb2d0c9662bbcc97d4bbb28f76d1293a..77c934abbecce7e00c71eef8dc667ef0193841af 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -764,15 +764,15 @@ ACameraManager::openCamera( } sp callbacks = device->getServiceCallback(); - sp deviceRemote; + sp deviceRemote_2_0; // No way to get package name from native. // Send a zero length package name and let camera service figure it out from UID Status status = Status::NO_ERROR; auto serviceRet = cs->connectDevice( - callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) { + callbacks, cameraId, [&status, &deviceRemote_2_0](auto s, auto &device) { status = s; - deviceRemote = device; + deviceRemote_2_0 = device; }); if (!serviceRet.isOk() || status != Status::NO_ERROR) { @@ -780,11 +780,18 @@ ACameraManager::openCamera( delete device; return utils::convertFromHidl(status); } - if (deviceRemote == nullptr) { + if (deviceRemote_2_0 == nullptr) { ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__); delete device; return ACAMERA_ERROR_CAMERA_DISCONNECTED; } + auto castResult = ICameraDeviceUser::castFrom(deviceRemote_2_0); + if (!castResult.isOk()) { + ALOGE("%s: failed to cast remote device to version 2.1", __FUNCTION__); + delete device; + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + sp deviceRemote = castResult; device->setRemoteDevice(deviceRemote); device->setDeviceMetadataQueues(); *outDevice = device; diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h index f25f5cb42576430e19b595e1c7d44375a564b2c0..4663529f6ca294abcfcdacf4786d5294b9329548 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.h +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,7 @@ namespace android { namespace acam { -using ICameraService = frameworks::cameraservice::service::V2_1::ICameraService; +using ICameraService = frameworks::cameraservice::service::V2_2::ICameraService; using CameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus; using ICameraServiceListener = frameworks::cameraservice::service::V2_1::ICameraServiceListener; using PhysicalCameraStatusAndId = frameworks::cameraservice::service::V2_1::PhysicalCameraStatusAndId; diff --git a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h index ed676151860efa56bc6fc84631bfa7c096959b50..5715d778770b38e2a5cb17fec589c37cefa9ce19 100644 --- a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h +++ b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h @@ -17,7 +17,7 @@ #include "utils.h" struct ACameraOutputTarget { - explicit ACameraOutputTarget(native_handle_t* window) : mWindow(window) {}; + explicit ACameraOutputTarget(const native_handle_t* window) : mWindow(window) {}; bool operator == (const ACameraOutputTarget& other) const { return mWindow == other.mWindow; diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h index f389f03d792de2f65c3d627716dd6fdf06612cac..6f5820ecee9cc67a8c364862adadf02db04f079c 100644 --- a/camera/ndk/ndk_vendor/impl/utils.h +++ b/camera/ndk/ndk_vendor/impl/utils.h @@ -42,7 +42,7 @@ using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfi // Utility class so that CaptureRequest can be stored by sp<> struct CaptureRequest : public RefBase { frameworks::cameraservice::device::V2_0::CaptureRequest mCaptureRequest; - std::vector mSurfaceList; + std::vector mSurfaceList; //Physical camera settings metadata is stored here, since the capture request //might not contain it. That's since, fmq might have consumed it. hidl_vec mPhysicalCameraSettings; @@ -62,13 +62,13 @@ bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_ha // Utility class so the native_handle_t can be compared with its contents instead // of just raw pointer comparisons. struct native_handle_ptr_wrapper { - native_handle_t *mWindow = nullptr; + const native_handle_t *mWindow = nullptr; - native_handle_ptr_wrapper(native_handle_t *nh) : mWindow(nh) { } + native_handle_ptr_wrapper(const native_handle_t *nh) : mWindow(nh) { } native_handle_ptr_wrapper() = default; - operator native_handle_t *() const { return mWindow; } + operator const native_handle_t *() const { return mWindow; } bool operator ==(const native_handle_ptr_wrapper other) const { return isWindowNativeHandleEqual(mWindow, other.mWindow); diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 938b5f55b8b61aec309cd0d208d37e765d59f1c8..ba14c5c4fd69fd029016e6870b7d55a1d35022e2 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -50,7 +50,7 @@ static constexpr int kTestImageHeight = 480; static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache; -using ConfiguredWindows = std::set; +using ConfiguredWindows = std::set; class CameraHelper { public: @@ -60,11 +60,11 @@ class CameraHelper { struct PhysicalImgReaderInfo { const char* physicalCameraId; - native_handle_t* anw; + const native_handle_t* anw; }; // Retaining the error code in case the caller needs to analyze it. - std::variant initCamera(native_handle_t* imgReaderAnw, + std::variant initCamera(const native_handle_t* imgReaderAnw, const std::vector& physicalImgReaders, bool usePhysicalSettings) { ConfiguredWindows configuredWindows; @@ -257,7 +257,7 @@ class CameraHelper { ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr}; ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr}; - native_handle_t* mImgReaderAnw = nullptr; // not owned by us. + const native_handle_t* mImgReaderAnw = nullptr; // not owned by us. // Camera device ACameraDevice* mDevice = nullptr; @@ -396,7 +396,7 @@ class ImageReaderTestCase { return 0; } - native_handle_t* getNativeWindow() { return mImgReaderAnw; } + const native_handle_t* getNativeWindow() { return mImgReaderAnw; } int getAcquiredImageCount() { std::lock_guard lock(mMutex); diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp index eee05ff4478d3a10412513b97d12e60dccd7d9cc..9f2f430f6246c3a3f8390117e4e4017796b6effd 100644 --- a/camera/tests/CameraBinderTests.cpp +++ b/camera/tests/CameraBinderTests.cpp @@ -363,7 +363,8 @@ TEST(CameraServiceBinderTest, CheckBinderCameraService) { // Check metadata binder call CameraMetadata metadata; - res = service->getCameraCharacteristics(cameraId, &metadata); + res = service->getCameraCharacteristics(cameraId, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, &metadata); EXPECT_TRUE(res.isOk()) << res; EXPECT_FALSE(metadata.isEmpty()); @@ -378,8 +379,8 @@ TEST(CameraServiceBinderTest, CheckBinderCameraService) { sp callbacks(new TestCameraDeviceCallbacks()); sp device; res = service->connectDevice(callbacks, cameraId, String16("meeeeeeeee!"), - {}, hardware::ICameraService::USE_CALLING_UID, - /*out*/&device); + {}, hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/ 0, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, /*out*/&device); EXPECT_TRUE(res.isOk()) << res; ASSERT_NE(nullptr, device.get()); device->disconnect(); @@ -421,8 +422,8 @@ protected: { SCOPED_TRACE("openNewDevice"); binder::Status res = service->connectDevice(callbacks, deviceId, String16("meeeeeeeee!"), - {}, hardware::ICameraService::USE_CALLING_UID, - /*out*/&device); + {}, hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/ 0, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, /*out*/&device); EXPECT_TRUE(res.isOk()) << res; } auto p = std::make_pair(callbacks, device); @@ -517,7 +518,7 @@ TEST_F(CameraClientBinderTest, CheckBinderCameraDeviceUser) { CameraMetadata sessionParams; std::vector offlineStreamIds; res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams, - &offlineStreamIds); + ns2ms(systemTime()), &offlineStreamIds); EXPECT_TRUE(res.isOk()) << res; EXPECT_FALSE(callbacks->hadError()); @@ -629,7 +630,7 @@ TEST_F(CameraClientBinderTest, CheckBinderCameraDeviceUser) { res = device->deleteStream(streamId); EXPECT_TRUE(res.isOk()) << res; res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams, - &offlineStreamIds); + ns2ms(systemTime()), &offlineStreamIds); EXPECT_TRUE(res.isOk()) << res; sleep(/*second*/1); // allow some time for errors to show up, if any diff --git a/camera/tests/CameraCharacteristicsPermission.cpp b/camera/tests/CameraCharacteristicsPermission.cpp index 135d2a38571dce4d4354199697c0d2df38c0e4ea..76dc38ccae848bfbdfa1051f963c97bd7d933690 100644 --- a/camera/tests/CameraCharacteristicsPermission.cpp +++ b/camera/tests/CameraCharacteristicsPermission.cpp @@ -73,7 +73,8 @@ TEST_F(CameraCharacteristicsPermission, TestCameraPermission) { CameraMetadata metadata; std::vector tagsNeedingPermission; - rc = mCameraService->getCameraCharacteristics(cameraIdStr, &metadata); + rc = mCameraService->getCameraCharacteristics(cameraIdStr, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, &metadata); ASSERT_TRUE(rc.isOk()); EXPECT_FALSE(metadata.isEmpty()); EXPECT_EQ(metadata.removePermissionEntries(CAMERA_METADATA_INVALID_VENDOR_ID, diff --git a/camera/tests/CameraZSLTests.cpp b/camera/tests/CameraZSLTests.cpp index 02c6e2aed204570aab8c05a8261c53b03dc04196..efd9daebbcc0bd61df1f71f2c489c44ef1af38f0 100644 --- a/camera/tests/CameraZSLTests.cpp +++ b/camera/tests/CameraZSLTests.cpp @@ -181,7 +181,8 @@ TEST_F(CameraZSLTests, TestAllPictureSizes) { } CameraMetadata metadata; - rc = mCameraService->getCameraCharacteristics(cameraIdStr, &metadata); + rc = mCameraService->getCameraCharacteristics(cameraIdStr, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, &metadata); if (!rc.isOk()) { // The test is relevant only for cameras with Hal 3.x // support. @@ -207,7 +208,8 @@ TEST_F(CameraZSLTests, TestAllPictureSizes) { rc = mCameraService->connect(this, cameraId, String16("ZSLTest"), hardware::ICameraService::USE_CALLING_UID, - hardware::ICameraService::USE_CALLING_PID, &cameraDevice); + hardware::ICameraService::USE_CALLING_PID, + /*targetSdkVersion*/__ANDROID_API_FUTURE__, &cameraDevice); EXPECT_TRUE(rc.isOk()); CameraParameters params(cameraDevice->getParameters()); diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index f4fb6266d2d90f38c51f6c9a8e7225a2485628d0..e6e347321d6769b70b9f0c577ffd8862f41b6e74 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -57,7 +57,7 @@ #include #include #include -#include +#include #include #include "screenrecord.h" @@ -68,7 +68,7 @@ using android::ABuffer; using android::ALooper; using android::AMessage; using android::AString; -using android::DisplayConfig; +using android::ui::DisplayMode; using android::FrameOutput; using android::IBinder; using android::IGraphicBufferProducer; @@ -273,14 +273,11 @@ static status_t setDisplayProjection( SurfaceComposerClient::Transaction& t, const sp& dpy, const ui::DisplayState& displayState) { - const ui::Size& viewport = displayState.viewport; - - // Set the region of the layer stack we're interested in, which in our - // case is "all of it". - Rect layerStackRect(viewport); + // Set the region of the layer stack we're interested in, which in our case is "all of it". + Rect layerStackRect(displayState.layerStackSpaceRect); // We need to preserve the aspect ratio of the display. - float displayAspect = viewport.getHeight() / static_cast(viewport.getWidth()); + float displayAspect = layerStackRect.getHeight() / static_cast(layerStackRect.getWidth()); // Set the way we map the output onto the display surface (which will @@ -692,27 +689,28 @@ static status_t recordScreen(const char* fileName) { return err; } - DisplayConfig displayConfig; - err = SurfaceComposerClient::getActiveDisplayConfig(display, &displayConfig); + DisplayMode displayMode; + err = SurfaceComposerClient::getActiveDisplayMode(display, &displayMode); if (err != NO_ERROR) { fprintf(stderr, "ERROR: unable to get display config\n"); return err; } - const ui::Size& viewport = displayState.viewport; + const ui::Size& layerStackSpaceRect = displayState.layerStackSpaceRect; if (gVerbose) { printf("Display is %dx%d @%.2ffps (orientation=%s), layerStack=%u\n", - viewport.getWidth(), viewport.getHeight(), displayConfig.refreshRate, - toCString(displayState.orientation), displayState.layerStack); + layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight(), + displayMode.refreshRate, toCString(displayState.orientation), + displayState.layerStack); fflush(stdout); } // Encoder can't take odd number as config if (gVideoWidth == 0) { - gVideoWidth = floorToEven(viewport.getWidth()); + gVideoWidth = floorToEven(layerStackSpaceRect.getWidth()); } if (gVideoHeight == 0) { - gVideoHeight = floorToEven(viewport.getHeight()); + gVideoHeight = floorToEven(layerStackSpaceRect.getHeight()); } // Configure and start the encoder. @@ -720,7 +718,7 @@ static status_t recordScreen(const char* fileName) { sp frameOutput; sp encoderInputSurface; if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) { - err = prepareEncoder(displayConfig.refreshRate, &encoder, &encoderInputSurface); + err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface); if (err != NO_ERROR && !gSizeSpecified) { // fallback is defined for landscape; swap if we're in portrait @@ -733,7 +731,7 @@ static status_t recordScreen(const char* fileName) { gVideoWidth, gVideoHeight, newWidth, newHeight); gVideoWidth = newWidth; gVideoHeight = newHeight; - err = prepareEncoder(displayConfig.refreshRate, &encoder, &encoderInputSurface); + err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface); } } if (err != NO_ERROR) return err; @@ -1170,14 +1168,14 @@ int main(int argc, char* const argv[]) { } break; case 'd': - gPhysicalDisplayId = atoll(optarg); - if (gPhysicalDisplayId == 0) { + gPhysicalDisplayId = PhysicalDisplayId(atoll(optarg)); + if (gPhysicalDisplayId.value == 0) { fprintf(stderr, "Please specify a valid physical display id\n"); return 2; } else if (SurfaceComposerClient:: getPhysicalDisplayToken(gPhysicalDisplayId) == nullptr) { - fprintf(stderr, "Invalid physical display id: %" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT "\n", gPhysicalDisplayId); + fprintf(stderr, "Invalid physical display id: %s\n", + to_string(gPhysicalDisplayId).c_str()); return 2; } break; diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index ef4c568764c0c35cb5679ed784c6d2e1f2b9dadd..803c4a477a37242c918e139264f6da6ab1d85354 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -16,6 +16,9 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_foundation libjpeg libui libgui libcutils liblog \ libhidlbase libdatasource libaudioclient \ android.hardware.media.omx@1.0 \ + framework-permission-aidl-cpp + +LOCAL_STATIC_LIBRARIES := framework-permission-aidl-cpp LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ @@ -48,7 +51,8 @@ LOCAL_HEADER_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia liblog libutils libbinder \ - libstagefright_foundation libdatasource libaudioclient + libstagefright_foundation libdatasource libaudioclient \ + framework-permission-aidl-cpp LOCAL_C_INCLUDES:= \ frameworks/av/camera/include \ @@ -85,7 +89,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ frameworks/native/include/media/openmax \ - frameworks/native/include/media/hardware + frameworks/native/include/media/hardware \ + framework-permission-aidl-cpp LOCAL_CFLAGS += -Wno-multichar -Werror -Wall @@ -113,7 +118,8 @@ LOCAL_HEADER_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia liblog libutils libbinder \ - libstagefright_foundation libaudioclient + libstagefright_foundation libaudioclient \ + framework-permission-aidl-cpp LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index 84a6d6be2ce3d512f0078f43987a2c5c87184d93..4b41ff8095574c06e99340b22e4c9e0c4297015e 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -38,6 +39,8 @@ using namespace android; +using content::AttributionSourceState; + static void usage(const char* name) { fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [-N name] []\n", name); @@ -110,9 +113,10 @@ int main(int argc, char* argv[]) audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER; attr.source = AUDIO_SOURCE_MIC; + // TODO b/182392769: use attribution source util source = new AudioSource( &attr, - String16(), + AttributionSourceState(), sampleRate, channels); } else { diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp index 33c46635e275e7b359244226395ae72ba51d44f0..beeab544d54326d731f9761d391a2352cf92167d 100644 --- a/cmds/stagefright/codec.cpp +++ b/cmds/stagefright/codec.cpp @@ -39,7 +39,7 @@ #include #include #include -#include +#include static void usage(const char *me) { fprintf(stderr, "usage: %s [-a] use audio\n" @@ -414,10 +414,10 @@ int main(int argc, char **argv) { const sp display = SurfaceComposerClient::getInternalDisplayToken(); CHECK(display != nullptr); - DisplayConfig config; - CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR); + ui::DisplayMode mode; + CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR); - const ui::Size& resolution = config.resolution; + const ui::Size& resolution = mode.resolution; const ssize_t displayWidth = resolution.getWidth(); const ssize_t displayHeight = resolution.getHeight(); diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp index ca058ab4c54de1ca5d7bca0581d0d018e629e462..67c68e65a3c43d38c0a72c2efb7d744d8fffca26 100644 --- a/cmds/stagefright/mediafilter.cpp +++ b/cmds/stagefright/mediafilter.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "RenderScript.h" #include "ScriptC_argbtorgba.h" @@ -752,10 +752,10 @@ int main(int argc, char **argv) { const android::sp display = SurfaceComposerClient::getInternalDisplayToken(); CHECK(display != nullptr); - DisplayConfig config; - CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR); + ui::DisplayMode mode; + CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR); - const ui::Size& resolution = config.resolution; + const ui::Size& resolution = mode.resolution; const ssize_t displayWidth = resolution.getWidth(); const ssize_t displayHeight = resolution.getHeight(); diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp index 37091c43e13e4fb56373d968db8f11a0fc473f62..098c27865a930c4ac02a6ec04d01ecb3327fbaf6 100644 --- a/cmds/stagefright/record.cpp +++ b/cmds/stagefright/record.cpp @@ -259,31 +259,6 @@ int main(int argc, char **argv) { printf("$\n"); #endif -#if 0 - CameraSource *source = CameraSource::Create( - String16(argv[0], strlen(argv[0]))); - source->start(); - - printf("source = %p\n", source); - - for (int i = 0; i < 100; ++i) { - MediaBuffer *buffer; - status_t err = source->read(&buffer); - CHECK_EQ(err, (status_t)OK); - - printf("got a frame, data=%p, size=%d\n", - buffer->data(), buffer->range_length()); - - buffer->release(); - buffer = NULL; - } - - err = source->stop(); - - delete source; - source = NULL; -#endif - if (err != OK && err != ERROR_END_OF_STREAM) { fprintf(stderr, "record failed: %d\n", err); return 1; diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index 250d26b8b8dbc47ae4a31d0cc71d095a56977993..40b23921997862788225a7048db2d9806377c594 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -42,7 +42,7 @@ #include #include -#include +#include using namespace android; @@ -321,10 +321,10 @@ int main(int argc, char **argv) { const sp display = SurfaceComposerClient::getInternalDisplayToken(); CHECK(display != nullptr); - DisplayConfig config; - CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR); + ui::DisplayMode mode; + CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR); - const ui::Size& resolution = config.resolution; + const ui::Size& resolution = mode.resolution; const ssize_t displayWidth = resolution.getWidth(); const ssize_t displayHeight = resolution.getHeight(); diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING index 2595e3eee82880e60037bf93b8702fbd07438863..364289846eadc98495544a096596ea19cae21c5e 100644 --- a/drm/TEST_MAPPING +++ b/drm/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ // The following tests validate codec and drm path. { "name": "GtsMediaTestCases", @@ -9,17 +9,9 @@ }, { "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests" - } - ] - }, - { - "name": "GtsExoPlayerTestCases", - "options" : [ - { - "include-annotation": "android.platform.test.annotations.SocPresubmit" }, { - "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed" + "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests" } ] } diff --git a/drm/drmserver/drmserver.rc b/drm/drmserver/drmserver.rc index de46fb92e7b0870df1046d244c8e035210f0576c..eb176c13841963b50efdab8b8c7b415c150c6cce 100644 --- a/drm/drmserver/drmserver.rc +++ b/drm/drmserver/drmserver.rc @@ -1,5 +1,12 @@ service drm /system/bin/drmserver + disabled class main user drm group drm system inet drmrpc readproc writepid /dev/cpuset/foreground/tasks + +on property:drm.service.enabled=true + start drm + +on property:drm.service.enabled=1 + start drm diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp index b0a441b556273165d5751d10d5b6e99bf72ac073..6cb0a99129137c6f19a5c5db3aa293b9c60cdf9b 100644 --- a/drm/libdrmframework/DrmManagerClientImpl.cpp +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -52,25 +52,11 @@ void DrmManagerClientImpl::remove(int uniqueId) { const sp& DrmManagerClientImpl::getDrmManagerService() { Mutex::Autolock lock(sMutex); if (NULL == sDrmManagerService.get()) { - char value[PROPERTY_VALUE_MAX]; - if (property_get("drm.service.enabled", value, NULL) == 0) { - // Drm is undefined for this device + sp sm = defaultServiceManager(); + sp binder = sm->checkService(String16("drm.drmManager")); + if (binder == NULL) { return sDrmManagerService; } - - sp sm = defaultServiceManager(); - sp binder; - do { - binder = sm->getService(String16("drm.drmManager")); - if (binder != 0) { - break; - } - ALOGW("DrmManagerService not published, waiting..."); - struct timespec reqt; - reqt.tv_sec = 0; - reqt.tv_nsec = 500000000; //0.5 sec - nanosleep(&reqt, NULL); - } while (true); if (NULL == sDeathNotifier.get()) { sDeathNotifier = new DeathNotifier(); } diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index 5ce724173eab745bc130953ebc11d9013ba7ab61..71df58cae8c8b8220eda58dc70db52f70e781bb6 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -60,6 +60,7 @@ cc_library { "android.hardware.drm@1.1", "android.hardware.drm@1.2", "android.hardware.drm@1.3", + "android.hardware.drm@1.4", "libhidlallocatorutils", "libhidlbase", ], @@ -72,6 +73,7 @@ cc_library { "android.hardware.drm@1.0", "android.hardware.drm@1.1", "android.hardware.drm@1.2", + "android.hardware.drm@1.4", ], cflags: [ diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp index 18772e04ad246d9d1f53bdbf75371e18381b94c2..e0db1c4378cf84831d56684cab5077103b80d47e 100644 --- a/drm/libmediadrm/CryptoHal.cpp +++ b/drm/libmediadrm/CryptoHal.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using drm::V1_0::BufferType; using drm::V1_0::DestinationBuffer; @@ -39,6 +40,7 @@ using drm::V1_0::SharedBuffer; using drm::V1_0::Status; using drm::V1_0::SubSample; +using ::android::DrmUtils::toStatusT; using ::android::hardware::hidl_array; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_memory; @@ -53,42 +55,6 @@ typedef drm::V1_2::Status Status_V1_2; namespace android { -static status_t toStatusT(Status status) { - switch (status) { - case Status::OK: - return OK; - case Status::ERROR_DRM_NO_LICENSE: - return ERROR_DRM_NO_LICENSE; - case Status::ERROR_DRM_LICENSE_EXPIRED: - return ERROR_DRM_LICENSE_EXPIRED; - case Status::ERROR_DRM_RESOURCE_BUSY: - return ERROR_DRM_RESOURCE_BUSY; - case Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: - return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; - case Status::ERROR_DRM_SESSION_NOT_OPENED: - return ERROR_DRM_SESSION_NOT_OPENED; - case Status::ERROR_DRM_CANNOT_HANDLE: - return ERROR_DRM_CANNOT_HANDLE; - case Status::ERROR_DRM_DECRYPT: - return ERROR_DRM_DECRYPT; - default: - return UNKNOWN_ERROR; - } -} - -static status_t toStatusT_1_2(Status_V1_2 status) { - switch (status) { - case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE: - return ERROR_DRM_SESSION_LOST_STATE;; - case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE: - return ERROR_DRM_FRAME_TOO_LARGE; - case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY: - return ERROR_DRM_INSUFFICIENT_SECURITY; - default: - return toStatusT(static_cast(status)); - } -} - static hidl_vec toHidlVec(const Vector &vector) { hidl_vec vec; vec.setToExternal(const_cast(vector.array()), vector.size()); @@ -180,6 +146,9 @@ sp CryptoHal::makeCryptoPlugin(const sp& factory, plugin = hPlugin; } ); + if (!hResult.isOk()) { + mInitCheck = DEAD_OBJECT; + } return plugin; } @@ -213,10 +182,8 @@ status_t CryptoHal::createPlugin(const uint8_t uuid[16], const void *data, } } - if (mPlugin == NULL) { - mInitCheck = ERROR_UNSUPPORTED; - } else { - mInitCheck = OK; + if (mInitCheck == NO_INIT) { + mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK; } return mInitCheck; @@ -376,6 +343,7 @@ ssize_t CryptoHal::decrypt(const uint8_t keyId[16], const uint8_t iv[16], Return hResult; + mLock.unlock(); if (mPluginV1_2 != NULL) { hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv), hMode, hPattern, hSubSamples, hSource, offset, hDestination, @@ -384,7 +352,7 @@ ssize_t CryptoHal::decrypt(const uint8_t keyId[16], const uint8_t iv[16], bytesWritten = hBytesWritten; *errorDetailMsg = toString8(hDetailedError); } - err = toStatusT_1_2(status); + err = toStatusT(status); } ); } else { @@ -414,7 +382,8 @@ void CryptoHal::notifyResolution(uint32_t width, uint32_t height) { return; } - mPlugin->notifyResolution(width, height); + auto hResult = mPlugin->notifyResolution(width, height); + ALOGE_IF(!hResult.isOk(), "notifyResolution txn failed %s", hResult.description().c_str()); } status_t CryptoHal::setMediaDrmSession(const Vector &sessionId) { @@ -424,7 +393,12 @@ status_t CryptoHal::setMediaDrmSession(const Vector &sessionId) { return mInitCheck; } - return toStatusT(mPlugin->setMediaDrmSession(toHidlVec(sessionId))); + auto err = mPlugin->setMediaDrmSession(toHidlVec(sessionId)); + return err.isOk() ? toStatusT(err) : DEAD_OBJECT; } +status_t CryptoHal::getLogMessages(Vector &logs) const { + Mutex::Autolock autoLock(mLock); + return DrmUtils::GetLogMessages(mPlugin, logs); +} } // namespace android diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index f218041c46fca26fe37752ed87f8fe50a1655a25..40d1e0c4af4ae38722928e5b076a0c1c4e10177a 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -16,13 +16,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "DrmHal" -#include - -#include - -#include #include +#include #include #include #include @@ -40,7 +36,9 @@ #include #include #include +#include +#include #include using drm::V1_0::KeyedVector; @@ -55,6 +53,7 @@ using drm::V1_1::SecureStopRelease; using drm::V1_1::SecurityLevel; using drm::V1_2::KeySetId; using drm::V1_2::KeyStatusType; +using ::android::DrmUtils::toStatusT; using ::android::hardware::drm::V1_1::DrmMetricGroup; using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; @@ -235,58 +234,6 @@ static List> toKeySetIds(const hidl_vec& return keySetIds; } -static status_t toStatusT(Status status) { - switch (status) { - case Status::OK: - return OK; - break; - case Status::ERROR_DRM_NO_LICENSE: - return ERROR_DRM_NO_LICENSE; - break; - case Status::ERROR_DRM_LICENSE_EXPIRED: - return ERROR_DRM_LICENSE_EXPIRED; - break; - case Status::ERROR_DRM_SESSION_NOT_OPENED: - return ERROR_DRM_SESSION_NOT_OPENED; - break; - case Status::ERROR_DRM_CANNOT_HANDLE: - return ERROR_DRM_CANNOT_HANDLE; - break; - case Status::ERROR_DRM_INVALID_STATE: - return ERROR_DRM_INVALID_STATE; - break; - case Status::BAD_VALUE: - return BAD_VALUE; - break; - case Status::ERROR_DRM_NOT_PROVISIONED: - return ERROR_DRM_NOT_PROVISIONED; - break; - case Status::ERROR_DRM_RESOURCE_BUSY: - return ERROR_DRM_RESOURCE_BUSY; - break; - case Status::ERROR_DRM_DEVICE_REVOKED: - return ERROR_DRM_DEVICE_REVOKED; - break; - case Status::ERROR_DRM_UNKNOWN: - default: - return ERROR_DRM_UNKNOWN; - break; - } -} - -static status_t toStatusT_1_2(Status_V1_2 status) { - switch (status) { - case Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION: - return ERROR_DRM_RESOURCE_CONTENTION; - case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE: - return ERROR_DRM_FRAME_TOO_LARGE; - case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY: - return ERROR_DRM_INSUFFICIENT_SECURITY; - default: - return toStatusT(static_cast(status)); - } -} - Mutex DrmHal::mLock; struct DrmHal::DrmSessionClient : public aidl::android::media::BnResourceManagerClient { @@ -370,8 +317,7 @@ void DrmHal::cleanup() { closeOpenSessions(); Mutex::Autolock autoLock(mLock); - reportPluginMetrics(); - reportFrameworkMetrics(); + reportFrameworkMetrics(reportPluginMetrics()); setListener(NULL); mInitCheck = NO_INIT; @@ -387,18 +333,19 @@ void DrmHal::cleanup() { mPlugin.clear(); mPluginV1_1.clear(); mPluginV1_2.clear(); + mPluginV1_4.clear(); } std::vector> DrmHal::makeDrmFactories() { - std::vector> factories(DrmUtils::MakeDrmFactories()); + static std::vector> factories(DrmUtils::MakeDrmFactories()); if (factories.size() == 0) { // must be in passthrough mode, load the default passthrough service auto passthrough = IDrmFactory::getService(); if (passthrough != NULL) { - ALOGI("makeDrmFactories: using default passthrough drm instance"); + DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance"); factories.push_back(passthrough); } else { - ALOGE("Failed to find any drm factories"); + DrmUtils::LOG2BE("Failed to find any drm factories"); } } return factories; @@ -414,7 +361,7 @@ sp DrmHal::makeDrmPlugin(const sp& factory, Return hResult = factory->createPlugin(uuid, appPackageName.string(), [&](Status status, const sp& hPlugin) { if (status != Status::OK) { - ALOGE("Failed to make drm plugin"); + DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status); return; } plugin = hPlugin; @@ -422,7 +369,8 @@ sp DrmHal::makeDrmPlugin(const sp& factory, ); if (!hResult.isOk()) { - ALOGE("createPlugin remote call failed"); + DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s", + hResult.description().c_str()); } return plugin; @@ -616,18 +564,21 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], Mutex::Autolock autoLock(mLock); for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { - if (mFactories[i]->isCryptoSchemeSupported(uuid)) { + auto hResult = mFactories[i]->isCryptoSchemeSupported(uuid); + if (hResult.isOk() && hResult) { auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); if (plugin != NULL) { mPlugin = plugin; mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin); mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin); + mPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mPlugin); break; } } } if (mPlugin == NULL) { + DrmUtils::LOG2BE(uuid, "No supported hal instance found"); mInitCheck = ERROR_UNSUPPORTED; } else { mInitCheck = OK; @@ -642,6 +593,7 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], mPlugin.clear(); mPluginV1_1.clear(); mPluginV1_2.clear(); + mPluginV1_4.clear(); } } @@ -822,7 +774,7 @@ status_t DrmHal::getKeyRequest(Vector const &sessionId, defaultUrl = toString8(hDefaultUrl); *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); } - err = toStatusT_1_2(status); + err = toStatusT(status); }); } else if (mPluginV1_1 != NULL) { hResult = mPluginV1_1->getKeyRequest_1_1( @@ -936,7 +888,7 @@ status_t DrmHal::getProvisionRequest(String8 const &certType, Return hResult; if (mPluginV1_2 != NULL) { - Return hResult = mPluginV1_2->getProvisionRequest_1_2( + hResult = mPluginV1_2->getProvisionRequest_1_2( toHidlString(certType), toHidlString(certAuthority), [&](Status_V1_2 status, const hidl_vec& hRequest, const hidl_string& hDefaultUrl) { @@ -944,11 +896,11 @@ status_t DrmHal::getProvisionRequest(String8 const &certType, request = toVector(hRequest); defaultUrl = toString8(hDefaultUrl); } - err = toStatusT_1_2(status); + err = toStatusT(status); } ); } else { - Return hResult = mPlugin->getProvisionRequest( + hResult = mPlugin->getProvisionRequest( toHidlString(certType), toHidlString(certAuthority), [&](Status status, const hidl_vec& hRequest, const hidl_string& hDefaultUrl) { @@ -1116,7 +1068,7 @@ status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel *connected, *connected = toHdcpLevel(hConnected); *max = toHdcpLevel(hMax); } - err = toStatusT_1_2(status); + err = toStatusT(status); }); } else if (mPluginV1_1 != NULL) { hResult = mPluginV1_1->getHdcpLevels( @@ -1511,7 +1463,7 @@ status_t DrmHal::signRSA(Vector const &sessionId, return hResult.isOk() ? err : DEAD_OBJECT; } -void DrmHal::reportFrameworkMetrics() const +std::string DrmHal::reportFrameworkMetrics(const std::string& pluginMetrics) const { mediametrics_handle_t item(mediametrics_create("mediadrm")); mediametrics_setUid(item, mMetrics.GetAppUid()); @@ -1540,21 +1492,26 @@ void DrmHal::reportFrameworkMetrics() const if (!b64EncodedMetrics.empty()) { mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str()); } + if (!pluginMetrics.empty()) { + mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str()); + } if (!mediametrics_selfRecord(item)) { ALOGE("Failed to self record framework metrics"); } mediametrics_delete(item); + return serializedMetrics; } -void DrmHal::reportPluginMetrics() const +std::string DrmHal::reportPluginMetrics() const { Vector metricsVector; String8 vendor; String8 description; + std::string metricsString; if (getPropertyStringInternal(String8("vendor"), vendor) == OK && getPropertyStringInternal(String8("description"), description) == OK && getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) { - std::string metricsString = toBase64StringNoPad(metricsVector.array(), + metricsString = toBase64StringNoPad(metricsVector.array(), metricsVector.size()); status_t res = android::reportDrmPluginMetrics(metricsString, vendor, description, mMetrics.GetAppUid()); @@ -1562,6 +1519,55 @@ void DrmHal::reportPluginMetrics() const ALOGE("Metrics were retrieved but could not be reported: %d", res); } } + return metricsString; +} + +status_t DrmHal::requiresSecureDecoder(const char *mime, bool *required) const { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return false; + } + auto hResult = mPluginV1_4->requiresSecureDecoderDefault(hidl_string(mime)); + if (!hResult.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); + return DEAD_OBJECT; + } + if (required) { + *required = hResult; + } + return OK; +} + +status_t DrmHal::requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, + bool *required) const { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return false; + } + auto hLevel = toHidlSecurityLevel(securityLevel); + auto hResult = mPluginV1_4->requiresSecureDecoder(hidl_string(mime), hLevel); + if (!hResult.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); + return DEAD_OBJECT; + } + if (required) { + *required = hResult; + } + return OK; +} + +status_t DrmHal::setPlaybackId(Vector const &sessionId, const char *playbackId) { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return ERROR_UNSUPPORTED; + } + auto err = mPluginV1_4->setPlaybackId(toHidlVec(sessionId), hidl_string(playbackId)); + return err.isOk() ? toStatusT(err) : DEAD_OBJECT; +} + +status_t DrmHal::getLogMessages(Vector &logs) const { + Mutex::Autolock autoLock(mLock); + return DrmUtils::GetLogMessages(mPlugin, logs); } } // namespace android diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp index d85fa61a522661759c42f15f766de322e8b4edd9..0b117a3cd21f828fa7282989cbdc6c38c3bd35cd 100644 --- a/drm/libmediadrm/DrmUtils.cpp +++ b/drm/libmediadrm/DrmUtils.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -41,6 +43,9 @@ #include #include +#include +#include + using HServiceManager = ::android::hidl::manager::V1_2::IServiceManager; using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; @@ -64,20 +69,30 @@ Hal *MakeObject(status_t *pstatus) { return obj; } -template -void MakeHidlFactories(const uint8_t uuid[16], V &factories) { +template +void MakeHidlFactories(const uint8_t uuid[16], V &factories, M& instances) { sp serviceManager = HServiceManager::getService(); if (serviceManager == nullptr) { - ALOGE("Failed to get service manager"); - exit(-1); + LOG2BE("Failed to get service manager"); + return; } serviceManager->listManifestByInterface(Hal::descriptor, [&](const hidl_vec ®istered) { for (const auto &instance : registered) { auto factory = Hal::getService(instance); if (factory != nullptr) { - ALOGI("found %s %s", Hal::descriptor, instance.c_str()); - if (!uuid || factory->isCryptoSchemeSupported(uuid)) { + instances[instance.c_str()] = Hal::descriptor; + if (!uuid) { + factories.push_back(factory); + continue; + } + auto supported = factory->isCryptoSchemeSupported(uuid); + if (!supported.isOk()) { + LOG2BE(uuid, "isCryptoSchemeSupported txn failed: %s", + supported.description().c_str()); + continue; + } + if (supported) { factories.push_back(factory); } } @@ -85,6 +100,12 @@ void MakeHidlFactories(const uint8_t uuid[16], V &factories) { }); } +template +void MakeHidlFactories(const uint8_t uuid[16], V &factories) { + std::map instances; + MakeHidlFactories(uuid, factories, instances); +} + hidl_vec toHidlVec(const void *ptr, size_t size) { hidl_vec vec(size); if (ptr != nullptr) { @@ -103,28 +124,42 @@ hidl_array toHidlArray16(const uint8_t *ptr) { sp<::V1_0::IDrmPlugin> MakeDrmPlugin(const sp<::V1_0::IDrmFactory> &factory, const uint8_t uuid[16], const char *appPackageName) { sp<::V1_0::IDrmPlugin> plugin; - factory->createPlugin(toHidlArray16(uuid), hidl_string(appPackageName), - [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) { - if (status != ::V1_0::Status::OK) { - return; - } - plugin = hPlugin; - }); - return plugin; + auto err = factory->createPlugin( + toHidlArray16(uuid), hidl_string(appPackageName), + [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) { + if (status != ::V1_0::Status::OK) { + LOG2BE(uuid, "MakeDrmPlugin failed: %d", status); + return; + } + plugin = hPlugin; + }); + if (err.isOk()) { + return plugin; + } else { + LOG2BE(uuid, "MakeDrmPlugin txn failed: %s", err.description().c_str()); + return nullptr; + } } sp<::V1_0::ICryptoPlugin> MakeCryptoPlugin(const sp<::V1_0::ICryptoFactory> &factory, const uint8_t uuid[16], const void *initData, size_t initDataSize) { sp<::V1_0::ICryptoPlugin> plugin; - factory->createPlugin(toHidlArray16(uuid), toHidlVec(initData, initDataSize), - [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) { - if (status != ::V1_0::Status::OK) { - return; - } - plugin = hPlugin; - }); - return plugin; + auto err = factory->createPlugin( + toHidlArray16(uuid), toHidlVec(initData, initDataSize), + [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) { + if (status != ::V1_0::Status::OK) { + LOG2BE(uuid, "MakeCryptoPlugin failed: %d", status); + return; + } + plugin = hPlugin; + }); + if (err.isOk()) { + return plugin; + } else { + LOG2BE(uuid, "MakeCryptoPlugin txn failed: %s", err.description().c_str()); + return nullptr; + } } } // namespace @@ -143,10 +178,15 @@ sp MakeCrypto(status_t *pstatus) { std::vector> MakeDrmFactories(const uint8_t uuid[16]) { std::vector> drmFactories; - MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories); - MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories); - MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories); - MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories); + std::map instances; + MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories, instances); + MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories, instances); + MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories, instances); + MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories, instances); + MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories, instances); + for (auto const& entry : instances) { + LOG2BI("found instance=%s version=%s", entry.first.c_str(), entry.second.c_str()); + } return drmFactories; } @@ -165,6 +205,7 @@ std::vector> MakeCryptoFactories(const uint8_t uuid[1 MakeHidlFactories<::V1_1::ICryptoFactory>(uuid, cryptoFactories); MakeHidlFactories<::V1_2::ICryptoFactory>(uuid, cryptoFactories); MakeHidlFactories<::V1_3::ICryptoFactory>(uuid, cryptoFactories); + MakeHidlFactories<::V1_4::ICryptoFactory>(uuid, cryptoFactories); return cryptoFactories; } @@ -177,5 +218,160 @@ std::vector> MakeCryptoPlugins(const uint8_t uuid[16], const v return plugins; } +status_t toStatusT_1_4(::V1_4::Status status) { + switch (status) { + case ::V1_4::Status::OK: + return OK; + case ::V1_4::Status::BAD_VALUE: + return BAD_VALUE; + case ::V1_4::Status::ERROR_DRM_CANNOT_HANDLE: + return ERROR_DRM_CANNOT_HANDLE; + case ::V1_4::Status::ERROR_DRM_DECRYPT: + return ERROR_DRM_DECRYPT; + case ::V1_4::Status::ERROR_DRM_DEVICE_REVOKED: + return ERROR_DRM_DEVICE_REVOKED; + case ::V1_4::Status::ERROR_DRM_FRAME_TOO_LARGE: + return ERROR_DRM_FRAME_TOO_LARGE; + case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: + return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; + case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_SECURITY: + return ERROR_DRM_INSUFFICIENT_SECURITY; + case ::V1_4::Status::ERROR_DRM_INVALID_STATE: + return ERROR_DRM_INVALID_STATE; + case ::V1_4::Status::ERROR_DRM_LICENSE_EXPIRED: + return ERROR_DRM_LICENSE_EXPIRED; + case ::V1_4::Status::ERROR_DRM_NO_LICENSE: + return ERROR_DRM_NO_LICENSE; + case ::V1_4::Status::ERROR_DRM_NOT_PROVISIONED: + return ERROR_DRM_NOT_PROVISIONED; + case ::V1_4::Status::ERROR_DRM_RESOURCE_BUSY: + return ERROR_DRM_RESOURCE_BUSY; + case ::V1_4::Status::ERROR_DRM_RESOURCE_CONTENTION: + return ERROR_DRM_RESOURCE_CONTENTION; + case ::V1_4::Status::ERROR_DRM_SESSION_LOST_STATE: + return ERROR_DRM_SESSION_LOST_STATE; + case ::V1_4::Status::ERROR_DRM_SESSION_NOT_OPENED: + return ERROR_DRM_SESSION_NOT_OPENED; + + // New in S / drm@1.4: + case ::V1_4::Status::CANNOT_DECRYPT_ZERO_SUBSAMPLES: + return ERROR_DRM_ZERO_SUBSAMPLES; + case ::V1_4::Status::CRYPTO_LIBRARY_ERROR: + return ERROR_DRM_CRYPTO_LIBRARY; + case ::V1_4::Status::GENERAL_OEM_ERROR: + return ERROR_DRM_GENERIC_OEM; + case ::V1_4::Status::GENERAL_PLUGIN_ERROR: + return ERROR_DRM_GENERIC_PLUGIN; + case ::V1_4::Status::INIT_DATA_INVALID: + return ERROR_DRM_INIT_DATA; + case ::V1_4::Status::KEY_NOT_LOADED: + return ERROR_DRM_KEY_NOT_LOADED; + case ::V1_4::Status::LICENSE_PARSE_ERROR: + return ERROR_DRM_LICENSE_PARSE; + case ::V1_4::Status::LICENSE_POLICY_ERROR: + return ERROR_DRM_LICENSE_POLICY; + case ::V1_4::Status::LICENSE_RELEASE_ERROR: + return ERROR_DRM_LICENSE_RELEASE; + case ::V1_4::Status::LICENSE_REQUEST_REJECTED: + return ERROR_DRM_LICENSE_REQUEST_REJECTED; + case ::V1_4::Status::LICENSE_RESTORE_ERROR: + return ERROR_DRM_LICENSE_RESTORE; + case ::V1_4::Status::LICENSE_STATE_ERROR: + return ERROR_DRM_LICENSE_STATE; + case ::V1_4::Status::MALFORMED_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MALFORMED; + case ::V1_4::Status::MEDIA_FRAMEWORK_ERROR: + return ERROR_DRM_MEDIA_FRAMEWORK; + case ::V1_4::Status::MISSING_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MISSING; + case ::V1_4::Status::PROVISIONING_CERTIFICATE_ERROR: + return ERROR_DRM_PROVISIONING_CERTIFICATE; + case ::V1_4::Status::PROVISIONING_CONFIGURATION_ERROR: + return ERROR_DRM_PROVISIONING_CONFIG; + case ::V1_4::Status::PROVISIONING_PARSE_ERROR: + return ERROR_DRM_PROVISIONING_PARSE; + case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED: + return ERROR_DRM_PROVISIONING_REQUEST_REJECTED; + case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR: + return ERROR_DRM_PROVISIONING_RETRY; + case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR: + return ERROR_DRM_SECURE_STOP_RELEASE; + case ::V1_4::Status::STORAGE_READ_FAILURE: + return ERROR_DRM_STORAGE_READ; + case ::V1_4::Status::STORAGE_WRITE_FAILURE: + return ERROR_DRM_STORAGE_WRITE; + + case ::V1_4::Status::ERROR_DRM_UNKNOWN: + default: + return ERROR_DRM_UNKNOWN; + } + return ERROR_DRM_UNKNOWN; +} + +namespace { +char logPriorityToChar(::V1_4::LogPriority priority) { + char p = 'U'; + switch (priority) { + case ::V1_4::LogPriority::VERBOSE: p = 'V'; break; + case ::V1_4::LogPriority::DEBUG: p = 'D'; break; + case ::V1_4::LogPriority::INFO: p = 'I'; break; + case ::V1_4::LogPriority::WARN: p = 'W'; break; + case ::V1_4::LogPriority::ERROR: p = 'E'; break; + case ::V1_4::LogPriority::FATAL: p = 'F'; break; + default: p = 'U'; break; + } + return p; +} +} // namespace + +std::string GetExceptionMessage(status_t err, const char *msg, + const Vector<::V1_4::LogMessage> &logs) { + std::string ruler("=============================="); + std::string header("Beginning of DRM Plugin Log"); + std::string footer("End of DRM Plugin Log"); + String8 msg8; + if (msg) { + msg8 += msg; + msg8 += ": "; + } + auto errStr = StrCryptoError(err); + msg8 += errStr.c_str(); + msg8 += String8::format("\n%s %s %s", ruler.c_str(), header.c_str(), ruler.c_str()); + + for (auto log : logs) { + time_t seconds = log.timeMs / 1000; + int ms = log.timeMs % 1000; + char buf[64] = {0}; + std::string timeStr = "00-00 00:00:00"; + if (strftime(buf, sizeof buf, "%m-%d %H:%M:%S", std::localtime(&seconds))) { + timeStr = buf; + } + + char p = logPriorityToChar(log.priority); + msg8 += String8::format("\n %s.%03d %c %s", timeStr.c_str(), ms, p, log.message.c_str()); + } + + msg8 += String8::format("\n%s %s %s", ruler.c_str(), footer.c_str(), ruler.c_str()); + return msg8.c_str(); +} + +void LogBuffer::addLog(const ::V1_4::LogMessage &log) { + std::unique_lock lock(mMutex); + mBuffer.push_back(log); + while (mBuffer.size() > MAX_CAPACITY) { + mBuffer.pop_front(); + } +} + +Vector<::V1_4::LogMessage> LogBuffer::getLogs() { + std::unique_lock lock(mMutex); + Vector<::V1_4::LogMessage> logs; + for (auto log : mBuffer) { + logs.push_back(log); + } + return logs; +} + +LogBuffer gLogBuf; } // namespace DrmUtils } // namespace android diff --git a/drm/libmediadrm/fuzzer/Android.bp b/drm/libmediadrm/fuzzer/Android.bp index 9faed3af383ab10620975ab8d2f74630c5cc16e1..49bbad4a8a2d346fac9cc9249d3832d2962058ea 100644 --- a/drm/libmediadrm/fuzzer/Android.bp +++ b/drm/libmediadrm/fuzzer/Android.bp @@ -56,6 +56,7 @@ cc_fuzz { "android.hardware.drm@1.1", "android.hardware.drm@1.2", "android.hardware.drm@1.3", + "android.hardware.drm@1.4", "libhidlallocatorutils", "libhidlbase", ], diff --git a/drm/libmediadrm/include/mediadrm/CryptoHal.h b/drm/libmediadrm/include/mediadrm/CryptoHal.h index c9fda679b79364de7f9b77aac85d88c569123e94..5fd39e64f2c1c574d4c9a4028f74be02bd127484 100644 --- a/drm/libmediadrm/include/mediadrm/CryptoHal.h +++ b/drm/libmediadrm/include/mediadrm/CryptoHal.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,8 @@ struct CryptoHal : public ICrypto { } virtual void unsetHeap(int32_t seqNum) { clearHeapBase(seqNum); } + virtual status_t getLogMessages(Vector &logs) const; + private: mutable Mutex mLock; diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h index 3b4639bec6eab29e595a65d6d6dd648cc4a99c25..7eb1decbc8f57ffbfca835d69b92076d6c4d29db 100644 --- a/drm/libmediadrm/include/mediadrm/DrmHal.h +++ b/drm/libmediadrm/include/mediadrm/DrmHal.h @@ -25,7 +25,10 @@ #include #include #include +#include +#include +#include #include #include #include @@ -176,6 +179,17 @@ struct DrmHal : public IDrm, virtual status_t setListener(const sp& listener); + virtual status_t requiresSecureDecoder(const char *mime, bool *required) const; + + virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, + bool *required) const; + + virtual status_t setPlaybackId( + Vector const &sessionId, + const char *playbackId); + + virtual status_t getLogMessages(Vector &logs) const; + // Methods of IDrmPluginListener Return sendEvent(EventType eventType, const hidl_vec& sessionId, const hidl_vec& data); @@ -202,6 +216,7 @@ private: sp mPlugin; sp mPluginV1_1; sp mPluginV1_2; + sp mPluginV1_4; String8 mAppPackageName; // Mutable to allow modification within GetPropertyByteArray. @@ -225,8 +240,8 @@ private: void writeByteArray(Parcel &obj, const hidl_vec& array); - void reportPluginMetrics() const; - void reportFrameworkMetrics() const; + std::string reportPluginMetrics() const; + std::string reportFrameworkMetrics(const std::string& pluginMetrics) const; status_t getPropertyStringInternal(String8 const &name, String8 &value) const; status_t getPropertyByteArrayInternal(String8 const &name, Vector &value) const; diff --git a/drm/libmediadrm/include/mediadrm/IDrm.h b/drm/libmediadrm/include/mediadrm/IDrm.h index 0177c246bb3f4ff3136123749add4fbc0d5fccfa..a88784d9cc2bdc141f96929a54475726eec708fb 100644 --- a/drm/libmediadrm/include/mediadrm/IDrm.h +++ b/drm/libmediadrm/include/mediadrm/IDrm.h @@ -24,6 +24,15 @@ #define ANDROID_IDRM_H_ namespace android { +namespace hardware { +namespace drm { +namespace V1_4 { +struct LogMessage; +} // namespace V1_4 +} // namespace drm +} // namespace hardware + +namespace drm = ::android::hardware::drm; struct AString; @@ -145,6 +154,17 @@ struct IDrm : public virtual RefBase { virtual status_t setListener(const sp& listener) = 0; + virtual status_t requiresSecureDecoder(const char *mime, bool *required) const = 0; + + virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, + bool *required) const = 0; + + virtual status_t setPlaybackId( + Vector const &sessionId, + const char *playbackId) = 0; + + virtual status_t getLogMessages(Vector &logs) const = 0; + protected: IDrm() {} diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h index 20b3fe9ed9bcbab6aee01b54a85926483c597849..ec0b8781dddaf40a9c8953136516e98bafcf99e5 100644 --- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h +++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h @@ -19,11 +19,30 @@ #include #include +#include +#include +#include #include // for status_t +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include + using namespace ::android::hardware::drm; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; namespace android { @@ -32,6 +51,61 @@ struct IDrm; namespace DrmUtils { +// Log APIs +class LogBuffer { + public: + static const int MAX_CAPACITY = 100; + void addLog(const ::V1_4::LogMessage &log); + Vector<::V1_4::LogMessage> getLogs(); + + private: + std::deque<::V1_4::LogMessage> mBuffer; + std::mutex mMutex; +}; + +extern LogBuffer gLogBuf; + +static inline int formatBuffer(char *buf, size_t size, const char *msg) { + return snprintf(buf, size, "%s", msg); +} + +template +static inline int formatBuffer(char *buf, size_t size, const char *fmt, First first, Args... args) { + return snprintf(buf, size, fmt, first, args...); +} + +template +void LogToBuffer(android_LogPriority level, const char *fmt, Args... args) { + const int LOG_BUF_SIZE = 256; + char buf[LOG_BUF_SIZE]; + int len = formatBuffer(buf, LOG_BUF_SIZE, fmt, args...); + if (len <= 0) { + return; + } + __android_log_write(level, LOG_TAG, buf); + if (level >= ANDROID_LOG_INFO) { + int64_t epochTimeMs = + std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1); + gLogBuf.addLog({epochTimeMs, static_cast<::V1_4::LogPriority>(level), buf}); + } +} + +template +void LogToBuffer(android_LogPriority level, const uint8_t uuid[16], const char *fmt, Args... args) { + const uint64_t* uuid2 = reinterpret_cast(uuid); + std::string uuidFmt("uuid=[%lx %lx] "); + uuidFmt += fmt; + LogToBuffer(level, uuidFmt.c_str(), htobe64(uuid2[0]), htobe64(uuid2[1]), args...); +} + +#ifndef LOG2BE +#define LOG2BE(...) LogToBuffer(ANDROID_LOG_ERROR, __VA_ARGS__) +#define LOG2BW(...) LogToBuffer(ANDROID_LOG_WARN, __VA_ARGS__) +#define LOG2BI(...) LogToBuffer(ANDROID_LOG_INFO, __VA_ARGS__) +#define LOG2BD(...) LogToBuffer(ANDROID_LOG_DEBUG, __VA_ARGS__) +#define LOG2BV(...) LogToBuffer(ANDROID_LOG_VERBOSE, __VA_ARGS__) +#endif + bool UseDrmService(); sp MakeDrm(status_t *pstatus = nullptr); @@ -91,8 +165,74 @@ std::vector> MakeCryptoFactories(const uint8_t uuid[1 std::vector> MakeCryptoPlugins(const uint8_t uuid[16], const void *initData, size_t initDataSize); -} // namespace DrmUtils +status_t toStatusT_1_4(::V1_4::Status status); -} // namespace android +template +inline status_t toStatusT(S status) { + auto err = static_cast<::V1_4::Status>(status); + return toStatusT_1_4(err); +} + +template +inline status_t toStatusT(const android::hardware::Return &status) { + auto t = static_cast(status); + auto err = static_cast<::V1_4::Status>(t); + return toStatusT_1_4(err); +} + +template +status_t GetLogMessages(const sp &obj, Vector<::V1_4::LogMessage> &logs) { + sp plugin = T::castFrom(obj); + if (obj == NULL) { + LOG2BW("%s obj is null", U::descriptor); + } else if (plugin == NULL) { + LOG2BW("Cannot cast %s obj to %s plugin", U::descriptor, T::descriptor); + } + + ::V1_4::Status err{}; + std::vector<::V1_4::LogMessage> pluginLogs; + ::V1_4::IDrmPlugin::getLogMessages_cb cb = [&]( + ::V1_4::Status status, + hidl_vec<::V1_4::LogMessage> hLogs) { + if (::V1_4::Status::OK != status) { + err = status; + return; + } + pluginLogs.assign(hLogs.data(), hLogs.data() + hLogs.size()); + }; + + Return hResult; + if (plugin != NULL) { + hResult = plugin->getLogMessages(cb); + } + if (!hResult.isOk()) { + LOG2BW("%s::getLogMessages remote call failed %s", + T::descriptor, hResult.description().c_str()); + } + + auto allLogs(gLogBuf.getLogs()); + LOG2BD("framework logs size %zu; plugin logs size %zu", + allLogs.size(), pluginLogs.size()); + std::copy(pluginLogs.begin(), pluginLogs.end(), std::back_inserter(allLogs)); + std::sort(allLogs.begin(), allLogs.end(), + [](const ::V1_4::LogMessage &a, const ::V1_4::LogMessage &b) { + return a.timeMs < b.timeMs; + }); + + logs.appendVector(allLogs); + return OK; +} + +std::string GetExceptionMessage(status_t err, const char *msg, + const Vector<::V1_4::LogMessage> &logs); +template +std::string GetExceptionMessage(status_t err, const char *msg, const sp &iface) { + Vector<::V1_4::LogMessage> logs; + iface->getLogMessages(logs); + return GetExceptionMessage(err, msg, logs); +} + +} // namespace DrmUtils +} // namespace android #endif // ANDROID_DRMUTILS_H diff --git a/drm/libmediadrm/interface/mediadrm/ICrypto.h b/drm/libmediadrm/interface/mediadrm/ICrypto.h index df980aede5c7512842cdeed5a019b41cc29128d9..2c4df60b35cd971dcdc712e31d941e889fd93746 100644 --- a/drm/libmediadrm/interface/mediadrm/ICrypto.h +++ b/drm/libmediadrm/interface/mediadrm/ICrypto.h @@ -32,6 +32,10 @@ namespace V1_0 { struct SharedBuffer; struct DestinationBuffer; } // namespace V1_0 + +namespace V1_4 { +struct LogMessage; +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android @@ -83,6 +87,8 @@ struct ICrypto : public RefBase { virtual int32_t setHeap(const sp& heap) = 0; virtual void unsetHeap(int32_t seqNum) = 0; + virtual status_t getLogMessages(Vector &logs) const = 0; + protected: ICrypto() {} diff --git a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp index 53ffae4ccdbe58fa695dead80a2d6b3d321244ca..a2d506da44d824165e2c2fdc3c6ef9e5fb748734 100644 --- a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp +++ b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp @@ -61,7 +61,7 @@ bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet, // all the base64 encoded keys. Each key is also stored separately as // a JSON object in mJsonObjects[1..n] where n is the total // number of keys in the set. - if (!isJsonWebKeySet(mJsonObjects[0])) { + if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) { return false; } diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp index 0ac879c239252c9ae0b174e4e1126a548bcfa878..e03a8962502434ba6df344a5aebce32362ef74f4 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp @@ -26,7 +26,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::SubSample; @@ -79,7 +79,7 @@ Status AesCtrDecryptor::decrypt( } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp index c49d5fece0fcc80a5b37f6eb0566e4654d616a12..6c68532f2d089629eccb6e352fb6364db5ccd2bc 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp +++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp @@ -57,6 +57,7 @@ cc_defaults { "android.hardware.drm@1.1", "android.hardware.drm@1.2", "android.hardware.drm@1.3", + "android.hardware.drm@1.4", "libbase", "libbinder", "libcrypto", @@ -110,18 +111,18 @@ cc_binary { } cc_binary { - name: "android.hardware.drm@1.3-service.clearkey", + name: "android.hardware.drm@1.4-service.clearkey", defaults: ["clearkey_service_defaults"], srcs: ["service.cpp"], - init_rc: ["android.hardware.drm@1.3-service.clearkey.rc"], - vintf_fragments: ["manifest_android.hardware.drm@1.3-service.clearkey.xml"], + init_rc: ["android.hardware.drm@1.4-service.clearkey.rc"], + vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"], } cc_binary { - name: "android.hardware.drm@1.3-service-lazy.clearkey", - overrides: ["android.hardware.drm@1.3-service.clearkey"], + name: "android.hardware.drm@1.4-service-lazy.clearkey", + overrides: ["android.hardware.drm@1.4-service.clearkey"], defaults: ["clearkey_service_defaults"], srcs: ["serviceLazy.cpp"], - init_rc: ["android.hardware.drm@1.3-service-lazy.clearkey.rc"], - vintf_fragments: ["manifest_android.hardware.drm@1.3-service.clearkey.xml"], + init_rc: ["android.hardware.drm@1.4-service-lazy.clearkey.rc"], + vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"], } diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp index 657a42f2e30ceb81352f3eb4d9f5438b5b36637a..d81f87523585764947e22fa026678c941df8f8ec 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp @@ -21,7 +21,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { sp decodeBase64(const std::string &s) { @@ -169,7 +169,7 @@ void encodeBase64Url(const void *_data, size_t size, std::string *out) { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp index 75f8395a161a2fd8c0be8fdbf0f936dfa6a0af61..dcb76f4a56e02f1d3e915ff76a0fcc191a46f2a5 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp @@ -21,7 +21,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { Buffer::Buffer(size_t capacity) @@ -47,7 +47,7 @@ Buffer::~Buffer() { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp index bfb0e05fa09150b4a327bca862cbff6867ce6a01..4ab33d3a4121084465f0e8e58927796b31dc2130 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp @@ -22,7 +22,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { extern "C" { @@ -38,7 +38,7 @@ ICryptoFactory* createCryptoFactory() { } // extern "C" } // namespace clearkey -} // namespace V1_3 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp index a6ed3bd6738c80fc5378fdebdcc909f612fc5cb1..0bebc3b122d9b93979e78beaa12102051b7d4426 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp @@ -27,11 +27,11 @@ namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_2::clearkey::CryptoPlugin; +using ::android::hardware::drm::V1_4::clearkey::CryptoPlugin; Return CryptoFactory::isCryptoSchemeSupported( const hidl_array &uuid) @@ -63,7 +63,7 @@ Return CryptoFactory::createPlugin( } } // namespace clearkey -} // namespace V1_3 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp index 302dd39ddc90dac10947e5a6bd1d83ac6e54b55a..3a675f64df0dc196ada922197546683e9a37f207 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp @@ -27,7 +27,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::BufferType; @@ -235,8 +235,23 @@ Return CryptoPlugin::setMediaDrmSession( return Status::OK; } +Return CryptoPlugin::getLogMessages( + getLogMessages_cb _hidl_cb) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::system_clock; + + auto timeMillis = duration_cast( + system_clock::now().time_since_epoch()).count(); + + std::vector logs = { + { timeMillis, LogPriority::ERROR, std::string("Not implemented") }}; + _hidl_cb(drm::V1_4::Status::OK, toHidlVec(logs)); + return Void(); +} + } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4. } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp index 2415b6fe5edd284d7992bcb91e7be212190da41e..0385d8fad812949555922881d0e304ee85924740 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp @@ -38,7 +38,7 @@ bool Hash(const std::string& data, std::string* hash) { namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { bool DeviceFiles::StoreLicense( @@ -246,7 +246,7 @@ ssize_t DeviceFiles::GetFileSize(const std::string& fileName) const { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp index 1ce8269787a2657799993a3b9ad397bfa1a82195..14cb5c1ff6718c09a851cdf88e8819e229d13a34 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp @@ -31,13 +31,13 @@ namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::Status; using ::android::hardware::drm::V1_1::SecurityLevel; -using ::android::hardware::drm::V1_2::clearkey::DrmPlugin; -using ::android::hardware::drm::V1_2::clearkey::SessionLibrary; +using ::android::hardware::drm::V1_4::clearkey::DrmPlugin; +using ::android::hardware::drm::V1_4::clearkey::SessionLibrary; using ::android::hardware::Void; Return DrmFactory::isCryptoSchemeSupported( @@ -105,7 +105,7 @@ Return DrmFactory::debug(const hidl_handle& fd, const hidl_vec +#include #include #include @@ -58,7 +59,7 @@ std::vector uint32ToVector(uint32_t value) { namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { KeyRequestType toKeyRequestType_V1_0(KeyRequestType_V1_1 keyRequestType) { @@ -635,6 +636,48 @@ Return DrmPlugin::getSecurityLevel(const hidl_vec& sessionId, return Void(); } +Return DrmPlugin::getLogMessages( + getLogMessages_cb _hidl_cb) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::system_clock; + + auto timeMillis = duration_cast( + system_clock::now().time_since_epoch()).count(); + + std::vector logs = { + { timeMillis, LogPriority::ERROR, std::string("Not implemented") }}; + _hidl_cb(drm::V1_4::Status::OK, toHidlVec(logs)); + return Void(); +} + +Return DrmPlugin::requiresSecureDecoder( + const hidl_string& mime, SecurityLevel level) { + UNUSED(mime); + UNUSED(level); + return false; +} + +Return DrmPlugin::requiresSecureDecoderDefault(const hidl_string& mime) { + UNUSED(mime); + // Clearkey only supports SW_SECURE_CRYPTO, so we always returns false + // regardless of mime type. + return false; +} + +Return DrmPlugin::setPlaybackId( + const hidl_vec& sessionId, + const hidl_string& playbackId) { + if (sessionId.size() == 0) { + ALOGE("Invalid empty session id"); + return Status::BAD_VALUE; + } + + std::vector sid = toVector(sessionId); + mPlaybackId[sid] = playbackId; + return Status::OK; +} + Return DrmPlugin::setSecurityLevel(const hidl_vec& sessionId, SecurityLevel level) { if (sessionId.size() == 0) { @@ -702,9 +745,25 @@ Return DrmPlugin::getMetrics(getMetrics_cb _hidl_cb) { "close_session", { closeSessionNotOpenedAttribute }, { closeSessionNotOpenedMetricValue } }; - DrmMetricGroup metrics = { { openSessionMetric, closeSessionMetric, - closeSessionNotOpenedMetric } }; - + // Set the setPlaybackId metric. + std::vector sids; + std::vector playbackIds; + for (const auto&[key, value] : mPlaybackId) { + std::string sid(key.begin(), key.end()); + DrmMetricGroup::Attribute sessionIdAttribute = { + "sid", DrmMetricGroup::ValueType::STRING_TYPE, 0, 0, sid }; + sids.push_back(sessionIdAttribute); + + DrmMetricGroup::Value playbackIdMetricValue = { + "playbackId", DrmMetricGroup::ValueType::STRING_TYPE, 0, 0, value }; + playbackIds.push_back(playbackIdMetricValue); + } + DrmMetricGroup::Metric setPlaybackIdMetric = { + "set_playback_id", { sids }, { playbackIds }}; + + DrmMetricGroup metrics = { + { openSessionMetric, closeSessionMetric, + closeSessionNotOpenedMetric, setPlaybackIdMetric }}; _hidl_cb(Status::OK, hidl_vec({metrics})); return Void(); } @@ -907,7 +966,7 @@ Return DrmPlugin::removeAllSecureStops() { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp index 8513434db55d237a8d19db7cc41df034c0a7835d..eccc8437b6cfebed8e6e30adf13f0985ebba48e5 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp @@ -31,7 +31,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { namespace { @@ -180,7 +180,7 @@ std::string InitDataParser::generateRequest(V1_0::KeyType keyType, } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp index d93777d9d9f1879f806c874235da8dd195f6eb8c..45cc775c6bfd0d62b46cc0b8640463470688a4ca 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp @@ -36,7 +36,7 @@ const std::string kTemporaryLicenseSession("temporary"); namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { JsonWebKey::JsonWebKey() { @@ -65,7 +65,7 @@ bool JsonWebKey::extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, // all the base64 encoded keys. Each key is also stored separately as // a JSON object in mJsonObjects[1..n] where n is the total // number of keys in the set. - if (!isJsonWebKeySet(mJsonObjects[0])) { + if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) { return false; } @@ -271,7 +271,7 @@ bool JsonWebKey::parseJsonWebKeySet(const std::string& jsonWebKeySet, } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp index 32cf2dc502cd0dd3e19c4628e754ff42fcb96e60..e61db3f71857c10e9e618d3a482c12386d7e61e8 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp @@ -11,7 +11,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { std::string MemoryFileSystem::GetFileName(const std::string& path) { @@ -93,7 +93,7 @@ bool MemoryFileSystem::RemoveAllFiles() { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp index a9d701607575234252f6acce5b3148c5a7781920..cf668d41cd0d8716e16ab9d0543ef20796da03f2 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp @@ -28,7 +28,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::KeyValue; @@ -95,7 +95,7 @@ Status_V1_2 Session::decrypt( } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp index 99fb30fd28c99b4da430e6c710ab2e6333f99053..88afcc40fb3449c2de11ec74865f808cc64ca51d 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::hidl_string; @@ -86,7 +86,7 @@ void SessionLibrary::destroySession(const sp& session) { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc new file mode 100644 index 0000000000000000000000000000000000000000..46aba885d611cc6f6c209d508c633a3d14b1b86f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc @@ -0,0 +1,18 @@ +service vendor.drm-clearkey-hal-1-4 /vendor/bin/hw/android.hardware.drm@1.4-service-lazy.clearkey + interface android.hardware.drm@1.0::ICryptoFactory clearkey + interface android.hardware.drm@1.0::IDrmFactory clearkey + interface android.hardware.drm@1.1::ICryptoFactory clearkey + interface android.hardware.drm@1.1::IDrmFactory clearkey + interface android.hardware.drm@1.2::ICryptoFactory clearkey + interface android.hardware.drm@1.2::IDrmFactory clearkey + interface android.hardware.drm@1.3::ICryptoFactory clearkey + interface android.hardware.drm@1.3::IDrmFactory clearkey + interface android.hardware.drm@1.4::ICryptoFactory clearkey + interface android.hardware.drm@1.4::IDrmFactory clearkey + disabled + oneshot + class hal + user media + group media mediadrm + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service.clearkey.rc new file mode 100644 index 0000000000000000000000000000000000000000..8186933165c4e7c4e3e8035674e48312cd3ca91f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service.clearkey.rc @@ -0,0 +1,16 @@ +service vendor.drm-clearkey-hal-1-4 /vendor/bin/hw/android.hardware.drm@1.4-service.clearkey + interface android.hardware.drm@1.0::ICryptoFactory clearkey + interface android.hardware.drm@1.0::IDrmFactory clearkey + interface android.hardware.drm@1.1::ICryptoFactory clearkey + interface android.hardware.drm@1.1::IDrmFactory clearkey + interface android.hardware.drm@1.2::ICryptoFactory clearkey + interface android.hardware.drm@1.2::IDrmFactory clearkey + interface android.hardware.drm@1.3::ICryptoFactory clearkey + interface android.hardware.drm@1.3::IDrmFactory clearkey + interface android.hardware.drm@1.4::ICryptoFactory clearkey + interface android.hardware.drm@1.4::IDrmFactory clearkey + class hal + user media + group media mediadrm + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h index 721f4c015c1b2c6066dcee30c7ae6f3fdc8ab5f6..97794f784ab4be5e7863e3d47d37a5f320f40e88 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h @@ -22,7 +22,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::Status; @@ -42,7 +42,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h index ec30cc10248c1d3b42bed750942e0e69dee3bc12..2349f23d80b1c71c728143d0c80819bd7ff658ba 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h @@ -25,7 +25,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::sp; @@ -38,7 +38,7 @@ void encodeBase64(const void *data, size_t size, std::string *out); void encodeBase64Url(const void *data, size_t size, std::string *out); } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h index c497e379be1fd5ce77714d27ac7ccb86a88604db..66aaa73e956f20d5e6e8dfcde4da2bbd9a2f0436 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h @@ -25,7 +25,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::sp; @@ -54,7 +54,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h index b83ce69d4e05792e938fbf4246a6c5d2b4ff9041..8e47c45c702dad02ba4853b1226bf5f70a516c9a 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h @@ -22,7 +22,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { static const std::string kVendorKey("vendor"); @@ -55,7 +55,7 @@ static const std::string kMetricsKey("metrics"); static const uint8_t kMetricsData[] = { 0 }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h index 03c434eaa32ba45a9b88285a2ea7cc86d3ae8f19..cd18029fc8518d03b518dcbe1afe022226939250 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::KeyValue; @@ -48,7 +48,7 @@ typedef std::map, std::vector > KeyMap; void operator=(const TypeName&) = delete; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h index c1c188ed72d3e2f3545cb77ebafd6d5bff82bc08..d4a8a17df85fd31a4adbe37cdae93bb6b13b827e 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h @@ -17,17 +17,17 @@ #ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ #define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ -#include -#include +#include +#include namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { -using ::android::hardware::drm::V1_3::ICryptoFactory; -using ::android::hardware::drm::V1_3::IDrmFactory; +using ::android::hardware::drm::V1_4::ICryptoFactory; +using ::android::hardware::drm::V1_4::IDrmFactory; extern "C" { IDrmFactory* createDrmFactory(); @@ -35,7 +35,7 @@ extern "C" { } } // namespace clearkey -} // namespace V1_3 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h index cb4811be814d118ff4004cb27f0730bb582e9afd..e6b541f9df8f4560cca6f091a67856b9f1dfe5c1 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h @@ -18,17 +18,17 @@ #define CLEARKEY_CRYPTO_FACTORY_H_ #include -#include +#include #include "ClearKeyTypes.h" namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { -using ::android::hardware::drm::V1_3::ICryptoFactory; +using ::android::hardware::drm::V1_4::ICryptoFactory; using ::android::hardware::drm::V1_0::ICryptoPlugin; using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; @@ -52,7 +52,7 @@ private: }; } // namespace clearkey -} // namespace V1_3 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h index 23a64fac50b9c7b7a121cadc4610a0b5008cd906..b272a83e543176e8330f58b6c472327705809ae5 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h @@ -17,7 +17,7 @@ #ifndef CLEARKEY_CRYPTO_PLUGIN_H_ #define CLEARKEY_CRYPTO_PLUGIN_H_ -#include +#include #include #include @@ -34,7 +34,7 @@ namespace { namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { namespace drm = ::android::hardware::drm; @@ -56,7 +56,7 @@ using ::android::sp; typedef drm::V1_2::Status Status_V1_2; -struct CryptoPlugin : public drm::V1_2::ICryptoPlugin { +struct CryptoPlugin : public drm::V1_4::ICryptoPlugin { explicit CryptoPlugin(const hidl_vec& sessionId) { mInitStatus = setMediaDrmSession(sessionId); } @@ -104,6 +104,8 @@ struct CryptoPlugin : public drm::V1_2::ICryptoPlugin { Return getInitStatus() const { return mInitStatus; } + Return getLogMessages( + getLogMessages_cb _hidl_cb); private: CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin); @@ -114,7 +116,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h index 554ae598fe13178970486b6f1d9c72d2b4051d87..6466ac3a3082115f18617193f93385bc6133e357 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h @@ -20,9 +20,11 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { +using ::android::hardware::drm::V1_2::clearkey::OfflineFile; + class DeviceFiles { public: typedef enum { @@ -63,7 +65,7 @@ class DeviceFiles { }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h index 63234cffa9ee2634f4746adf3129c3cfde247d50..fea1ec8f71a1cbb956a3003531ac10d4fee80e2a 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h @@ -17,15 +17,15 @@ #ifndef CLEARKEY_DRM_FACTORY_H_ #define CLEARKEY_DRM_FACTORY_H_ -#include -#include +#include +#include #include "ClearKeyTypes.h" namespace android { namespace hardware { namespace drm { -namespace V1_3 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_1::SecurityLevel; @@ -63,7 +63,7 @@ private: }; } // namespace clearkey -} // namespace V1_3 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h index 894985bd1b3d6a51523635facbbe9c96be0504ea..5d6e3daca178a6114cde26e00b9961b9fa702c6f 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -17,7 +17,7 @@ #ifndef CLEARKEY_DRM_PLUGIN_H_ #define CLEARKEY_DRM_PLUGIN_H_ -#include +#include #include #include @@ -32,7 +32,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { namespace drm = ::android::hardware::drm; @@ -50,9 +50,12 @@ using drm::V1_1::DrmMetricGroup; using drm::V1_1::HdcpLevel; using drm::V1_1::SecureStopRelease; using drm::V1_1::SecurityLevel; -using drm::V1_2::IDrmPlugin; using drm::V1_2::KeySetId; using drm::V1_2::OfflineLicenseState; +using drm::V1_4::clearkey::DeviceFiles; +using drm::V1_4::clearkey::Session; +using drm::V1_4::clearkey::SessionLibrary; +using drm::V1_4::IDrmPlugin; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; @@ -199,6 +202,18 @@ struct DrmPlugin : public IDrmPlugin { Return setPropertyByteArray( const hidl_string& name, const hidl_vec& value) override; + Return getLogMessages( + getLogMessages_cb _hidl_cb) override; + + Return setPlaybackId( + const hidl_vec& sessionId, + const hidl_string& playbackId) override; + + Return requiresSecureDecoder( + const hidl_string& mime, SecurityLevel level) override; + + Return requiresSecureDecoderDefault(const hidl_string& mime) override; + Return setCipherAlgorithm( const hidl_vec& sessionId, const hidl_string& algorithm) { if (sessionId.size() == 0 || algorithm.size() == 0) { @@ -398,6 +413,7 @@ private: std::map mStringProperties; std::map > mByteArrayProperties; std::map > mReleaseKeysMap; + std::map, std::string> mPlaybackId; std::map, SecurityLevel> mSecurityLevel; sp mListener; sp mListenerV1_2; @@ -424,7 +440,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h index 889f51164b3de2eb544d30ccf69fe0a70b9ffee5..59338c9f7f659c16d43ae1a1d1346edecc916ca0 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::drm::V1_0::Status; @@ -49,7 +49,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h index e57470cd0d38d4ee68b540ef7eb0033b42355677..40a2d749c87341fd4479a6f031dfa8284865bf99 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h @@ -23,7 +23,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { class JsonWebKey { @@ -54,7 +54,7 @@ class JsonWebKey { }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h index 6ac0e2c4bb993cde003e62dcc6916a9e3ec93b4e..a90d818b0f7f51d2ace642ee2552bcdeaff3b150 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h @@ -15,7 +15,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { // Using android file system requires clearkey plugin to update @@ -64,7 +64,7 @@ class MemoryFileSystem { }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h index a159e5ae76f183076833c2da35f01b69214d7481..05cb8c8c7ef1ec6e2e6c9e047d937b41179faf6f 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h @@ -27,7 +27,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { namespace drm = ::android::hardware::drm; @@ -73,7 +73,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h index 1e567b8d09ebe62b00fe7e2b9e208f04f75286c6..5e774388c98f9daea125baf5e9bd948defb52a74 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h @@ -26,7 +26,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::sp; @@ -58,7 +58,7 @@ private: }; } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h index b0f8607729bde3e261924f6dc08c716f1bfb648f..22eeccde05f4dd7b89f4740e5be6ff911a70caeb 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT -#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT +#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT +#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT #include @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace drm { -namespace V1_2 { +namespace V1_4 { namespace clearkey { using ::android::hardware::hidl_array; @@ -80,9 +80,9 @@ inline Status toStatus_1_0(Status_V1_2 status) { } } // namespace clearkey -} // namespace V1_2 +} // namespace V1_4 } // namespace drm } // namespace hardware } // namespace android -#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT +#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT diff --git a/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml new file mode 100644 index 0000000000000000000000000000000000000000..31ddb5f1980ee8c9973c5b1890bd11aac80a8e4a --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml @@ -0,0 +1,23 @@ + + + + + android.hardware.drm + hwbinder + @1.4::ICryptoFactory/clearkey + @1.4::IDrmFactory/clearkey + + diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp index b62baae662b3b0199dbad0e1a0f9723e22630771..d3d69057379452fb6b6462441b7f91c4a5b0f26a 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp @@ -25,10 +25,10 @@ using ::android::hardware::configureRpcThreadpool; using ::android::hardware::joinRpcThreadpool; using ::android::sp; -using android::hardware::drm::V1_3::ICryptoFactory; -using android::hardware::drm::V1_3::IDrmFactory; -using android::hardware::drm::V1_3::clearkey::CryptoFactory; -using android::hardware::drm::V1_3::clearkey::DrmFactory; +using android::hardware::drm::V1_4::ICryptoFactory; +using android::hardware::drm::V1_4::IDrmFactory; +using android::hardware::drm::V1_4::clearkey::CryptoFactory; +using android::hardware::drm::V1_4::clearkey::DrmFactory; int main(int /* argc */, char** /* argv */) { sp drmFactory = new DrmFactory; diff --git a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp index b3e0179cf6405d43063f676fbe1b17fe9847afe9..358b5ccdb853add1ac937604834f841ae658be6c 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp @@ -25,10 +25,10 @@ using ::android::hardware::configureRpcThreadpool; using ::android::hardware::joinRpcThreadpool; using ::android::sp; -using android::hardware::drm::V1_3::ICryptoFactory; -using android::hardware::drm::V1_3::IDrmFactory; -using android::hardware::drm::V1_3::clearkey::CryptoFactory; -using android::hardware::drm::V1_3::clearkey::DrmFactory; +using android::hardware::drm::V1_4::ICryptoFactory; +using android::hardware::drm::V1_4::IDrmFactory; +using android::hardware::drm::V1_4::clearkey::CryptoFactory; +using android::hardware::drm::V1_4::clearkey::DrmFactory; using android::hardware::LazyServiceRegistrar; int main(int /* argc */, char** /* argv */) { diff --git a/include/drm/TEST_MAPPING b/include/drm/TEST_MAPPING index 28e432e121bccd3f65ea27b5261ff71a4871b47e..74fa50d9c586a87ab38f18230fcc7262c7dd4818 100644 --- a/include/drm/TEST_MAPPING +++ b/include/drm/TEST_MAPPING @@ -8,17 +8,9 @@ }, { "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests" - } - ] - }, - { - "name": "GtsExoPlayerTestCases", - "options" : [ - { - "include-annotation": "android.platform.test.annotations.SocPresubmit" }, { - "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed" + "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests" } ] } diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h index 703cf771177374803d80afa8cc5a8cdd52cbdfac..71e760475f21d8e9b60cf8d9e854e4bcfdcd62cd 100644 --- a/include/media/Interpolator.h +++ b/include/media/Interpolator.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -39,17 +40,10 @@ template class Interpolator : public std::map { public: // Polynomial spline interpolators - // Extend only at the end of enum, as this must match order in VolumeShapers.java. - enum InterpolatorType : int32_t { - INTERPOLATOR_TYPE_STEP, // Not continuous - INTERPOLATOR_TYPE_LINEAR, // C0 - INTERPOLATOR_TYPE_CUBIC, // C1 - INTERPOLATOR_TYPE_CUBIC_MONOTONIC, // C1 (to provide locally monotonic curves) - // INTERPOLATOR_TYPE_CUBIC_C2, // TODO - requires global computation / cache - }; + using InterpolatorType = media::InterpolatorType; explicit Interpolator( - InterpolatorType interpolatorType = INTERPOLATOR_TYPE_LINEAR, + InterpolatorType interpolatorType = InterpolatorType::CUBIC, bool cache = true) : mCache(cache) , mFirstSlope(0) @@ -82,13 +76,13 @@ public: // now that we have two adjacent points: switch (mInterpolatorType) { - case INTERPOLATOR_TYPE_STEP: + case InterpolatorType::STEP: return high->first == x ? high->second : low->second; - case INTERPOLATOR_TYPE_LINEAR: + case InterpolatorType::LINEAR: return ((high->first - x) * low->second + (x - low->first) * high->second) / (high->first - low->first); - case INTERPOLATOR_TYPE_CUBIC: - case INTERPOLATOR_TYPE_CUBIC_MONOTONIC: + case InterpolatorType::CUBIC: + case InterpolatorType::CUBIC_MONOTONIC: default: { // See https://en.wikipedia.org/wiki/Cubic_Hermite_spline @@ -116,7 +110,7 @@ public: // non catmullRom (finite difference) with regular cubic; // the choices here minimize computation. bool monotonic, catmullRom; - if (mInterpolatorType == INTERPOLATOR_TYPE_CUBIC_MONOTONIC) { + if (mInterpolatorType == InterpolatorType::CUBIC_MONOTONIC) { monotonic = true; catmullRom = false; } else { @@ -202,11 +196,11 @@ public: status_t setInterpolatorType(InterpolatorType interpolatorType) { switch (interpolatorType) { - case INTERPOLATOR_TYPE_STEP: // Not continuous - case INTERPOLATOR_TYPE_LINEAR: // C0 - case INTERPOLATOR_TYPE_CUBIC: // C1 - case INTERPOLATOR_TYPE_CUBIC_MONOTONIC: // C1 + other constraints - // case INTERPOLATOR_TYPE_CUBIC_C2: + case InterpolatorType::STEP: // Not continuous + case InterpolatorType::LINEAR: // C0 + case InterpolatorType::CUBIC: // C1 + case InterpolatorType::CUBIC_MONOTONIC: // C1 + other constraints + // case InterpolatorType::CUBIC_C2: mInterpolatorType = interpolatorType; return NO_ERROR; default: @@ -235,49 +229,50 @@ public: mMemo.clear(); } + // TODO(ytai): remove this method once it is not used. status_t writeToParcel(Parcel *parcel) const { - if (parcel == nullptr) { - return BAD_VALUE; - } - status_t res = parcel->writeInt32(mInterpolatorType) - ?: parcel->writeFloat(mFirstSlope) - ?: parcel->writeFloat(mLastSlope) - ?: parcel->writeUint32((uint32_t)this->size()); // silent truncation - if (res != NO_ERROR) { - return res; - } + media::InterpolatorConfig config; + writeToConfig(&config); + return config.writeToParcel(parcel); + } + + void writeToConfig(media::InterpolatorConfig *config) const { + config->type = mInterpolatorType; + config->firstSlope = mFirstSlope; + config->lastSlope = mLastSlope; for (const auto &pt : *this) { - res = parcel->writeFloat(pt.first) - ?: parcel->writeFloat(pt.second); - if (res != NO_ERROR) { - return res; - } + config->xy.push_back(pt.first); + config->xy.push_back(pt.second); } - return NO_ERROR; } + // TODO(ytai): remove this method once it is not used. status_t readFromParcel(const Parcel &parcel) { - this->clear(); - int32_t type; - uint32_t size; - status_t res = parcel.readInt32(&type) - ?: parcel.readFloat(&mFirstSlope) - ?: parcel.readFloat(&mLastSlope) - ?: parcel.readUint32(&size) - ?: setInterpolatorType((InterpolatorType)type); + media::InterpolatorConfig config; + status_t res = config.readFromParcel(&parcel); if (res != NO_ERROR) { return res; } + return readFromConfig(config); + } + + status_t readFromConfig(const media::InterpolatorConfig &config) { + this->clear(); + setInterpolatorType(config.type); + if ((config.xy.size() & 1) != 0) { + // xy size must be even. + return BAD_VALUE; + } + uint32_t size = config.xy.size() / 2; + mFirstSlope = config.firstSlope; + mLastSlope = config.lastSlope; + // Note: We don't need to check size is within some bounds as // the Parcel read will fail if size is incorrectly specified too large. float lastx; for (uint32_t i = 0; i < size; ++i) { - float x, y; - res = parcel.readFloat(&x) - ?: parcel.readFloat(&y); - if (res != NO_ERROR) { - return res; - } + float x = config.xy[i * 2]; + float y = config.xy[i * 2 + 1]; if ((i > 0 && !(x > lastx)) /* handle nan */ || y != y /* handle nan */) { // This is a std::map object which imposes sorted order diff --git a/include/media/MicrophoneInfo.h b/include/media/MicrophoneInfo.h index 0a24b0214be8f14858a9995dec83af8c284a50d6..a5045b96d24b5a9f8a1b293254992af8219d6bbd 100644 --- a/include/media/MicrophoneInfo.h +++ b/include/media/MicrophoneInfo.h @@ -17,33 +17,24 @@ #ifndef ANDROID_MICROPHONE_INFO_H #define ANDROID_MICROPHONE_INFO_H +#include #include #include +#include #include -#include -#include namespace android { namespace media { -#define RETURN_IF_FAILED(calledOnce) \ - { \ - status_t returnStatus = calledOnce; \ - if (returnStatus) { \ - ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ - return returnStatus; \ - } \ - } - class MicrophoneInfo : public Parcelable { public: MicrophoneInfo() = default; MicrophoneInfo(const MicrophoneInfo& microphoneInfo) = default; MicrophoneInfo(audio_microphone_characteristic_t& characteristic) { - mDeviceId = String16(&characteristic.device_id[0]); + mDeviceId = std::string(&characteristic.device_id[0]); mPortId = characteristic.id; mType = characteristic.device; - mAddress = String16(&characteristic.address[0]); + mAddress = std::string(&characteristic.address[0]); mDeviceLocation = characteristic.location; mDeviceGroup = characteristic.group; mIndexInTheGroup = characteristic.index_in_the_group; @@ -53,8 +44,8 @@ public: mOrientation.push_back(characteristic.orientation.x); mOrientation.push_back(characteristic.orientation.y); mOrientation.push_back(characteristic.orientation.z); - Vector frequencies; - Vector responses; + std::vector frequencies; + std::vector responses; for (size_t i = 0; i < characteristic.num_frequency_responses; i++) { frequencies.push_back(characteristic.frequency_responses[0][i]); responses.push_back(characteristic.frequency_responses[1][i]); @@ -73,76 +64,73 @@ public: virtual ~MicrophoneInfo() = default; virtual status_t writeToParcel(Parcel* parcel) const { - RETURN_IF_FAILED(parcel->writeString16(mDeviceId)); - RETURN_IF_FAILED(parcel->writeInt32(mPortId)); - RETURN_IF_FAILED(parcel->writeUint32(mType)); - RETURN_IF_FAILED(parcel->writeString16(mAddress)); - RETURN_IF_FAILED(parcel->writeInt32(mDeviceLocation)); - RETURN_IF_FAILED(parcel->writeInt32(mDeviceGroup)); - RETURN_IF_FAILED(parcel->writeInt32(mIndexInTheGroup)); - RETURN_IF_FAILED(writeFloatVector(parcel, mGeometricLocation)); - RETURN_IF_FAILED(writeFloatVector(parcel, mOrientation)); + MicrophoneInfoData parcelable; + return writeToParcelable(&parcelable) + ?: parcelable.writeToParcel(parcel); + } + + virtual status_t writeToParcelable(MicrophoneInfoData* parcelable) const { + parcelable->deviceId = mDeviceId; + parcelable->portId = mPortId; + parcelable->type = VALUE_OR_RETURN_STATUS(convertReinterpret(mType)); + parcelable->address = mAddress; + parcelable->deviceGroup = mDeviceGroup; + parcelable->indexInTheGroup = mIndexInTheGroup; + parcelable->geometricLocation = mGeometricLocation; + parcelable->orientation = mOrientation; if (mFrequencyResponses.size() != 2) { return BAD_VALUE; } - for (size_t i = 0; i < mFrequencyResponses.size(); i++) { - RETURN_IF_FAILED(parcel->writeInt32(mFrequencyResponses[i].size())); - RETURN_IF_FAILED(writeFloatVector(parcel, mFrequencyResponses[i])); - } - std::vector channelMapping; - for (size_t i = 0; i < mChannelMapping.size(); ++i) { - channelMapping.push_back(mChannelMapping[i]); - } - RETURN_IF_FAILED(parcel->writeInt32Vector(channelMapping)); - RETURN_IF_FAILED(parcel->writeFloat(mSensitivity)); - RETURN_IF_FAILED(parcel->writeFloat(mMaxSpl)); - RETURN_IF_FAILED(parcel->writeFloat(mMinSpl)); - RETURN_IF_FAILED(parcel->writeInt32(mDirectionality)); + parcelable->frequencies = mFrequencyResponses[0]; + parcelable->frequencyResponses = mFrequencyResponses[1]; + parcelable->channelMapping = mChannelMapping; + parcelable->sensitivity = mSensitivity; + parcelable->maxSpl = mMaxSpl; + parcelable->minSpl = mMinSpl; + parcelable->directionality = mDirectionality; return OK; } virtual status_t readFromParcel(const Parcel* parcel) { - RETURN_IF_FAILED(parcel->readString16(&mDeviceId)); - RETURN_IF_FAILED(parcel->readInt32(&mPortId)); - RETURN_IF_FAILED(parcel->readUint32(&mType)); - RETURN_IF_FAILED(parcel->readString16(&mAddress)); - RETURN_IF_FAILED(parcel->readInt32(&mDeviceLocation)); - RETURN_IF_FAILED(parcel->readInt32(&mDeviceGroup)); - RETURN_IF_FAILED(parcel->readInt32(&mIndexInTheGroup)); - RETURN_IF_FAILED(readFloatVector(parcel, &mGeometricLocation, 3)); - RETURN_IF_FAILED(readFloatVector(parcel, &mOrientation, 3)); - int32_t frequenciesNum; - RETURN_IF_FAILED(parcel->readInt32(&frequenciesNum)); - Vector frequencies; - RETURN_IF_FAILED(readFloatVector(parcel, &frequencies, frequenciesNum)); - int32_t responsesNum; - RETURN_IF_FAILED(parcel->readInt32(&responsesNum)); - Vector responses; - RETURN_IF_FAILED(readFloatVector(parcel, &responses, responsesNum)); - if (frequencies.size() != responses.size()) { + MicrophoneInfoData data; + return data.readFromParcel(parcel) + ?: readFromParcelable(data); + } + + virtual status_t readFromParcelable(const MicrophoneInfoData& parcelable) { + mDeviceId = parcelable.deviceId; + mPortId = parcelable.portId; + mType = VALUE_OR_RETURN_STATUS(convertReinterpret(parcelable.type)); + mAddress = parcelable.address; + mDeviceLocation = parcelable.deviceLocation; + mDeviceGroup = parcelable.deviceGroup; + mIndexInTheGroup = parcelable.indexInTheGroup; + if (parcelable.geometricLocation.size() != 3) { return BAD_VALUE; } - mFrequencyResponses.push_back(frequencies); - mFrequencyResponses.push_back(responses); - std::vector channelMapping; - status_t result = parcel->readInt32Vector(&channelMapping); - if (result != OK) { - return result; + mGeometricLocation = parcelable.geometricLocation; + if (parcelable.orientation.size() != 3) { + return BAD_VALUE; } - if (channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) { + mOrientation = parcelable.orientation; + if (parcelable.frequencies.size() != parcelable.frequencyResponses.size()) { return BAD_VALUE; } - for (size_t i = 0; i < channelMapping.size(); i++) { - mChannelMapping.push_back(channelMapping[i]); + + mFrequencyResponses.push_back(parcelable.frequencies); + mFrequencyResponses.push_back(parcelable.frequencyResponses); + if (parcelable.channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) { + return BAD_VALUE; } - RETURN_IF_FAILED(parcel->readFloat(&mSensitivity)); - RETURN_IF_FAILED(parcel->readFloat(&mMaxSpl)); - RETURN_IF_FAILED(parcel->readFloat(&mMinSpl)); - RETURN_IF_FAILED(parcel->readInt32(&mDirectionality)); + mChannelMapping = parcelable.channelMapping; + mSensitivity = parcelable.sensitivity; + mMaxSpl = parcelable.maxSpl; + mMinSpl = parcelable.minSpl; + mDirectionality = parcelable.directionality; return OK; } - String16 getDeviceId() const { + std::string getDeviceId() const { return mDeviceId; } @@ -154,7 +142,7 @@ public: return mType; } - String16 getAddress() const { + std::string getAddress() const { return mAddress; } @@ -170,19 +158,19 @@ public: return mIndexInTheGroup; } - const Vector& getGeometricLocation() const { + const std::vector& getGeometricLocation() const { return mGeometricLocation; } - const Vector& getOrientation() const { + const std::vector& getOrientation() const { return mOrientation; } - const Vector>& getFrequencyResponses() const { + const std::vector>& getFrequencyResponses() const { return mFrequencyResponses; } - const Vector& getChannelMapping() const { + const std::vector& getChannelMapping() const { return mChannelMapping; } @@ -203,46 +191,38 @@ public: } private: - status_t readFloatVector( - const Parcel* parcel, Vector *vectorPtr, size_t defaultLength) { - std::optional> v; - status_t result = parcel->readFloatVector(&v); - if (result != OK) return result; - vectorPtr->clear(); - if (v) { - for (const auto& iter : *v) { - vectorPtr->push_back(iter); - } - } else { - vectorPtr->resize(defaultLength); - } - return OK; - } - status_t writeFloatVector(Parcel* parcel, const Vector& vector) const { - std::vector v; - for (size_t i = 0; i < vector.size(); i++) { - v.push_back(vector[i]); - } - return parcel->writeFloatVector(v); - } - - String16 mDeviceId; + std::string mDeviceId; int32_t mPortId; uint32_t mType; - String16 mAddress; + std::string mAddress; int32_t mDeviceLocation; int32_t mDeviceGroup; int32_t mIndexInTheGroup; - Vector mGeometricLocation; - Vector mOrientation; - Vector> mFrequencyResponses; - Vector mChannelMapping; + std::vector mGeometricLocation; + std::vector mOrientation; + std::vector> mFrequencyResponses; + std::vector mChannelMapping; float mSensitivity; float mMaxSpl; float mMinSpl; int32_t mDirectionality; }; +// Conversion routines, according to AidlConversion.h conventions. +inline ConversionResult +aidl2legacy_MicrophoneInfo(const media::MicrophoneInfoData& aidl) { + MicrophoneInfo legacy; + RETURN_IF_ERROR(legacy.readFromParcelable(aidl)); + return legacy; +} + +inline ConversionResult +legacy2aidl_MicrophoneInfo(const MicrophoneInfo& legacy) { + media::MicrophoneInfoData aidl; + RETURN_IF_ERROR(legacy.writeToParcelable(&aidl)); + return aidl; +} + } // namespace media } // namespace android diff --git a/include/media/MmapStreamInterface.h b/include/media/MmapStreamInterface.h index b3bf16d04baa5d361dcb339e768aaa73e4e32be5..61de98709ed5eee628e70f502531c464a85dcd3c 100644 --- a/include/media/MmapStreamInterface.h +++ b/include/media/MmapStreamInterface.h @@ -22,6 +22,8 @@ #include #include +#include + namespace android { class MmapStreamCallback; @@ -102,6 +104,19 @@ class MmapStreamInterface : public virtual RefBase */ virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0; + /** + * Get a recent count of the number of audio frames presented/received to/from an + * external observer. + * + * \param[out] position count of presented audio frames + * \param[out] timeNanos associated clock time + * + * \return OK if the external position is set correctly. + * NO_INIT in case of initialization error + * INVALID_OPERATION if the interface is not implemented + */ + virtual status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) = 0; + /** * Start a stream operating in mmap mode. * createMmapBuffer() must be called before calling start() diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h index fe519bb56321bfa9b1e27c7fb1c1317370be4921..5271e100a1de48ec7c09ac0bf263617a910a00fa 100644 --- a/include/media/VolumeShaper.h +++ b/include/media/VolumeShaper.h @@ -22,6 +22,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -284,30 +289,41 @@ public: clampVolume(); } - // The parcel layout must match VolumeShaper.java status_t writeToParcel(Parcel *parcel) const override { - if (parcel == nullptr) return BAD_VALUE; - return parcel->writeInt32((int32_t)mType) - ?: parcel->writeInt32(mId) - ?: mType == TYPE_ID - ? NO_ERROR - : parcel->writeInt32((int32_t)mOptionFlags) - ?: parcel->writeDouble(mDurationMs) - ?: Interpolator::writeToParcel(parcel); - } - - status_t readFromParcel(const Parcel *parcel) override { - int32_t type, optionFlags; - return parcel->readInt32(&type) - ?: setType((Type)type) - ?: parcel->readInt32(&mId) - ?: mType == TYPE_ID - ? NO_ERROR - : parcel->readInt32(&optionFlags) - ?: setOptionFlags((OptionFlag)optionFlags) - ?: parcel->readDouble(&mDurationMs) - ?: Interpolator::readFromParcel(*parcel) - ?: checkCurve(); + VolumeShaperConfiguration parcelable; + writeToParcelable(&parcelable); + return parcelable.writeToParcel(parcel); + } + + void writeToParcelable(VolumeShaperConfiguration *parcelable) const { + parcelable->id = getId(); + parcelable->type = getTypeAsAidl(); + parcelable->optionFlags = 0; + if (mType != TYPE_ID) { + parcelable->optionFlags = getOptionFlagsAsAidl(); + parcelable->durationMs = getDurationMs(); + parcelable->interpolatorConfig.emplace(); // create value in std::optional + Interpolator::writeToConfig(&*parcelable->interpolatorConfig); + } + } + + status_t readFromParcel(const Parcel* parcel) override { + VolumeShaperConfiguration data; + return data.readFromParcel(parcel) + ?: readFromParcelable(data); + } + + status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) { + setId(parcelable.id); + return setTypeFromAidl(parcelable.type) + ?: mType == TYPE_ID + ? NO_ERROR + : setOptionFlagsFromAidl(parcelable.optionFlags) + ?: setDurationMs(parcelable.durationMs) + ?: !parcelable.interpolatorConfig // check std::optional for value + ? BAD_VALUE // must be nonnull. + : Interpolator::readFromConfig(*parcelable.interpolatorConfig) + ?: checkCurve(); } // Returns a string for debug printing. @@ -329,6 +345,51 @@ public: int32_t mId; // A valid id is >= 0. OptionFlag mOptionFlags; // option flags for the configuration. double mDurationMs; // duration, must be > 0; default is 1000 ms. + + int32_t getOptionFlagsAsAidl() const { + int32_t result = 0; + if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) { + result |= + 1 << static_cast(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS); + } + if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) { + result |= 1 << static_cast(VolumeShaperConfigurationOptionFlag::CLOCK_TIME); + } + return result; + } + + status_t setOptionFlagsFromAidl(int32_t aidl) { + std::underlying_type_t options = 0; + if (aidl & (1 << static_cast(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) { + options |= OPTION_FLAG_VOLUME_IN_DBFS; + } + if (aidl & (1 << static_cast(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) { + options |= OPTION_FLAG_CLOCK_TIME; + } + return setOptionFlags(static_cast(options)); + } + + status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) { + switch (aidl) { + case VolumeShaperConfigurationType::ID: + return setType(TYPE_ID); + case VolumeShaperConfigurationType::SCALE: + return setType(TYPE_SCALE); + default: + return BAD_VALUE; + } + } + + VolumeShaperConfigurationType getTypeAsAidl() const { + switch (getType()) { + case TYPE_ID: + return VolumeShaperConfigurationType::ID; + case TYPE_SCALE: + return VolumeShaperConfigurationType::SCALE; + default: + LOG_ALWAYS_FATAL("Shouldn't get here"); + } + } }; // Configuration /* VolumeShaper::Operation expresses an operation to perform on the @@ -420,19 +481,29 @@ public: return NO_ERROR; } - status_t writeToParcel(Parcel *parcel) const override { + status_t writeToParcel(Parcel* parcel) const override { if (parcel == nullptr) return BAD_VALUE; - return parcel->writeInt32((int32_t)mFlags) - ?: parcel->writeInt32(mReplaceId) - ?: parcel->writeFloat(mXOffset); + VolumeShaperOperation op; + writeToParcelable(&op); + return op.writeToParcel(parcel); } - status_t readFromParcel(const Parcel *parcel) override { - int32_t flags; - return parcel->readInt32(&flags) - ?: parcel->readInt32(&mReplaceId) - ?: parcel->readFloat(&mXOffset) - ?: setFlags((Flag)flags); + void writeToParcelable(VolumeShaperOperation* op) const { + op->flags = getFlagsAsAidl(); + op->replaceId = mReplaceId; + op->xOffset = mXOffset; + } + + status_t readFromParcel(const Parcel* parcel) override { + VolumeShaperOperation op; + return op.readFromParcel(parcel) + ?: readFromParcelable(op); + } + + status_t readFromParcelable(const VolumeShaperOperation& op) { + mReplaceId = op.replaceId; + mXOffset = op.xOffset; + return setFlagsFromAidl(op.flags); } std::string toString() const { @@ -444,6 +515,48 @@ public: return ss.str(); } + private: + status_t setFlagsFromAidl(int32_t aidl) { + std::underlying_type_t flags = 0; + if (aidl & (1 << static_cast(VolumeShaperOperationFlag::REVERSE))) { + flags |= FLAG_REVERSE; + } + if (aidl & (1 << static_cast(VolumeShaperOperationFlag::TERMINATE))) { + flags |= FLAG_TERMINATE; + } + if (aidl & (1 << static_cast(VolumeShaperOperationFlag::JOIN))) { + flags |= FLAG_JOIN; + } + if (aidl & (1 << static_cast(VolumeShaperOperationFlag::DELAY))) { + flags |= FLAG_DELAY; + } + if (aidl & (1 << static_cast(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) { + flags |= FLAG_CREATE_IF_NECESSARY; + } + return setFlags(static_cast(flags)); + } + + int32_t getFlagsAsAidl() const { + int32_t aidl = 0; + std::underlying_type_t flags = getFlags(); + if (flags & FLAG_REVERSE) { + aidl |= (1 << static_cast(VolumeShaperOperationFlag::REVERSE)); + } + if (flags & FLAG_TERMINATE) { + aidl |= (1 << static_cast(VolumeShaperOperationFlag::TERMINATE)); + } + if (flags & FLAG_JOIN) { + aidl |= (1 << static_cast(VolumeShaperOperationFlag::JOIN)); + } + if (flags & FLAG_DELAY) { + aidl |= (1 << static_cast(VolumeShaperOperationFlag::DELAY)); + } + if (flags & FLAG_CREATE_IF_NECESSARY) { + aidl |= (1 << static_cast(VolumeShaperOperationFlag::CREATE_IF_NECESSARY)); + } + return aidl; + } + private: Flag mFlags; // operation to do int32_t mReplaceId; // if >= 0 the id to remove in a replace operation. @@ -483,15 +596,28 @@ public: mXOffset = xOffset; } - status_t writeToParcel(Parcel *parcel) const override { + status_t writeToParcel(Parcel* parcel) const override { if (parcel == nullptr) return BAD_VALUE; - return parcel->writeFloat(mVolume) - ?: parcel->writeFloat(mXOffset); + VolumeShaperState state; + writeToParcelable(&state); + return state.writeToParcel(parcel); + } + + void writeToParcelable(VolumeShaperState* parcelable) const { + parcelable->volume = mVolume; + parcelable->xOffset = mXOffset; + } + + status_t readFromParcel(const Parcel* parcel) override { + VolumeShaperState state; + return state.readFromParcel(parcel) + ?: readFromParcelable(state); } - status_t readFromParcel(const Parcel *parcel) override { - return parcel->readFloat(&mVolume) - ?: parcel->readFloat(&mXOffset); + status_t readFromParcelable(const VolumeShaperState& parcelable) { + mVolume = parcelable.volume; + mXOffset = parcelable.xOffset; + return OK; } std::string toString() const { diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING index a6dfb2145ea85e5e00c5c9c540d2da1e78ab71d8..5bc726275bc5ec4fe30b79701a77f807f0d00541 100644 --- a/media/TEST_MAPPING +++ b/media/TEST_MAPPING @@ -1,32 +1,65 @@ +// for frameworks/av/media { - "presubmit": [ - { - "name": "GtsMediaTestCases", - "options" : [ + "presubmit-large": [ + // runs whenever we change something in this tree { - "include-annotation": "android.platform.test.annotations.Presubmit" + "name": "CtsMediaTestCases", + "options": [ + { + "include-filter": "android.media.cts.EncodeDecodeTest" + } + ] }, { - "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests" + "name": "CtsMediaTestCases", + "options": [ + { + "include-filter": "android.media.cts.DecodeEditEncodeTest" + } + ] } - ] - }, - { - "name": "GtsExoPlayerTestCases", - "options" : [ + ], + "presubmit": [ { - "include-annotation": "android.platform.test.annotations.SocPresubmit" + "name": "GtsMediaTestCases", + "options" : [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests" + }, + { + "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests" + } + ] + } + ], + + "imports": [ + { + "path": "frameworks/av/drm/mediadrm/plugins" + } + ], + + "platinum-postsubmit": [ + // runs regularly, independent of changes in this tree. + // signals if changes elsewhere break media functionality + { + "name": "CtsMediaTestCases", + "options": [ + { + "include-filter": "android.media.cts.EncodeDecodeTest" + } + ] }, { - "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed" + "name": "CtsMediaTestCases", + "options": [ + { + "include-filter": "android.media.cts.DecodeEditEncodeTest" + } + ] } - ] - } - ], - "imports": [ - { - "path": "frameworks/av/drm/mediadrm/plugins" - } - ] + ] } - diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..be25ffbe38155abf8c2051cfb7ad127f1cfd6ad2 --- /dev/null +++ b/media/audioserver/Android.bp @@ -0,0 +1,67 @@ +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_binary { + name: "audioserver", + + srcs: [ + "main_audioserver.cpp", + ], + + cflags: [ + "-Wall", + "-Werror", + ], + + header_libs: [ + "libaudiohal_headers", + "libmediametrics_headers", + ], + + shared_libs: [ + "libaaudioservice", + "libaudioflinger", + "libaudiopolicyservice", + "libaudioprocessing", + "libbinder", + "libcutils", + "libhidlbase", + "liblog", + "libmedia", + "libmedialogservice", + "libmediautils", + "libnbaio", + "libnblog", + "libpowermanager", + "libutils", + "libvibrator", + + ], + + // TODO check if we still need all of these include directories + include_dirs: [ + "external/sonic", + "frameworks/av/media/libaaudio/include", + "frameworks/av/media/libaaudio/src", + "frameworks/av/media/libaaudio/src/binding", + "frameworks/av/media/libmedia/include", + "frameworks/av/services/audioflinger", + "frameworks/av/services/audiopolicy", + "frameworks/av/services/audiopolicy/common/include", + "frameworks/av/services/audiopolicy/common/managerdefinitions/include", + "frameworks/av/services/audiopolicy/engine/interface", + "frameworks/av/services/audiopolicy/service", + "frameworks/av/services/medialog", + + // TODO oboeservice is the old folder name for aaudioservice. It will be changed. + "frameworks/av/services/oboeservice", + ], + + init_rc: ["audioserver.rc"], +} diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk deleted file mode 100644 index acc1b82e8ec319d0ea2ed69df0fffb6f6ed65f57..0000000000000000000000000000000000000000 --- a/media/audioserver/Android.mk +++ /dev/null @@ -1,54 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - main_audioserver.cpp \ - -LOCAL_SHARED_LIBRARIES := \ - libaaudioservice \ - libaudioflinger \ - libaudiopolicyservice \ - libaudioprocessing \ - libbinder \ - libcutils \ - liblog \ - libhidlbase \ - libmedia \ - libmedialogservice \ - libmediautils \ - libnbaio \ - libnblog \ - libutils \ - libvibrator - -LOCAL_HEADER_LIBRARIES := \ - libaudiohal_headers \ - libmediametrics_headers \ - -# TODO oboeservice is the old folder name for aaudioservice. It will be changed. -LOCAL_C_INCLUDES := \ - frameworks/av/services/audioflinger \ - frameworks/av/services/audiopolicy \ - frameworks/av/services/audiopolicy/common/managerdefinitions/include \ - frameworks/av/services/audiopolicy/common/include \ - frameworks/av/services/audiopolicy/engine/interface \ - frameworks/av/services/audiopolicy/service \ - frameworks/av/services/medialog \ - frameworks/av/services/oboeservice \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/src \ - frameworks/av/media/libaaudio/src/binding \ - frameworks/av/media/libmedia/include \ - external/sonic \ - -LOCAL_MODULE := audioserver -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE - -LOCAL_INIT_RC := audioserver.rc - -LOCAL_CFLAGS := -Werror -Wall - -include $(BUILD_EXECUTABLE) diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc index f75e4c7a9b233f28728a11e825a44b39110898ff..c4a6601fd195b6d41408c1e607b68cbfb825b084 100644 --- a/media/audioserver/audioserver.rc +++ b/media/audioserver/audioserver.rc @@ -8,6 +8,7 @@ service audioserver /system/bin/audioserver task_profiles ProcessCapacityHigh HighPerformance onrestart restart vendor.audio-hal onrestart restart vendor.audio-hal-4-0-msd + onrestart restart audio_proxy_service # Keep the original service names for backward compatibility onrestart restart vendor.audio-hal-2-0 onrestart restart audio-hal-2-0 @@ -20,6 +21,7 @@ on property:vts.native_server.on=0 on property:init.svc.audioserver=stopped stop vendor.audio-hal stop vendor.audio-hal-4-0-msd + stop audio_proxy_service # Keep the original service names for backward compatibility stop vendor.audio-hal-2-0 stop audio-hal-2-0 @@ -28,6 +30,7 @@ on property:init.svc.audioserver=stopped # audioserver bringing it back into running state. start vendor.audio-hal start vendor.audio-hal-4-0-msd + start audio_proxy_service # Keep the original service names for backward compatibility start vendor.audio-hal-2-0 start audio-hal-2-0 @@ -35,6 +38,7 @@ on property:init.svc.audioserver=stopped on property:init.svc.audioserver=running start vendor.audio-hal start vendor.audio-hal-4-0-msd + start audio_proxy_service # Keep the original service names for backward compatibility start vendor.audio-hal-2-0 start audio-hal-2-0 @@ -44,10 +48,12 @@ on property:sys.audio.restart.hal=1 # Keep the original service names for backward compatibility stop vendor.audio-hal stop vendor.audio-hal-4-0-msd + stop audio_proxy_service stop vendor.audio-hal-2-0 stop audio-hal-2-0 start vendor.audio-hal start vendor.audio-hal-4-0-msd + start audio_proxy_service start vendor.audio-hal-2-0 start audio-hal-2-0 # reset the property diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp index 17309dd2f0e55a8c499a53bbf219624565e8e347..8ee1efb2f3eef24b94fc6a7da47a8821a3169212 100644 --- a/media/audioserver/main_audioserver.cpp +++ b/media/audioserver/main_audioserver.cpp @@ -29,8 +29,8 @@ #include #include -// from LOCAL_C_INCLUDES -#include "aaudio/AAudioTesting.h" +// from include_dirs +#include "aaudio/AAudioTesting.h" // aaudio_policy_t, AAUDIO_PROP_MMAP_POLICY, AAUDIO_POLICY_* #include "AudioFlinger.h" #include "AudioPolicyService.h" #include "AAudioService.h" diff --git a/media/bufferpool/1.0/TEST_MAPPING b/media/bufferpool/1.0/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..a1e6a585a59b9efde02ac18cb55aed358dd8a849 --- /dev/null +++ b/media/bufferpool/1.0/TEST_MAPPING @@ -0,0 +1,8 @@ +// mappings for frameworks/av/media/bufferpool/1.0 +{ + "presubmit": [ + + { "name": "VtsVndkHidlBufferpoolV1_0TargetSingleTest" }, + { "name": "VtsVndkHidlBufferpoolV1_0TargetMultiTest"} + ] +} diff --git a/media/bufferpool/1.0/vts/Android.bp b/media/bufferpool/1.0/vts/Android.bp index b39871ef531453577a16b70a9616a9f2fdccd7d2..0977ba5274e099cf1112ddf402aff96d06d56f87 100644 --- a/media/bufferpool/1.0/vts/Android.bp +++ b/media/bufferpool/1.0/vts/Android.bp @@ -25,6 +25,7 @@ package { cc_test { name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest", + test_suites: ["device-tests"], defaults: ["VtsHalTargetTestDefaults"], srcs: [ "allocator.cpp", @@ -43,6 +44,7 @@ cc_test { cc_test { name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest", + test_suites: ["device-tests"], defaults: ["VtsHalTargetTestDefaults"], srcs: [ "allocator.cpp", diff --git a/media/bufferpool/2.0/TEST_MAPPING b/media/bufferpool/2.0/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..65dee2c5316256f835b993660844fb150288c334 --- /dev/null +++ b/media/bufferpool/2.0/TEST_MAPPING @@ -0,0 +1,7 @@ +// mappings for frameworks/av/media/bufferpool/2.0 +{ + "presubmit": [ + { "name": "VtsVndkHidlBufferpoolV2_0TargetSingleTest"}, + { "name": "VtsVndkHidlBufferpoolV2_0TargetMultiTest"} + ] +} diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp index d6db20261da43c943e9bdb62e4fe38bbd740f8bc..803a813bcc67dcbc7583b6eb8790dea5d90d5bf1 100644 --- a/media/bufferpool/2.0/tests/Android.bp +++ b/media/bufferpool/2.0/tests/Android.bp @@ -25,6 +25,7 @@ package { cc_test { name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest", + test_suites: ["device-tests"], defaults: ["VtsHalTargetTestDefaults"], srcs: [ "allocator.cpp", @@ -33,7 +34,7 @@ cc_test { static_libs: [ "android.hardware.media.bufferpool@2.0", "libcutils", - "libstagefright_bufferpool@2.0", + "libstagefright_bufferpool@2.0.1", ], shared_libs: [ "libfmq", @@ -43,6 +44,7 @@ cc_test { cc_test { name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest", + test_suites: ["device-tests"], defaults: ["VtsHalTargetTestDefaults"], srcs: [ "allocator.cpp", @@ -51,10 +53,30 @@ cc_test { static_libs: [ "android.hardware.media.bufferpool@2.0", "libcutils", - "libstagefright_bufferpool@2.0", + "libstagefright_bufferpool@2.0.1", ], shared_libs: [ "libfmq", ], compile_multilib: "both", } + +cc_test { + name: "VtsVndkHidlBufferpoolV2_0TargetCondTest", + test_suites: ["device-tests"], + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "allocator.cpp", + "cond.cpp", + ], + static_libs: [ + "android.hardware.media.bufferpool@2.0", + "libcutils", + "libstagefright_bufferpool@2.0.1", + ], + shared_libs: [ + "libhidlbase", + "libfmq", + ], + compile_multilib: "both", +} diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp index 843f7ea9719a8f6a7992a5eea625d0fc4ba425cc..25b08ef13248fac637ced4a03af1ef0a5ee1a7e8 100644 --- a/media/bufferpool/2.0/tests/allocator.cpp +++ b/media/bufferpool/2.0/tests/allocator.cpp @@ -120,6 +120,24 @@ struct AllocationDtor { } +void IpcMutex::init() { + pthread_mutexattr_t mattr; + pthread_mutexattr_init(&mattr); + pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&lock, &mattr); + pthread_mutexattr_destroy(&mattr); + + pthread_condattr_t cattr; + pthread_condattr_init(&cattr); + pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); + pthread_cond_init(&cond, &cattr); + pthread_condattr_destroy(&cattr); +} + +IpcMutex *IpcMutex::Import(void *pMutex) { + return reinterpret_cast(pMutex); +} + ResultStatus TestBufferPoolAllocator::allocate( const std::vector ¶ms, @@ -201,9 +219,33 @@ bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsign return false; } +bool TestBufferPoolAllocator::MapMemoryForMutex(const native_handle_t *handle, void **mem) { + if (!HandleAshmem::isValid(handle)) { + return false; + } + const HandleAshmem *o = static_cast(handle); + *mem = mmap( + NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0); + if (*mem == MAP_FAILED || *mem == nullptr) { + return false; + } + return true; +} + +bool TestBufferPoolAllocator::UnmapMemoryForMutex(void *mem) { + munmap(mem, sizeof(IpcMutex)); + return true; +} + void getTestAllocatorParams(std::vector *params) { constexpr static int kAllocationSize = 1024 * 10; Params ashmemParams(kAllocationSize); params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams)); } + +void getIpcMutexParams(std::vector *params) { + Params ashmemParams(sizeof(IpcMutex)); + + params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams)); +} diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h index 5281dc31443edacc17c66428d201eca8271a7fa7..862d1a533a9e62eee3e5753eaba1f7af48a1d190 100644 --- a/media/bufferpool/2.0/tests/allocator.h +++ b/media/bufferpool/2.0/tests/allocator.h @@ -17,6 +17,7 @@ #ifndef VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H #define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H +#include #include using android::hardware::media::bufferpool::V2_0::ResultStatus; @@ -25,6 +26,17 @@ using android::hardware::media::bufferpool::V2_0::implementation:: using android::hardware::media::bufferpool::V2_0::implementation:: BufferPoolAllocator; +struct IpcMutex { + pthread_mutex_t lock; + pthread_cond_t cond; + int counter = 0; + bool signalled = false; + + void init(); + + static IpcMutex *Import(void *mem); +}; + // buffer allocator for the tests class TestBufferPoolAllocator : public BufferPoolAllocator { public: @@ -43,9 +55,14 @@ class TestBufferPoolAllocator : public BufferPoolAllocator { static bool Verify(const native_handle_t *handle, const unsigned char val); + static bool MapMemoryForMutex(const native_handle_t *handle, void **mem); + + static bool UnmapMemoryForMutex(void *mem); }; // retrieve buffer allocator paramters void getTestAllocatorParams(std::vector *params); +void getIpcMutexParams(std::vector *params); + #endif // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H diff --git a/media/bufferpool/2.0/tests/cond.cpp b/media/bufferpool/2.0/tests/cond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21beea8f44084967ee84f6889e4eddee87367046 --- /dev/null +++ b/media/bufferpool/2.0/tests/cond.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 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. + */ + +#define LOG_TAG "buffferpool_unit_test" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "allocator.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::hidl_handle; +using android::hardware::media::bufferpool::V2_0::IClientManager; +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation::BufferId; +using android::hardware::media::bufferpool::V2_0::implementation::ClientManager; +using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId; +using android::hardware::media::bufferpool::V2_0::implementation::TransactionId; +using android::hardware::media::bufferpool::BufferPoolData; + +namespace { + +// communication message types between processes. +enum PipeCommand : int32_t { + INIT_OK = 0, + INIT_ERROR, + SEND, + RECEIVE_OK, + RECEIVE_ERROR, +}; + +// communication message between processes. +union PipeMessage { + struct { + int32_t command; + BufferId bufferId; + ConnectionId connectionId; + TransactionId transactionId; + int64_t timestampUs; + } data; + char array[0]; +}; + +constexpr int kSignalInt = 200; + +// media.bufferpool test setup +class BufferpoolMultiTest : public ::testing::Test { + public: + virtual void SetUp() override { + ResultStatus status; + mReceiverPid = -1; + mConnectionValid = false; + + ASSERT_TRUE(pipe(mCommandPipeFds) == 0); + ASSERT_TRUE(pipe(mResultPipeFds) == 0); + + mReceiverPid = fork(); + ASSERT_TRUE(mReceiverPid >= 0); + + if (mReceiverPid == 0) { + doReceiver(); + // In order to ignore gtest behaviour, wait for being killed from + // tearDown + pause(); + } + + mManager = ClientManager::getInstance(); + ASSERT_NE(mManager, nullptr); + + mAllocator = std::make_shared(); + ASSERT_TRUE((bool)mAllocator); + + status = mManager->create(mAllocator, &mConnectionId); + ASSERT_TRUE(status == ResultStatus::OK); + mConnectionValid = true; + } + + virtual void TearDown() override { + if (mReceiverPid > 0) { + kill(mReceiverPid, SIGKILL); + int wstatus; + wait(&wstatus); + } + + if (mConnectionValid) { + mManager->close(mConnectionId); + } + } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + android::sp mManager; + std::shared_ptr mAllocator; + bool mConnectionValid; + ConnectionId mConnectionId; + pid_t mReceiverPid; + int mCommandPipeFds[2]; + int mResultPipeFds[2]; + + bool sendMessage(int *pipes, const PipeMessage &message) { + int ret = write(pipes[1], message.array, sizeof(PipeMessage)); + return ret == sizeof(PipeMessage); + } + + bool receiveMessage(int *pipes, PipeMessage *message) { + int ret = read(pipes[0], message->array, sizeof(PipeMessage)); + return ret == sizeof(PipeMessage); + } + + void doReceiver() { + configureRpcThreadpool(1, false); + PipeMessage message; + mManager = ClientManager::getInstance(); + if (!mManager) { + message.data.command = PipeCommand::INIT_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + android::status_t status = mManager->registerAsService(); + if (status != android::OK) { + message.data.command = PipeCommand::INIT_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + message.data.command = PipeCommand::INIT_OK; + sendMessage(mResultPipeFds, message); + + int val = 0; + receiveMessage(mCommandPipeFds, &message); + { + native_handle_t *rhandle = nullptr; + std::shared_ptr rbuffer; + void *mem = nullptr; + IpcMutex *mutex = nullptr; + ResultStatus status = mManager->receive( + message.data.connectionId, message.data.transactionId, + message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer); + mManager->close(message.data.connectionId); + if (status != ResultStatus::OK) { + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + if (!TestBufferPoolAllocator::MapMemoryForMutex(rhandle, &mem)) { + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + mutex = IpcMutex::Import(mem); + pthread_mutex_lock(&(mutex->lock)); + while (mutex->signalled != true) { + pthread_cond_wait(&(mutex->cond), &(mutex->lock)); + } + val = mutex->counter; + pthread_mutex_unlock(&(mutex->lock)); + + (void)TestBufferPoolAllocator::UnmapMemoryForMutex(mem); + if (rhandle) { + native_handle_close(rhandle); + native_handle_delete(rhandle); + } + } + if (val == kSignalInt) { + message.data.command = PipeCommand::RECEIVE_OK; + } else { + message.data.command = PipeCommand::RECEIVE_ERROR; + } + sendMessage(mResultPipeFds, message); + } +}; + +// Buffer transfer test between processes. +TEST_F(BufferpoolMultiTest, TransferBuffer) { + ResultStatus status; + PipeMessage message; + + ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)); + + android::sp receiver = IClientManager::getService(); + ConnectionId receiverId; + ASSERT_TRUE((bool)receiver); + + status = mManager->registerSender(receiver, mConnectionId, &receiverId); + ASSERT_TRUE(status == ResultStatus::OK); + { + native_handle_t *shandle = nullptr; + std::shared_ptr sbuffer; + TransactionId transactionId; + int64_t postUs; + std::vector vecParams; + void *mem = nullptr; + IpcMutex *mutex = nullptr; + + getIpcMutexParams(&vecParams); + status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer); + ASSERT_TRUE(status == ResultStatus::OK); + + ASSERT_TRUE(TestBufferPoolAllocator::MapMemoryForMutex(shandle, &mem)); + + mutex = new(mem) IpcMutex(); + mutex->init(); + + status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs); + ASSERT_TRUE(status == ResultStatus::OK); + + message.data.command = PipeCommand::SEND; + message.data.bufferId = sbuffer->mId; + message.data.connectionId = receiverId; + message.data.transactionId = transactionId; + message.data.timestampUs = postUs; + sendMessage(mCommandPipeFds, message); + for (int i=0; i < 200000000; ++i) { + // no-op in order to ensure + // pthread_cond_wait is called before pthread_cond_signal + } + pthread_mutex_lock(&(mutex->lock)); + mutex->counter = kSignalInt; + mutex->signalled = true; + pthread_cond_signal(&(mutex->cond)); + pthread_mutex_unlock(&(mutex->lock)); + (void)TestBufferPoolAllocator::UnmapMemoryForMutex(mem); + if (shandle) { + native_handle_close(shandle); + native_handle_delete(shandle); + } + } + EXPECT_TRUE(receiveMessage(mResultPipeFds, &message)); + EXPECT_TRUE(message.data.command == PipeCommand::RECEIVE_OK); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + android::hardware::details::setTrebleTestingOverride(true); + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp index b40838e6d3e95f449ffe20621f42fba2fe41f2ab..43b0a8c2719070f49315103da1c5ec7dc1ae159b 100644 --- a/media/bufferpool/2.0/tests/multi.cpp +++ b/media/bufferpool/2.0/tests/multi.cpp @@ -161,11 +161,18 @@ class BufferpoolMultiTest : public ::testing::Test { message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer); mManager->close(message.data.connectionId); if (status != ResultStatus::OK) { - if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) { - message.data.command = PipeCommand::RECEIVE_ERROR; - sendMessage(mResultPipeFds, message); - return; - } + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) { + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + if (rhandle) { + native_handle_close(rhandle); + native_handle_delete(rhandle); } } message.data.command = PipeCommand::RECEIVE_OK; @@ -198,6 +205,10 @@ TEST_F(BufferpoolMultiTest, TransferBuffer) { ASSERT_TRUE(status == ResultStatus::OK); ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77)); + if (shandle) { + native_handle_close(shandle); + native_handle_delete(shandle); + } status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs); ASSERT_TRUE(status == ResultStatus::OK); @@ -210,6 +221,7 @@ TEST_F(BufferpoolMultiTest, TransferBuffer) { sendMessage(mCommandPipeFds, message); } EXPECT_TRUE(receiveMessage(mResultPipeFds, &message)); + EXPECT_TRUE(message.data.command == PipeCommand::RECEIVE_OK); } } // anonymous namespace diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp index 777edcfef83f981d33590572287ecf2242622d74..1e9027b0c005ccff79925af882322b04baccb41d 100644 --- a/media/bufferpool/2.0/tests/single.cpp +++ b/media/bufferpool/2.0/tests/single.cpp @@ -102,6 +102,10 @@ TEST_F(BufferpoolSingleTest, AllocateBuffer) { for (int i = 0; i < kNumAllocationTest; ++i) { status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]); ASSERT_TRUE(status == ResultStatus::OK); + if (allocHandle) { + native_handle_close(allocHandle); + native_handle_delete(allocHandle); + } } for (int i = 0; i < kNumAllocationTest; ++i) { for (int j = i + 1; j < kNumAllocationTest; ++j) { @@ -125,6 +129,10 @@ TEST_F(BufferpoolSingleTest, RecycleBuffer) { status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer); ASSERT_TRUE(status == ResultStatus::OK); bid[i] = buffer->mId; + if (allocHandle) { + native_handle_close(allocHandle); + native_handle_delete(allocHandle); + } } for (int i = 1; i < kNumRecycleTest; ++i) { ASSERT_TRUE(bid[i - 1] == bid[i]); @@ -154,6 +162,15 @@ TEST_F(BufferpoolSingleTest, TransferBuffer) { &recvHandle, &rbuffer); EXPECT_TRUE(status == ResultStatus::OK); ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77)); + + if (allocHandle) { + native_handle_close(allocHandle); + native_handle_delete(allocHandle); + } + if (recvHandle) { + native_handle_close(recvHandle); + native_handle_delete(recvHandle); + } } } // anonymous namespace diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING index 8afa1a8148faae688cb90c59d6aabe6b7d89f548..6ac42101c331e9b3f666d3664bd045d309a3bae9 100644 --- a/media/codec2/TEST_MAPPING +++ b/media/codec2/TEST_MAPPING @@ -1,5 +1,12 @@ { "presubmit": [ + // TODO failing 4 of 13 + // { "name": "codec2_core_param_test"}, + // TODO(b/155516524) + // { "name": "codec2_vndk_interface_test"}, + { "name": "codec2_vndk_test"} + ], + "presubmit-large": [ { "name": "CtsMediaTestCases", "options": [ diff --git a/media/codec2/components/aac/DrcPresModeWrap.cpp b/media/codec2/components/aac/DrcPresModeWrap.cpp index bee969bf4f0febcbbc4791bcc0fd9cb02720582b..7ce5c9d2f2098878446391c70aa644d351f3d030 100644 --- a/media/codec2/components/aac/DrcPresModeWrap.cpp +++ b/media/codec2/components/aac/DrcPresModeWrap.cpp @@ -161,7 +161,7 @@ CDrcPresModeWrapper::update() int newHeavy = mDesHeavy; if (mDataUpdate) { - // sanity check + // Validation check if ((mDesTarget < MAX_TARGET_LEVEL) && (mDesTarget != -1)){ mDesTarget = MAX_TARGET_LEVEL; // limit target level to -10 dB or below newTarget = MAX_TARGET_LEVEL; @@ -217,7 +217,7 @@ CDrcPresModeWrapper::update() } else { // handle other used encoder target levels - // Sanity check: DRC presentation mode is only specified for max. 5.1 channels + // Validation check: DRC presentation mode is only specified for max. 5.1 channels if (mStreamNrAACChan > 6) { drcPresMode = 0; } @@ -308,7 +308,7 @@ CDrcPresModeWrapper::update() } // switch() } // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL) - // sanity again + // Validation check again if (newHeavy == 1) { newBoostFactor=127; // not really needed as the same would be done by the decoder anyway newAttFactor = 127; diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index 0b121ad0ee29ea84cc74831738daf3dff4e87a78..d65ffa58f3ff1dc0b1123d8d4fff5a9f991abd51 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -393,6 +393,61 @@ public: C2P &me) { (void)mayBlock; (void)me; + + // TODO: refactor with same algorithm in the SetQp() + int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN; + int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX; + + for (size_t i = 0; i < me.v.flexCount(); ++i) { + const C2PictureQuantizationStruct &layer = me.v.m.values[i]; + + if (layer.type_ == C2Config::picture_type_t(I_FRAME)) { + iMax = layer.max; + iMin = layer.min; + ALOGV("iMin %d iMax %d", iMin, iMax); + } else if (layer.type_ == C2Config::picture_type_t(P_FRAME)) { + pMax = layer.max; + pMin = layer.min; + ALOGV("pMin %d pMax %d", pMin, pMax); + } else if (layer.type_ == C2Config::picture_type_t(B_FRAME)) { + bMax = layer.max; + bMin = layer.min; + ALOGV("bMin %d bMax %d", bMin, bMax); + } + } + + ALOGV("PictureQuantizationSetter(entry): i %d-%d p %d-%d b %d-%d", + iMin, iMax, pMin, pMax, bMin, bMax); + + // ensure we have legal values + iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX); + iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX); + pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX); + pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX); + bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX); + bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX); + + // put them back into the structure + for (size_t i = 0; i < me.v.flexCount(); ++i) { + const C2PictureQuantizationStruct &layer = me.v.m.values[i]; + + if (layer.type_ == C2Config::picture_type_t(I_FRAME)) { + me.set().m.values[i].max = iMax; + me.set().m.values[i].min = iMin; + } + if (layer.type_ == C2Config::picture_type_t(P_FRAME)) { + me.set().m.values[i].max = pMax; + me.set().m.values[i].min = pMin; + } + if (layer.type_ == C2Config::picture_type_t(B_FRAME)) { + me.set().m.values[i].max = bMax; + me.set().m.values[i].min = bMin; + } + } + + ALOGV("PictureQuantizationSetter(exit): i %d-%d p %d-%d b %d-%d", + iMin, iMax, pMin, pMax, bMin, bMax); + return C2R::Ok(); } @@ -765,10 +820,11 @@ c2_status_t C2SoftAvcEnc::setQp() { s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL; s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP; - // these are the ones we're going to set, so want them to default .... - // to the DEFAULT values for the codec instea dof CODEC_ bounding - int32_t iMin = INT32_MIN, pMin = INT32_MIN, bMin = INT32_MIN; - int32_t iMax = INT32_MAX, pMax = INT32_MAX, bMax = INT32_MAX; + // TODO: refactor with same algorithm in the PictureQuantizationSetter() + int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN; + int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX; + + IntfImpl::Lock lock = mIntf->lock(); std::shared_ptr qp = mIntf->getPictureQuantization_l(); @@ -790,22 +846,6 @@ c2_status_t C2SoftAvcEnc::setQp() { } } - // INT32_{MIN,MAX} means unspecified, so use the codec's default - if (iMax == INT32_MAX) iMax = DEFAULT_I_QP_MAX; - if (iMin == INT32_MIN) iMin = DEFAULT_I_QP_MIN; - if (pMax == INT32_MAX) pMax = DEFAULT_P_QP_MAX; - if (pMin == INT32_MIN) pMin = DEFAULT_P_QP_MIN; - if (bMax == INT32_MAX) bMax = DEFAULT_B_QP_MAX; - if (bMin == INT32_MIN) bMin = DEFAULT_B_QP_MIN; - - // ensure we have legal values - iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX); - iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX); - pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX); - pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX); - bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX); - bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX); - s_qp_ip.u4_i_qp_max = iMax; s_qp_ip.u4_i_qp_min = iMin; s_qp_ip.u4_p_qp_max = pMax; @@ -818,7 +858,7 @@ c2_status_t C2SoftAvcEnc::setQp() { s_qp_ip.u4_p_qp = std::clamp(DEFAULT_P_QP, pMin, pMax); s_qp_ip.u4_b_qp = std::clamp(DEFAULT_B_QP, bMin, bMax); - ALOGV("setting QP: i %d-%d p %d-%d b %d-%d", iMin, iMax, pMin, pMax, bMin, bMax); + ALOGV("setQp(): i %d-%d p %d-%d b %d-%d", iMin, iMax, pMin, pMax, bMin, bMax); s_qp_ip.u4_timestamp_high = -1; diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h index baf33e20f8db880b7cd222d524c7e42fe97ef0e8..1fecd9ed33a52e77209a9f037b0befae397a9663 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.h +++ b/media/codec2/components/avc/C2SoftAvcEnc.h @@ -99,8 +99,10 @@ namespace android { #define STRLENGTH 500 #define DEFAULT_CONSTRAINED_INTRA 0 -/** limits as specified by h264 */ -#define CODEC_QP_MIN 0 +/** limits as specified by h264 + * (QP_MIN==4 is actually a limitation of this SW codec, not the H.264 standard) + **/ +#define CODEC_QP_MIN 4 #define CODEC_QP_MAX 51 diff --git a/media/codec2/components/flac/Android.bp b/media/codec2/components/flac/Android.bp index 1143bedbc6c490b708838c28b44468fd716b02e0..38dfce46503ee5db2c4014f86f961f2f6bafc60c 100644 --- a/media/codec2/components/flac/Android.bp +++ b/media/codec2/components/flac/Android.bp @@ -42,11 +42,8 @@ cc_library { srcs: ["C2SoftFlacEnc.cpp"], - shared_libs: [ - "libaudioutils", - ], - static_libs: [ "libFLAC", + "libaudioutils", ], } diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp index 1c0babda9b042d8c217f172df5a3570bda41d495..6fead3a8a7cc7f075d239761dd5fd7df2cf24499 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.cpp +++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp @@ -262,9 +262,10 @@ void C2SoftFlacEnc::process( work->result = C2_NO_MEMORY; return; } - C2WriteView wView = mOutputBlock->map().get(); - if (wView.error()) { - ALOGE("write view map failed %d", wView.error()); + + err = mOutputBlock->map().get().error(); + if (err) { + ALOGE("write view map failed %d", err); work->result = C2_CORRUPTED; return; } diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index 7eaed08bc54c023282dd5b72145e62f452d367f1..2cc7ab7d7de1f7f2db1211b5980d12c0da5bc8ea 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -2367,6 +2367,7 @@ typedef C2PortParam, kParamIndexTunnelSyste C2PortTunnelSystemTime; constexpr char C2_PARAMKEY_OUTPUT_RENDER_TIME[] = "output.render-time"; + /** * Tunneled mode video peek signaling flag. * @@ -2391,22 +2392,24 @@ typedef C2StreamParam C2StreamTunnelStartRender; constexpr char C2_PARAMKEY_TUNNEL_START_RENDER[] = "output.tunnel-start-render"; -C2ENUM(C2PlatformConfig::encoding_quality_level_t, uint32_t, - NONE, - S_HANDHELD, - S_HANDHELD_PC -); - -namespace android { - /** * Encoding quality level signaling. + * + * Signal the 'minimum encoding quality' introduced in Android 12/S. It indicates + * whether the underlying codec is expected to take extra steps to ensure quality meets the + * appropriate minimum. A value of NONE indicates that the codec is not to apply + * any minimum quality bar requirements. Other values indicate that the codec is to apply + * a minimum quality bar, with the exact quality bar being decided by the parameter value. */ typedef C2GlobalParam>, kParamIndexEncodingQualityLevel> C2EncodingQualityLevel; +constexpr char C2_PARAMKEY_ENCODING_QUALITY_LEVEL[] = "algo.encoding-quality-level"; -} +C2ENUM(C2PlatformConfig::encoding_quality_level_t, uint32_t, + NONE = 0, + S_HANDHELD = 1 // corresponds to VMAF=70 +); /// @} diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index 008def806b23923f385333805cba8954497697d7..122aacdf42f5ac1f967eb6c0fb49e4ef8887efc0 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -15,7 +15,6 @@ cc_library { defaults: ["hidl_defaults"], srcs: [ - "OutputBufferQueue.cpp", "types.cpp", ], diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp index 5ec88eca32e14164f4a1f8798c1c1b035de5f8a9..7c2e01400b8a3f6e1ff4254af99e5cc985f00987 100644 --- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp @@ -201,6 +201,8 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc); mAllocatorMutex.unlock(); if (err != OK) { + native_handle_close(handle); + native_handle_delete(handle); return UNKNOWN_ERROR; } std::shared_ptr block = diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp index fb1c291a4a7d0a2621089c653097798865d19602..47ceed58415dbd56eafe45c99de32e52dc53f0d9 100644 --- a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp @@ -73,9 +73,10 @@ TEST_P(Codec2MasterHalTest, ListComponents) { ASSERT_NE(listener, nullptr); // Create component from all known services - component = - mClient->CreateComponentByName(listTraits[i].name.c_str(), listener, &mClient); - ASSERT_NE(component, nullptr) + const c2_status_t status = + android::Codec2Client::CreateComponentByName( + listTraits[i].name.c_str(), listener, &component, &mClient); + ASSERT_EQ(status, C2_OK) << "Create component failed for " << listTraits[i].name.c_str(); } } diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp index f211ecfc91cbd5b9e7b0f1577c254fa417c7473c..ecc4f9dcb60d39c6c3072861f8af4226639d4e68 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp @@ -36,6 +36,8 @@ cc_test { "libgui", "libutils", "libcrypto", + "libdatasource", + "libui", ], data: [":media_c2_v1_video_decode_res"], test_config: "VtsHalMediaC2V1_0TargetVideoDecTest.xml", diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp index 9fef1fda7b5aa777462d78e3d1846e0e5bb8cc0a..67873fa16b1b9e52dfb3c318ea8baf0933a57633 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp @@ -33,11 +33,18 @@ #include #include #include +#include +#include +#include #include "media_c2_hidl_test_common.h" #include "media_c2_video_hidl_test_common.h" -using DecodeTestParameters = std::tuple; +constexpr size_t kSmoothnessFactor = 4; +constexpr size_t kRenderingDepth = 3; +enum surfaceMode_t { NO_SURFACE, NULL_SURFACE, SURFACE }; + +using DecodeTestParameters = std::tuple; static std::vector gDecodeTestParameters; using CsdFlushTestParameters = std::tuple; @@ -396,6 +403,36 @@ bool Codec2VideoDecHidlTestBase::getFileNames(size_t streamIndex) { return false; } +void setOutputSurface(const std::shared_ptr& component, + surfaceMode_t surfMode) { + using namespace android; + sp producer = 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; + if (surfMode == SURFACE) { + sp consumer = nullptr; + BufferQueue::createBufferQueue(&producer, &consumer); + ASSERT_NE(producer, nullptr) << "createBufferQueue returned invalid producer"; + ASSERT_NE(consumer, nullptr) << "createBufferQueue returned invalid consumer"; + + sp texture = + new GLConsumer(consumer, 0 /* tex */, GLConsumer::TEXTURE_EXTERNAL, + true /* useFenceSync */, false /* isControlledByApp */); + + sp gSurface = new Surface(producer); + ASSERT_NE(gSurface, nullptr) << "getSurface failed"; + + producer->setGenerationNumber(generation); + } + + c2_status_t err = component->setOutputSurface(C2BlockPool::BASIC_GRAPHIC, producer, generation, + maxDequeueBuffers); + ASSERT_EQ(err, C2_OK) << "setOutputSurface failed"; +} + void decodeNFrames(const std::shared_ptr& component, std::mutex& queueLock, std::condition_variable& queueCondition, std::list>& workQueue, @@ -554,6 +591,7 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { if (mDisableTest) GTEST_SKIP() << "Test is disabled"; bool signalEOS = std::get<3>(GetParam()); + surfaceMode_t surfMode = std::get<4>(GetParam()); mTimestampDevTest = true; android::Vector Info; @@ -598,6 +636,10 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { refChksum.close(); } + if (surfMode != NO_SURFACE) { + ASSERT_NO_FATAL_FAILURE(setOutputSurface(mComponent, surfMode)); + } + ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS)); @@ -1065,18 +1107,23 @@ int main(int argc, char** argv) { parseArgs(argc, argv); gTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER); for (auto params : gTestParameters) { + // mOutputBufferQueue->configure() crashes when surface is NULL + std::initializer_list surfaceMode = { + surfaceMode_t::NO_SURFACE, surfaceMode_t::NULL_SURFACE, surfaceMode_t::SURFACE}; + for (surfaceMode_t mode : surfaceMode) { + gDecodeTestParameters.push_back( + std::make_tuple(std::get<0>(params), std::get<1>(params), 0, false, mode)); + gDecodeTestParameters.push_back( + std::make_tuple(std::get<0>(params), std::get<1>(params), 0, true, mode)); + } gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 0, false)); - gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 0, true)); - gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 1, false)); + std::make_tuple(std::get<0>(params), std::get<1>(params), 1, false, NO_SURFACE)); gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 1, true)); + std::make_tuple(std::get<0>(params), std::get<1>(params), 1, true, NO_SURFACE)); gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 2, false)); + std::make_tuple(std::get<0>(params), std::get<1>(params), 2, false, NO_SURFACE)); gDecodeTestParameters.push_back( - std::make_tuple(std::get<0>(params), std::get<1>(params), 2, true)); + std::make_tuple(std::get<0>(params), std::get<1>(params), 2, true, NO_SURFACE)); gCsdFlushTestParameters.push_back( std::make_tuple(std::get<0>(params), std::get<1>(params), true)); diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp index 839a910de5d176be06d0f750b85cd33b9e941a07..0eeedb603a951bda04994bdae708ce9b0fe1ea92 100644 --- a/media/codec2/hidl/1.1/utils/Android.bp +++ b/media/codec2/hidl/1.1/utils/Android.bp @@ -15,7 +15,6 @@ cc_library { defaults: ["hidl_defaults"], srcs: [ - "OutputBufferQueue.cpp", "types.cpp", ], @@ -176,14 +175,3 @@ cc_defaults { ], } -// Alias to the latest "defaults" for Codec 2.0 HAL service implementations -cc_defaults { - name: "libcodec2-hidl-defaults", - defaults: ["libcodec2-hidl-defaults@1.1"], -} - -// Alias to the latest "defaults" for Codec 2.0 HAL client -cc_defaults { - name: "libcodec2-hidl-client-defaults", - defaults: ["libcodec2-hidl-client-defaults@1.1"], -} diff --git a/media/codec2/hidl/1.1/utils/ComponentStore.cpp b/media/codec2/hidl/1.1/utils/ComponentStore.cpp index 163686ddc620b7b536d1130fa3b0b3e3c7b5f026..d47abdd040636e264f977ec9c4fa9e169432c8aa 100644 --- a/media/codec2/hidl/1.1/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.1/utils/ComponentStore.cpp @@ -366,6 +366,9 @@ Return ComponentStore::createComponent_1_1( mStore->createComponent(name, &c2component)); if (status == Status::OK) { +#ifndef __ANDROID_APEX__ + c2component = GetFilterWrapper()->maybeWrapComponent(c2component); +#endif onInterfaceLoaded(c2component->intf()); component = new Component(c2component, listener, this, pool); if (!component) { diff --git a/media/codec2/hidl/1.2/utils/Android.bp b/media/codec2/hidl/1.2/utils/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..e4e4ad58ff7cd40c12c9583d8c780de965d8a06c --- /dev/null +++ b/media/codec2/hidl/1.2/utils/Android.bp @@ -0,0 +1,206 @@ +// DO NOT DEPEND ON THIS DIRECTLY +// use libcodec2-hidl-client-defaults instead +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_license"], +} + +cc_library { + name: "libcodec2_hidl_client@1.2", + + defaults: ["hidl_defaults"], + + srcs: [ + "types.cpp", + ], + + header_libs: [ + "libcodec2_internal", // private + ], + + shared_libs: [ + "android.hardware.media.bufferpool@2.0", + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "libbase", + "libcodec2", + "libcodec2_hidl_client@1.0", + "libcodec2_hidl_client@1.1", + "libcodec2_vndk", + "libcutils", + "libgui", + "libhidlbase", + "liblog", + "libstagefright_bufferpool@2.0.1", + "libui", + "libutils", + ], + + export_include_dirs: [ + "include", + ], + + export_shared_lib_headers: [ + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "libcodec2", + "libcodec2_hidl_client@1.0", + "libcodec2_hidl_client@1.1", + "libgui", + "libstagefright_bufferpool@2.0.1", + "libui", + ], + + // Device does not boot when global ThinLTO is enabled for this library. + // http://b/170595429 + lto: { + never: true, + }, +} + + +// DO NOT DEPEND ON THIS DIRECTLY +// use libcodec2-hidl-defaults instead +cc_library { + name: "libcodec2_hidl@1.2", + vendor_available: true, + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + "test_com.android.media.swcodec", + ], + + defaults: ["hidl_defaults"], + + srcs: [ + "Component.cpp", + "ComponentInterface.cpp", + "ComponentStore.cpp", + "Configurable.cpp", + "InputBufferManager.cpp", + "InputSurface.cpp", + "InputSurfaceConnection.cpp", + "types.cpp", + ], + + header_libs: [ + "libbinder_headers", + "libsystem_headers", + "libcodec2_internal", // private + ], + + shared_libs: [ + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + "android.hardware.graphics.common@1.0", + "android.hardware.media@1.0", + "android.hardware.media.bufferpool@2.0", + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "android.hardware.media.omx@1.0", + "libbase", + "libcodec2", + "libcodec2_hidl@1.0", + "libcodec2_hidl@1.1", + "libcodec2_hidl_plugin_stub", + "libcodec2_vndk", + "libcutils", + "libhidlbase", + "liblog", + "libstagefright_bufferpool@2.0.1", + "libstagefright_bufferqueue_helper_novndk", + "libui", + "libutils", + ], + + target: { + vendor: { + exclude_shared_libs: [ + "libstagefright_bufferqueue_helper_novndk", + "libcodec2_hidl_plugin_stub", + ], + shared_libs: [ + "libstagefright_bufferqueue_helper", + "libcodec2_hidl_plugin", + ], + }, + apex: { + exclude_shared_libs: [ + "libcodec2_hidl_plugin_stub", + "libcodec2_hidl_plugin", + ], + }, + }, + + export_include_dirs: [ + "include", + ], + + export_shared_lib_headers: [ + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "libcodec2", + "libcodec2_hidl@1.0", + "libcodec2_hidl@1.1", + "libcodec2_vndk", + "libhidlbase", + "libstagefright_bufferpool@2.0.1", + "libui", + ], +} + +// public dependency for Codec 2.0 HAL service implementations +cc_defaults { + name: "libcodec2-hidl-defaults@1.2", + defaults: ["libcodec2-impl-defaults"], + + shared_libs: [ + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "libcodec2_hidl@1.0", + "libcodec2_hidl@1.1", + "libcodec2_hidl@1.2", + "libcodec2_vndk", + "libhidlbase", + ], +} + +// public dependency for Codec 2.0 HAL client +cc_defaults { + name: "libcodec2-hidl-client-defaults@1.2", + defaults: ["libcodec2-impl-defaults"], + + shared_libs: [ + "android.hardware.media.c2@1.0", + "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", + "libcodec2_hidl_client@1.0", + "libcodec2_hidl_client@1.1", + "libcodec2_hidl_client@1.2", + "libcodec2_vndk", + "libhidlbase", + ], +} + +// Alias to the latest "defaults" for Codec 2.0 HAL service implementations +cc_defaults { + name: "libcodec2-hidl-defaults", + defaults: ["libcodec2-hidl-defaults@1.2"], +} + +// Alias to the latest "defaults" for Codec 2.0 HAL client +cc_defaults { + name: "libcodec2-hidl-client-defaults", + defaults: ["libcodec2-hidl-client-defaults@1.2"], +} + diff --git a/media/codec2/hidl/1.2/utils/Component.cpp b/media/codec2/hidl/1.2/utils/Component.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8924e6d9a938180e857bfcab7a9a34da6a3377a1 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/Component.cpp @@ -0,0 +1,535 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-Component@1.2" +#include + +#include +#include +#include + +#ifndef __ANDROID_APEX__ +#include +#endif + +#include +#include + +#include +#include +#include + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using namespace ::android; + +// ComponentListener wrapper +struct Component::Listener : public C2Component::Listener { + + Listener(const sp& component) : + mComponent(component), + mListener(component->mListener) { + } + + virtual void onError_nb( + std::weak_ptr /* c2component */, + uint32_t errorCode) override { + sp listener = mListener.promote(); + if (listener) { + Return transStatus = listener->onError(Status::OK, errorCode); + if (!transStatus.isOk()) { + LOG(ERROR) << "Component::Listener::onError_nb -- " + << "transaction failed."; + } + } + } + + virtual void onTripped_nb( + std::weak_ptr /* c2component */, + std::vector> c2settingResult + ) override { + sp listener = mListener.promote(); + if (listener) { + hidl_vec settingResults(c2settingResult.size()); + size_t ix = 0; + for (const std::shared_ptr &c2result : + c2settingResult) { + if (c2result) { + if (!objcpy(&settingResults[ix++], *c2result)) { + break; + } + } + } + settingResults.resize(ix); + Return transStatus = listener->onTripped(settingResults); + if (!transStatus.isOk()) { + LOG(ERROR) << "Component::Listener::onTripped_nb -- " + << "transaction failed."; + } + } + } + + virtual void onWorkDone_nb( + std::weak_ptr /* c2component */, + std::list> c2workItems) override { + for (const std::unique_ptr& work : c2workItems) { + if (work) { + if (work->worklets.empty() + || !work->worklets.back() + || (work->worklets.back()->output.flags & + C2FrameData::FLAG_INCOMPLETE) == 0) { + InputBufferManager:: + unregisterFrameData(mListener, work->input); + } + } + } + + sp listener = mListener.promote(); + if (listener) { + WorkBundle workBundle; + + sp strongComponent = mComponent.promote(); + beginTransferBufferQueueBlocks(c2workItems, true); + if (!objcpy(&workBundle, c2workItems, strongComponent ? + &strongComponent->mBufferPoolSender : nullptr)) { + LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " + << "received corrupted work items."; + endTransferBufferQueueBlocks(c2workItems, false, true); + return; + } + Return transStatus = listener->onWorkDone(workBundle); + if (!transStatus.isOk()) { + LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " + << "transaction failed."; + endTransferBufferQueueBlocks(c2workItems, false, true); + return; + } + endTransferBufferQueueBlocks(c2workItems, true, true); + } + } + +protected: + wp mComponent; + wp mListener; +}; + +// Component::Sink +struct Component::Sink : public IInputSink { + std::shared_ptr mComponent; + sp mConfigurable; + + virtual Return queue(const WorkBundle& workBundle) override { + return mComponent->queue(workBundle); + } + + virtual Return> getConfigurable() override { + return mConfigurable; + } + + Sink(const std::shared_ptr& component); + virtual ~Sink() override; + + // Process-wide map: Component::Sink -> C2Component. + static std::mutex sSink2ComponentMutex; + static std::map> sSink2Component; + + static std::shared_ptr findLocalComponent( + const sp& sink); +}; + +std::mutex + Component::Sink::sSink2ComponentMutex{}; +std::map> + Component::Sink::sSink2Component{}; + +Component::Sink::Sink(const std::shared_ptr& component) + : mComponent{component}, + mConfigurable{[&component]() -> sp { + Return> ret1 = component->getInterface(); + if (!ret1.isOk()) { + LOG(ERROR) << "Sink::Sink -- component's transaction failed."; + return nullptr; + } + Return> ret2 = + static_cast>(ret1)-> + getConfigurable(); + if (!ret2.isOk()) { + LOG(ERROR) << "Sink::Sink -- interface's transaction failed."; + return nullptr; + } + return static_cast>(ret2); + }()} { + std::lock_guard lock(sSink2ComponentMutex); + sSink2Component.emplace(this, component->mComponent); +} + +Component::Sink::~Sink() { + std::lock_guard lock(sSink2ComponentMutex); + sSink2Component.erase(this); +} + +std::shared_ptr Component::Sink::findLocalComponent( + const sp& sink) { + std::lock_guard lock(sSink2ComponentMutex); + auto i = sSink2Component.find(sink.get()); + if (i == sSink2Component.end()) { + return nullptr; + } + return i->second.lock(); +} + +// Component +Component::Component( + const std::shared_ptr& component, + const sp& listener, + const sp& store, + 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 + mInit = mInterface->status(); +} + +c2_status_t Component::status() const { + return mInit; +} + +// Methods from ::android::hardware::media::c2::V1_1::IComponent +Return Component::queue(const WorkBundle& workBundle) { + std::list> c2works; + + if (!objcpy(&c2works, workBundle)) { + return Status::CORRUPTED; + } + + // Register input buffers. + for (const std::unique_ptr& work : c2works) { + if (work) { + InputBufferManager:: + registerFrameData(mListener, work->input); + } + } + + return static_cast(mComponent->queue_nb(&c2works)); +} + +Return Component::flush(flush_cb _hidl_cb) { + std::list> c2flushedWorks; + c2_status_t c2res = mComponent->flush_sm( + C2Component::FLUSH_COMPONENT, + &c2flushedWorks); + + // Unregister input buffers. + for (const std::unique_ptr& work : c2flushedWorks) { + if (work) { + if (work->worklets.empty() + || !work->worklets.back() + || (work->worklets.back()->output.flags & + C2FrameData::FLAG_INCOMPLETE) == 0) { + InputBufferManager:: + unregisterFrameData(mListener, work->input); + } + } + } + + WorkBundle flushedWorkBundle; + Status res = static_cast(c2res); + beginTransferBufferQueueBlocks(c2flushedWorks, true); + if (c2res == C2_OK) { + if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { + res = Status::CORRUPTED; + } + } + _hidl_cb(res, flushedWorkBundle); + endTransferBufferQueueBlocks(c2flushedWorks, true, true); + return Void(); +} + +Return Component::drain(bool withEos) { + return static_cast(mComponent->drain_nb(withEos ? + C2Component::DRAIN_COMPONENT_WITH_EOS : + C2Component::DRAIN_COMPONENT_NO_EOS)); +} + +Return Component::setOutputSurface( + uint64_t blockPoolId, + const sp& surface) { + std::shared_ptr pool; + GetCodec2BlockPool(blockPoolId, mComponent, &pool); + if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { + std::shared_ptr bqPool = + std::static_pointer_cast(pool); + C2BufferQueueBlockPool::OnRenderCallback cb = + [this](uint64_t producer, int32_t slot, int64_t nsecs) { + // TODO: batch this + hidl_vec rendered; + rendered.resize(1); + rendered[0] = { producer, slot, nsecs }; + (void)mListener->onFramesRendered(rendered).isOk(); + }; + if (bqPool) { + bqPool->setRenderCallback(cb); + bqPool->configureProducer(surface); + } + } + return Status::OK; +} + +Return Component::connectToInputSurface( + const sp& inputSurface, + connectToInputSurface_cb _hidl_cb) { + Status status; + sp connection; + auto transStatus = inputSurface->connect( + asInputSink(), + [&status, &connection]( + Status s, const sp& c) { + status = s; + connection = c; + } + ); + _hidl_cb(status, connection); + return Void(); +} + +Return Component::connectToOmxInputSurface( + const sp& producer, + const sp<::android::hardware::media::omx::V1_0:: + IGraphicBufferSource>& source, + connectToOmxInputSurface_cb _hidl_cb) { + (void)producer; + (void)source; + (void)_hidl_cb; + return Void(); +} + +Return Component::disconnectFromInputSurface() { + // TODO implement + return Status::OK; +} + +namespace /* unnamed */ { + +struct BlockPoolIntf : public ConfigurableC2Intf { + BlockPoolIntf(const std::shared_ptr& pool) + : ConfigurableC2Intf{ + "C2BlockPool:" + + (pool ? std::to_string(pool->getLocalId()) : "null"), + 0}, + mPool{pool} { + } + + virtual c2_status_t config( + const std::vector& params, + c2_blocking_t mayBlock, + std::vector>* const failures + ) override { + (void)params; + (void)mayBlock; + (void)failures; + return C2_OK; + } + + virtual c2_status_t query( + const std::vector& indices, + c2_blocking_t mayBlock, + std::vector>* const params + ) const override { + (void)indices; + (void)mayBlock; + (void)params; + return C2_OK; + } + + virtual c2_status_t querySupportedParams( + std::vector>* const params + ) const override { + (void)params; + return C2_OK; + } + + virtual c2_status_t querySupportedValues( + std::vector& fields, + c2_blocking_t mayBlock) const override { + (void)fields; + (void)mayBlock; + return C2_OK; + } + +protected: + std::shared_ptr mPool; +}; + +} // unnamed namespace + +Return Component::createBlockPool( + uint32_t allocatorId, + createBlockPool_cb _hidl_cb) { + std::shared_ptr blockPool; +#ifdef __ANDROID_APEX__ + c2_status_t status = CreateCodec2BlockPool( + static_cast(allocatorId), + mComponent, + &blockPool); +#else + c2_status_t status = ComponentStore::GetFilterWrapper()->createBlockPool( + static_cast(allocatorId), + mComponent, + &blockPool); +#endif + if (status != C2_OK) { + blockPool = nullptr; + } + if (blockPool) { + mBlockPoolsMutex.lock(); + mBlockPools.emplace(blockPool->getLocalId(), blockPool); + mBlockPoolsMutex.unlock(); + } else if (status == C2_OK) { + status = C2_CORRUPTED; + } + + _hidl_cb(static_cast(status), + blockPool ? blockPool->getLocalId() : 0, + new CachedConfigurable( + std::make_unique(blockPool))); + return Void(); +} + +Return Component::destroyBlockPool(uint64_t blockPoolId) { + std::lock_guard lock(mBlockPoolsMutex); + return mBlockPools.erase(blockPoolId) == 1 ? + Status::OK : Status::CORRUPTED; +} + +Return Component::start() { + return static_cast(mComponent->start()); +} + +Return Component::stop() { + InputBufferManager::unregisterFrameData(mListener); + return static_cast(mComponent->stop()); +} + +Return Component::reset() { + Status status = static_cast(mComponent->reset()); + { + std::lock_guard lock(mBlockPoolsMutex); + mBlockPools.clear(); + } + InputBufferManager::unregisterFrameData(mListener); + return status; +} + +Return Component::release() { + Status status = static_cast(mComponent->release()); + { + std::lock_guard lock(mBlockPoolsMutex); + mBlockPools.clear(); + } + InputBufferManager::unregisterFrameData(mListener); + return status; +} + +Return> Component::getInterface() { + return sp(mInterface); +} + +Return> Component::asInputSink() { + std::lock_guard lock(mSinkMutex); + if (!mSink) { + mSink = new Sink(shared_from_this()); + } + return {mSink}; +} + +Return Component::configureVideoTunnel( + uint32_t avSyncHwId, configureVideoTunnel_cb _hidl_cb) { + (void)avSyncHwId; + _hidl_cb(Status::OMITTED, hidl_handle{}); + return Void(); +} + +Return Component::setOutputSurfaceWithSyncObj( + uint64_t blockPoolId, const sp& surface, + const SurfaceSyncObj& syncObject) { + std::shared_ptr pool; + GetCodec2BlockPool(blockPoolId, mComponent, &pool); + if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { + std::shared_ptr bqPool = + std::static_pointer_cast(pool); + C2BufferQueueBlockPool::OnRenderCallback cb = + [this](uint64_t producer, int32_t slot, int64_t nsecs) { + // TODO: batch this + hidl_vec rendered; + rendered.resize(1); + rendered[0] = { producer, slot, nsecs }; + (void)mListener->onFramesRendered(rendered).isOk(); + }; + if (bqPool) { + const native_handle_t *h = syncObject.syncMemory; + native_handle_t *syncMemory = h ? native_handle_clone(h) : nullptr; + uint64_t bqId = syncObject.bqId; + uint32_t generationId = syncObject.generationId; + uint64_t consumerUsage = syncObject.consumerUsage; + + bqPool->setRenderCallback(cb); + bqPool->configureProducer(surface, syncMemory, bqId, + generationId, consumerUsage); + } + } + return Status::OK; +} + +std::shared_ptr Component::findLocalComponent( + const sp& sink) { + return Component::Sink::findLocalComponent(sink); +} + +void Component::initListener(const sp& self) { + std::shared_ptr c2listener = + std::make_shared(self); + c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK); + if (res != C2_OK) { + mInit = res; + } +} + +Component::~Component() { + InputBufferManager::unregisterFrameData(mListener); + mStore->reportComponentDeath(this); +} + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl b/media/codec2/hidl/1.2/utils/ComponentInterface.cpp similarity index 79% rename from media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl rename to media/codec2/hidl/1.2/utils/ComponentInterface.cpp index f6a22b86139fca1829b3a0b17df7fe279ce98408..30fe4d6cdfa484c059e403905528ddeda5266bde 100644 --- a/media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl +++ b/media/codec2/hidl/1.2/utils/ComponentInterface.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,4 @@ * limitations under the License. */ -package android.media.VolumeShaper; - -parcelable State cpp_header "media/VolumeShaper.h"; +#include diff --git a/media/codec2/hidl/1.2/utils/ComponentStore.cpp b/media/codec2/hidl/1.2/utils/ComponentStore.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9fac5d52687ca45a84d8d8faea9463b468a08f2b --- /dev/null +++ b/media/codec2/hidl/1.2/utils/ComponentStore.cpp @@ -0,0 +1,562 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-ComponentStore@1.2" +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef __ANDROID_APEX__ +#include +#include +#include +#include +#include +#endif + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using namespace ::android; +using ::android::GraphicBufferSource; +using namespace ::android::hardware::media::bufferpool::V2_0::implementation; + +namespace /* unnamed */ { + +struct StoreIntf : public ConfigurableC2Intf { + StoreIntf(const std::shared_ptr& store) + : ConfigurableC2Intf{store ? store->getName() : "", 0}, + mStore{store} { + } + + virtual c2_status_t config( + const std::vector ¶ms, + c2_blocking_t mayBlock, + std::vector> *const failures + ) override { + // Assume all params are blocking + // TODO: Filter for supported params + if (mayBlock == C2_DONT_BLOCK && params.size() != 0) { + return C2_BLOCKING; + } + return mStore->config_sm(params, failures); + } + + virtual c2_status_t query( + const std::vector &indices, + c2_blocking_t mayBlock, + std::vector> *const params) const override { + // Assume all params are blocking + // TODO: Filter for supported params + if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) { + return C2_BLOCKING; + } + return mStore->query_sm({}, indices, params); + } + + virtual c2_status_t querySupportedParams( + std::vector> *const params + ) const override { + return mStore->querySupportedParams_nb(params); + } + + virtual c2_status_t querySupportedValues( + std::vector &fields, + c2_blocking_t mayBlock) const override { + // Assume all params are blocking + // TODO: Filter for supported params + if (mayBlock == C2_DONT_BLOCK && fields.size() != 0) { + return C2_BLOCKING; + } + return mStore->querySupportedValues_sm(fields); + } + +protected: + std::shared_ptr mStore; +}; + +} // unnamed namespace + +struct ComponentStore::StoreParameterCache : public ParameterCache { + std::mutex mStoreMutex; + ComponentStore* mStore; + + StoreParameterCache(ComponentStore* store): mStore{store} { + } + + virtual c2_status_t validate( + const std::vector>& params + ) override { + std::scoped_lock _lock(mStoreMutex); + return mStore ? mStore->validateSupportedParams(params) : C2_NO_INIT; + } + + void onStoreDestroyed() { + std::scoped_lock _lock(mStoreMutex); + mStore = nullptr; + } +}; + +ComponentStore::ComponentStore(const std::shared_ptr& store) + : mConfigurable{new CachedConfigurable(std::make_unique(store))}, + mParameterCache{std::make_shared(this)}, + mStore{store} { + + std::shared_ptr platformStore = android::GetCodec2PlatformComponentStore(); + SetPreferredCodec2ComponentStore(store); + + // Retrieve struct descriptors + mParamReflector = mStore->getParamReflector(); + + // Retrieve supported parameters from store + using namespace std::placeholders; + mInit = mConfigurable->init(mParameterCache); +} + +ComponentStore::~ComponentStore() { + mParameterCache->onStoreDestroyed(); +} + +c2_status_t ComponentStore::status() const { + return mInit; +} + +c2_status_t ComponentStore::validateSupportedParams( + const std::vector>& params) { + c2_status_t res = C2_OK; + + for (const std::shared_ptr &desc : params) { + if (!desc) { + // All descriptors should be valid + res = res ? res : C2_BAD_VALUE; + continue; + } + C2Param::CoreIndex coreIndex = desc->index().coreIndex(); + std::lock_guard lock(mStructDescriptorsMutex); + auto it = mStructDescriptors.find(coreIndex); + if (it == mStructDescriptors.end()) { + std::shared_ptr structDesc = + mParamReflector->describe(coreIndex); + if (!structDesc) { + // All supported params must be described + res = C2_BAD_INDEX; + } + mStructDescriptors.insert({ coreIndex, structDesc }); + } + } + return res; +} + +std::shared_ptr ComponentStore::getParameterCache() const { + return mParameterCache; +} + +#ifndef __ANDROID_APEX__ +// static +std::shared_ptr ComponentStore::GetFilterWrapper() { + constexpr const char kPluginPath[] = "libc2filterplugin.so"; + static std::shared_ptr wrapper = FilterWrapper::Create( + std::make_unique(kPluginPath)); + return wrapper; +} +#endif + +// Methods from ::android::hardware::media::c2::V1_0::IComponentStore +Return ComponentStore::createComponent( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_cb _hidl_cb) { + + sp component; + std::shared_ptr c2component; + Status status = static_cast( + mStore->createComponent(name, &c2component)); + + if (status == Status::OK) { +#ifndef __ANDROID_APEX__ + c2component = GetFilterWrapper()->maybeWrapComponent(c2component); +#endif + onInterfaceLoaded(c2component->intf()); + component = new Component(c2component, listener, this, pool); + if (!component) { + status = Status::CORRUPTED; + } else { + reportComponentBirth(component.get()); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } else { + component->initListener(component); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } + } + } + } + _hidl_cb(status, component); + return Void(); +} + +Return ComponentStore::createInterface( + const hidl_string& name, + createInterface_cb _hidl_cb) { + std::shared_ptr c2interface; + c2_status_t res = mStore->createInterface(name, &c2interface); + sp interface; + if (res == C2_OK) { +#ifndef __ANDROID_APEX__ + c2interface = GetFilterWrapper()->maybeWrapInterface(c2interface); +#endif + onInterfaceLoaded(c2interface); + interface = new ComponentInterface(c2interface, mParameterCache); + } + _hidl_cb(static_cast(res), interface); + return Void(); +} + +Return ComponentStore::listComponents(listComponents_cb _hidl_cb) { + std::vector> c2traits = + mStore->listComponents(); + hidl_vec traits(c2traits.size()); + size_t ix = 0; + for (const std::shared_ptr &c2trait : c2traits) { + if (c2trait) { + if (objcpy(&traits[ix], *c2trait)) { + ++ix; + } else { + break; + } + } + } + traits.resize(ix); + _hidl_cb(Status::OK, traits); + return Void(); +} + +Return ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) { + sp source = new GraphicBufferSource(); + if (source->initCheck() != OK) { + _hidl_cb(Status::CORRUPTED, nullptr); + return Void(); + } + using namespace std::placeholders; + sp inputSurface = new InputSurface( + mParameterCache, + std::make_shared(), + source->getHGraphicBufferProducer(), + source); + _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY, + inputSurface); + return Void(); +} + +void ComponentStore::onInterfaceLoaded(const std::shared_ptr &intf) { + // invalidate unsupported struct descriptors if a new interface is loaded as it may have + // exposed new descriptors + std::lock_guard lock(mStructDescriptorsMutex); + if (!mLoadedInterfaces.count(intf->getName())) { + mUnsupportedStructDescriptors.clear(); + mLoadedInterfaces.emplace(intf->getName()); + } +} + +Return ComponentStore::getStructDescriptors( + const hidl_vec& indices, + getStructDescriptors_cb _hidl_cb) { + hidl_vec descriptors(indices.size()); + size_t dstIx = 0; + Status res = Status::OK; + for (size_t srcIx = 0; srcIx < indices.size(); ++srcIx) { + std::lock_guard lock(mStructDescriptorsMutex); + const C2Param::CoreIndex coreIndex = C2Param::CoreIndex(indices[srcIx]).coreIndex(); + const auto item = mStructDescriptors.find(coreIndex); + if (item == mStructDescriptors.end()) { + // not in the cache, and not known to be unsupported, query local reflector + if (!mUnsupportedStructDescriptors.count(coreIndex)) { + std::shared_ptr structDesc = + mParamReflector->describe(coreIndex); + if (!structDesc) { + mUnsupportedStructDescriptors.emplace(coreIndex); + } else { + mStructDescriptors.insert({ coreIndex, structDesc }); + if (objcpy(&descriptors[dstIx], *structDesc)) { + ++dstIx; + continue; + } + res = Status::CORRUPTED; + break; + } + } + res = Status::NOT_FOUND; + } else if (item->second) { + if (objcpy(&descriptors[dstIx], *item->second)) { + ++dstIx; + continue; + } + res = Status::CORRUPTED; + break; + } else { + res = Status::NO_MEMORY; + break; + } + } + descriptors.resize(dstIx); + _hidl_cb(res, descriptors); + return Void(); +} + +Return> ComponentStore::getPoolClientManager() { + return ClientManager::getInstance(); +} + +Return ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) { + // TODO implement + (void)src; + (void)dst; + return Status::OMITTED; +} + +Return> ComponentStore::getConfigurable() { + return mConfigurable; +} + +// Methods from ::android::hardware::media::c2::V1_1::IComponentStore +Return ComponentStore::createComponent_1_1( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_1_1_cb _hidl_cb) { + + sp component; + std::shared_ptr c2component; + Status status = static_cast( + mStore->createComponent(name, &c2component)); + + if (status == Status::OK) { +#ifndef __ANDROID_APEX__ + c2component = GetFilterWrapper()->maybeWrapComponent(c2component); +#endif + onInterfaceLoaded(c2component->intf()); + component = new Component(c2component, listener, this, pool); + if (!component) { + status = Status::CORRUPTED; + } else { + reportComponentBirth(component.get()); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } else { + component->initListener(component); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } + } + } + } + _hidl_cb(status, component); + return Void(); +} + +// Methods from ::android::hardware::media::c2::V1_2::IComponentStore +Return ComponentStore::createComponent_1_2( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_1_2_cb _hidl_cb) { + + sp component; + std::shared_ptr c2component; + Status status = static_cast( + mStore->createComponent(name, &c2component)); + + if (status == Status::OK) { +#ifndef __ANDROID_APEX__ + c2component = GetFilterWrapper()->maybeWrapComponent(c2component); +#endif + onInterfaceLoaded(c2component->intf()); + component = new Component(c2component, listener, this, pool); + if (!component) { + status = Status::CORRUPTED; + } else { + reportComponentBirth(component.get()); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } else { + component->initListener(component); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } + } + } + } + _hidl_cb(status, component); + return Void(); +} + +// Called from createComponent() after a successful creation of `component`. +void ComponentStore::reportComponentBirth(Component* component) { + ComponentStatus componentStatus; + componentStatus.c2Component = component->mComponent; + componentStatus.birthTime = std::chrono::system_clock::now(); + + std::lock_guard lock(mComponentRosterMutex); + mComponentRoster.emplace(component, componentStatus); +} + +// Called from within the destructor of `component`. No virtual function calls +// are made on `component` here. +void ComponentStore::reportComponentDeath(Component* component) { + std::lock_guard lock(mComponentRosterMutex); + mComponentRoster.erase(component); +} + +// Dumps component traits. +std::ostream& ComponentStore::dump( + std::ostream& out, + const std::shared_ptr& comp) { + + constexpr const char indent[] = " "; + + out << indent << "name: " << comp->name << std::endl; + out << indent << "domain: " << comp->domain << std::endl; + out << indent << "kind: " << comp->kind << std::endl; + out << indent << "rank: " << comp->rank << std::endl; + out << indent << "mediaType: " << comp->mediaType << std::endl; + out << indent << "aliases:"; + for (const auto& alias : comp->aliases) { + out << ' ' << alias; + } + out << std::endl; + + return out; +} + +// Dumps component status. +std::ostream& ComponentStore::dump( + std::ostream& out, + ComponentStatus& compStatus) { + + constexpr const char indent[] = " "; + + // Print birth time. + std::chrono::milliseconds ms = + std::chrono::duration_cast( + compStatus.birthTime.time_since_epoch()); + std::time_t birthTime = std::chrono::system_clock::to_time_t( + compStatus.birthTime); + std::tm tm = *std::localtime(&birthTime); + out << indent << "Creation time: " + << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") + << '.' << std::setfill('0') << std::setw(3) << ms.count() % 1000 + << std::endl; + + // Print name and id. + std::shared_ptr intf = compStatus.c2Component->intf(); + if (!intf) { + out << indent << "Unknown component -- null interface" << std::endl; + return out; + } + out << indent << "Name: " << intf->getName() << std::endl; + out << indent << "Id: " << intf->getId() << std::endl; + + return out; +} + +// Dumps information when lshal is called. +Return ComponentStore::debug( + const hidl_handle& handle, + const hidl_vec& /* args */) { + LOG(INFO) << "debug -- dumping..."; + const native_handle_t *h = handle.getNativeHandle(); + if (!h || h->numFds != 1) { + LOG(ERROR) << "debug -- dumping failed -- " + "invalid file descriptor to dump to"; + return Void(); + } + std::ostringstream out; + + { // Populate "out". + + constexpr const char indent[] = " "; + + // Show name. + out << "Beginning of dump -- C2ComponentStore: " + << mStore->getName() << std::endl << std::endl; + + // Retrieve the list of supported components. + std::vector> traitsList = + mStore->listComponents(); + + // Dump the traits of supported components. + out << indent << "Supported components:" << std::endl << std::endl; + if (traitsList.size() == 0) { + out << indent << indent << "NONE" << std::endl << std::endl; + } else { + for (const auto& traits : traitsList) { + dump(out, traits) << std::endl; + } + } + + // Dump active components. + { + out << indent << "Active components:" << std::endl << std::endl; + std::lock_guard lock(mComponentRosterMutex); + if (mComponentRoster.size() == 0) { + out << indent << indent << "NONE" << std::endl << std::endl; + } else { + for (auto& pair : mComponentRoster) { + dump(out, pair.second) << std::endl; + } + } + } + + out << "End of dump -- C2ComponentStore: " + << mStore->getName() << std::endl; + } + + if (!android::base::WriteStringToFd(out.str(), h->data[0])) { + PLOG(WARNING) << "debug -- dumping failed -- write()"; + } else { + LOG(INFO) << "debug -- dumping succeeded"; + } + return Void(); +} + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.2/utils/Configurable.cpp similarity index 85% rename from media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp rename to media/codec2/hidl/1.2/utils/Configurable.cpp index 65756e8cde12cfce292007b3def97fa11f75e44c..243870e29d5114fd349320e77ef3025eecaf38d1 100644 --- a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp +++ b/media/codec2/hidl/1.2/utils/Configurable.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,4 +14,4 @@ * limitations under the License. */ -#include +#include diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl b/media/codec2/hidl/1.2/utils/InputBufferManager.cpp similarity index 79% rename from media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl rename to media/codec2/hidl/1.2/utils/InputBufferManager.cpp index 4290d9d947c17e01d138b565c21f224ec05924c3..11200757c2cd59ad64c029ab8528caeaf6590053 100644 --- a/media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl +++ b/media/codec2/hidl/1.2/utils/InputBufferManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,4 @@ * limitations under the License. */ -package android.media.VolumeShaper; - -parcelable Operation cpp_header "media/VolumeShaper.h"; +#include diff --git a/media/codec2/hidl/1.2/utils/InputSurface.cpp b/media/codec2/hidl/1.2/utils/InputSurface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c4d28b76d494d098c5bc6030ffec26ebf8a6c65 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/InputSurface.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include diff --git a/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1bd58c2eaf617c7707f97501915538b28ef79352 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h new file mode 100644 index 0000000000000000000000000000000000000000..7937664b21801b9eb63dd980c8f0b2b0cf419514 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h @@ -0,0 +1,154 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_H +#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { + +using ::android::hardware::media::c2::V1_2::IComponent; +using ::android::hardware::media::c2::V1_0::IComponentListener; + +namespace utils { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::IBinder; +using ::android::sp; +using ::android::wp; + +struct ComponentStore; + +struct Component : public IComponent, + public std::enable_shared_from_this { + Component( + const std::shared_ptr&, + const sp& listener, + const sp& store, + const sp<::android::hardware::media::bufferpool::V2_0:: + IClientManager>& clientPoolManager); + c2_status_t status() const; + + typedef ::android::hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer HGraphicBufferProducer1; + typedef ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer HGraphicBufferProducer2; + + // Methods from IComponent follow. + virtual Return queue(const WorkBundle& workBundle) override; + virtual Return flush(flush_cb _hidl_cb) override; + virtual Return drain(bool withEos) override; + virtual Return setOutputSurface( + uint64_t blockPoolId, + const sp& surface) override; + virtual Return connectToInputSurface( + const sp& inputSurface, + connectToInputSurface_cb _hidl_cb) override; + virtual Return connectToOmxInputSurface( + const sp& producer, + const sp<::android::hardware::media::omx::V1_0:: + IGraphicBufferSource>& source, + connectToOmxInputSurface_cb _hidl_cb) override; + virtual Return disconnectFromInputSurface() override; + virtual Return createBlockPool( + uint32_t allocatorId, + createBlockPool_cb _hidl_cb) override; + virtual Return destroyBlockPool(uint64_t blockPoolId) override; + virtual Return start() override; + virtual Return stop() override; + virtual Return reset() override; + virtual Return release() override; + virtual Return> getInterface() override; + virtual Return> asInputSink() override; + virtual Return configureVideoTunnel( + uint32_t avSyncHwId, configureVideoTunnel_cb _hidl_cb) override; + virtual Return setOutputSurfaceWithSyncObj( + uint64_t blockPoolId, + const sp& surface, + const SurfaceSyncObj& syncObject) override; + + + // Returns a C2Component associated to the given sink if the sink is indeed + // a local component. Returns nullptr otherwise. + // + // This function is used by InputSurface::connect(). + static std::shared_ptr findLocalComponent( + const sp& sink); + +protected: + c2_status_t mInit; + std::shared_ptr mComponent; + sp mInterface; + sp mListener; + sp mStore; + ::android::hardware::media::c2::V1_2::utils::DefaultBufferPoolSender + mBufferPoolSender; + + struct Sink; + std::mutex mSinkMutex; + sp mSink; + + std::mutex mBlockPoolsMutex; + // This map keeps C2BlockPool objects that are created by createBlockPool() + // alive. These C2BlockPool objects can be deleted by calling + // destroyBlockPool(), reset() or release(), or by destroying the component. + std::map> mBlockPools; + + void initListener(const sp& self); + + virtual ~Component() override; + + friend struct ComponentStore; + + struct Listener; +}; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..09d9f93d77d95519eaea4c7f6226e326f19f8e56 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H +#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using ::android::hardware::media::c2::V1_0::utils::ComponentInterface; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h new file mode 100644 index 0000000000000000000000000000000000000000..e95a651756dcd71b48b9afff02a935baa0a9b875 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h @@ -0,0 +1,177 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H +#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace android { +class FilterWrapper; + +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using ::android::hardware::media::bufferpool::V2_0::IClientManager; + +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct ComponentStore : public IComponentStore { + ComponentStore(const std::shared_ptr& store); + virtual ~ComponentStore(); + + /** + * Returns the status of the construction of this object. + */ + c2_status_t status() const; + + /** + * This function is called by CachedConfigurable::init() to validate + * supported parameters. + */ + c2_status_t validateSupportedParams( + const std::vector>& params); + + /** + * Returns the store's ParameterCache. This is used for validation by + * Configurable::init(). + */ + std::shared_ptr getParameterCache() const; + + static std::shared_ptr GetFilterWrapper(); + + // Methods from ::android::hardware::media::c2::V1_0::IComponentStore. + virtual Return createComponent( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_cb _hidl_cb) override; + virtual Return createInterface( + const hidl_string& name, + createInterface_cb _hidl_cb) override; + virtual Return listComponents(listComponents_cb _hidl_cb) override; + virtual Return createInputSurface( + createInputSurface_cb _hidl_cb) override; + virtual Return getStructDescriptors( + const hidl_vec& indices, + getStructDescriptors_cb _hidl_cb) override; + virtual Return> getPoolClientManager() override; + virtual Return copyBuffer( + const Buffer& src, + const Buffer& dst) override; + virtual Return> getConfigurable() override; + + // Methods from ::android::hardware::media::c2::V1_1::IComponentStore. + virtual Return createComponent_1_1( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_1_1_cb _hidl_cb) override; + + // Methods from ::android::hardware::media::c2::V1_2::IComponentStore. + virtual Return createComponent_1_2( + const hidl_string& name, + const sp& listener, + const sp& pool, + createComponent_1_2_cb _hidl_cb) override; + + /** + * Dumps information when lshal is called. + */ + virtual Return debug( + const hidl_handle& handle, + const hidl_vec& args) override; + +protected: + sp mConfigurable; + struct StoreParameterCache; + std::shared_ptr mParameterCache; + + // Does bookkeeping for an interface that has been loaded. + void onInterfaceLoaded(const std::shared_ptr &intf); + + c2_status_t mInit; + std::shared_ptr mStore; + std::shared_ptr mParamReflector; + + std::map> mStructDescriptors; + std::set mUnsupportedStructDescriptors; + std::set mLoadedInterfaces; + mutable std::mutex mStructDescriptorsMutex; + + // ComponentStore keeps track of live Components. + + struct ComponentStatus { + std::shared_ptr c2Component; + std::chrono::system_clock::time_point birthTime; + }; + + mutable std::mutex mComponentRosterMutex; + std::map mComponentRoster; + + // Called whenever Component is created. + void reportComponentBirth(Component* component); + // Called only from the destructor of Component. + void reportComponentDeath(Component* component); + + friend Component; + + // Helper functions for dumping. + + std::ostream& dump( + std::ostream& out, + const std::shared_ptr& comp); + + std::ostream& dump( + std::ostream& out, + ComponentStatus& compStatus); + +}; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h new file mode 100644 index 0000000000000000000000000000000000000000..2efad3179752da8fefcce08d2dcf3ddf78bbd03a --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h @@ -0,0 +1,41 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H +#define CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using ::android::hardware::media::c2::V1_0::utils::ConfigurableC2Intf; +using ::android::hardware::media::c2::V1_0::utils::ParameterCache; +using ::android::hardware::media::c2::V1_0::utils::CachedConfigurable; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h new file mode 100644 index 0000000000000000000000000000000000000000..e4a5db41f4bab0b1c9ccb6a67a138906074c7de4 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H +#define CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using ::android::hardware::media::c2::V1_0::utils::InputBufferManager; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h similarity index 66% rename from media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h rename to media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h index f77852d66fc0a5bfeecc193968557dcfab94bcd4..3fae86b4fd808ad815874de4ce3af50a4fc67065 100644 --- a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,26 @@ * limitations under the License. */ -#ifndef CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE -#define CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE +#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H +#define CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H -#include -#include +#include +#include namespace android { namespace hardware { namespace media { namespace c2 { -namespace V1_1 { +namespace V1_2 { namespace utils { -using ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue; +using ::android::hardware::media::c2::V1_0::utils::InputSurface; } // namespace utils -} // namespace V1_1 +} // namespace V1_2 } // namespace c2 } // namespace media } // namespace hardware } // namespace android -#endif // CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE +#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h new file mode 100644 index 0000000000000000000000000000000000000000..13a8a61845dd2315edcbeffbf2f1086ecb0be7cd --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H +#define CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { +namespace utils { + +using ::android::hardware::media::c2::V1_0::utils::InputSurfaceConnection; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h new file mode 100644 index 0000000000000000000000000000000000000000..d3180b0eeabb7d788f9f09e1cc5ffc9a6b49f399 --- /dev/null +++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h @@ -0,0 +1,105 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_2_UTILS_TYPES_H +#define CODEC2_HIDL_V1_2_UTILS_TYPES_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_2 { + +using ::android::hardware::media::c2::V1_0::BaseBlock; +using ::android::hardware::media::c2::V1_0::Block; +using ::android::hardware::media::c2::V1_0::Buffer; +using ::android::hardware::media::c2::V1_0::FieldDescriptor; +using ::android::hardware::media::c2::V1_0::FieldId; +using ::android::hardware::media::c2::V1_0::FieldSupportedValues; +using ::android::hardware::media::c2::V1_0::FieldSupportedValuesQuery; +using ::android::hardware::media::c2::V1_0::FieldSupportedValuesQueryResult; +using ::android::hardware::media::c2::V1_0::FrameData; +using ::android::hardware::media::c2::V1_0::InfoBuffer; +using ::android::hardware::media::c2::V1_0::ParamDescriptor; +using ::android::hardware::media::c2::V1_0::ParamField; +using ::android::hardware::media::c2::V1_0::ParamFieldValues; +using ::android::hardware::media::c2::V1_0::ParamIndex; +using ::android::hardware::media::c2::V1_0::Params; +using ::android::hardware::media::c2::V1_0::PrimitiveValue; +using ::android::hardware::media::c2::V1_0::SettingResult; +using ::android::hardware::media::c2::V1_0::Status; +using ::android::hardware::media::c2::V1_0::StructDescriptor; +using ::android::hardware::media::c2::V1_0::ValueRange; +using ::android::hardware::media::c2::V1_0::Work; +using ::android::hardware::media::c2::V1_0::WorkBundle; +using ::android::hardware::media::c2::V1_0::WorkOrdinal; +using ::android::hardware::media::c2::V1_0::Worklet; + +using ::android::hardware::media::c2::V1_2::SurfaceSyncObj; + +using ::android::hardware::media::c2::V1_0::IComponentInterface; +using ::android::hardware::media::c2::V1_0::IComponentListener; +using ::android::hardware::media::c2::V1_0::IConfigurable; +using ::android::hardware::media::c2::V1_0::IInputSink; +using ::android::hardware::media::c2::V1_0::IInputSurface; +using ::android::hardware::media::c2::V1_0::IInputSurfaceConnection; + +namespace utils { + +using ::android::hardware::media::c2::V1_0::utils::toC2Status; + +using ::android::hardware::media::c2::V1_0::utils::C2Hidl_Range; +using ::android::hardware::media::c2::V1_0::utils::C2Hidl_RangeInfo; +using ::android::hardware::media::c2::V1_0::utils::C2Hidl_Rect; +using ::android::hardware::media::c2::V1_0::utils::C2Hidl_RectInfo; + +using ::android::hardware::media::c2::V1_0::utils::objcpy; +using ::android::hardware::media::c2::V1_0::utils::parseParamsBlob; +using ::android::hardware::media::c2::V1_0::utils::createParamsBlob; +using ::android::hardware::media::c2::V1_0::utils::copyParamsFromBlob; +using ::android::hardware::media::c2::V1_0::utils::updateParamsFromBlob; + +using ::android::hardware::media::c2::V1_0::utils::BufferPoolSender; +using ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender; + +using ::android::hardware::media::c2::V1_0::utils::beginTransferBufferQueueBlock; +using ::android::hardware::media::c2::V1_0::utils::beginTransferBufferQueueBlocks; +using ::android::hardware::media::c2::V1_0::utils::endTransferBufferQueueBlock; +using ::android::hardware::media::c2::V1_0::utils::endTransferBufferQueueBlocks; +using ::android::hardware::media::c2::V1_0::utils::displayBufferQueueBlock; + +using ::android::hardware::media::c2::V1_0::utils::operator<<; + +} // namespace utils +} // namespace V1_2 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_2_UTILS_TYPES_H diff --git a/media/codec2/hidl/1.2/utils/types.cpp b/media/codec2/hidl/1.2/utils/types.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e0a08b7fba409e2a28f11173c93cde666efe5dc --- /dev/null +++ b/media/codec2/hidl/1.2/utils/types.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index 5a34c302d5c8ddb92eb186b051282e5e220c1409..0e52813d4e6e2ce2832199cd11bf630210254427 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -12,6 +12,11 @@ cc_library { srcs: [ "client.cpp", + "output.cpp", + ], + + header_libs: [ + "libcodec2_internal", // private ], shared_libs: [ @@ -19,11 +24,13 @@ cc_library { "android.hardware.media.bufferpool@2.0", "android.hardware.media.c2@1.0", "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", "libbase", "libbinder", "libcodec2", "libcodec2_hidl_client@1.0", "libcodec2_hidl_client@1.1", + "libcodec2_hidl_client@1.2", "libcodec2_vndk", "libcutils", "libgui", @@ -41,9 +48,11 @@ cc_library { export_shared_lib_headers: [ "android.hardware.media.c2@1.0", "android.hardware.media.c2@1.1", + "android.hardware.media.c2@1.2", "libcodec2", "libcodec2_hidl_client@1.0", "libcodec2_hidl_client@1.1", + "libcodec2_hidl_client@1.2", "libcodec2_vndk", ], diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 465067263153a4b42af1ff1d2fd1c5e9748056d4..42b3c4334d4433979d6548e3392278615b81fb54 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -21,6 +21,7 @@ #include #include #include +#include // for C2StreamUsageTuning #include #include @@ -33,15 +34,19 @@ #include #include -#include #include -#include #include +#include +#include #include #include #include +#include // for GRALLOC_USAGE_* #include +#include // for NATIVE_WINDOW_QUERY_* +#include // for asString(status_t) + #include #include @@ -73,12 +78,17 @@ using B2HGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: V2_0::utils::B2HGraphicBufferProducer; using H2BGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: V2_0::utils::H2BGraphicBufferProducer; +using ::android::hardware::media::c2::V1_2::SurfaceSyncObj; namespace /* unnamed */ { // c2_status_t value that corresponds to hwbinder transaction failure. constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED; +// By default prepare buffer to be displayed on any of the common surfaces +constexpr uint64_t kDefaultConsumerUsage = + (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER); + // Searches for a name in GetServiceNames() and returns the index found. If the // name is not found, the returned index will be equal to // GetServiceNames().size(). @@ -592,9 +602,9 @@ struct Codec2Client::Component::BufferPoolSender : // Codec2Client::Component::OutputBufferQueue struct Codec2Client::Component::OutputBufferQueue : - hardware::media::c2::V1_1::utils::OutputBufferQueue { + hardware::media::c2::OutputBufferQueue { OutputBufferQueue() - : hardware::media::c2::V1_1::utils::OutputBufferQueue() { + : hardware::media::c2::OutputBufferQueue() { } }; @@ -612,6 +622,7 @@ Codec2Client::Codec2Client(sp const& base, }, mBase1_0{base}, mBase1_1{Base1_1::castFrom(base)}, + mBase1_2{Base1_2::castFrom(base)}, mServiceIndex{serviceIndex} { Return> transResult = base->getPoolClientManager(); if (!transResult.isOk()) { @@ -633,6 +644,10 @@ sp const& Codec2Client::getBase1_1() const { return mBase1_1; } +sp const& Codec2Client::getBase1_2() const { + return mBase1_2; +} + std::string const& Codec2Client::getServiceName() const { return GetServiceNames()[mServiceIndex]; } @@ -645,8 +660,9 @@ c2_status_t Codec2Client::createComponent( c2_status_t status; sp hidlListener = new Component::HidlListener{}; hidlListener->base = listener; - Return transStatus = mBase1_1 ? - mBase1_1->createComponent_1_1( + Return transStatus; + if (mBase1_2) { + transStatus = mBase1_2->createComponent_1_2( name, hidlListener, ClientManager::getInstance(), @@ -659,8 +675,25 @@ c2_status_t Codec2Client::createComponent( } *component = std::make_shared(c); hidlListener->component = *component; - }) : - mBase1_0->createComponent( + }); + } + else if (mBase1_1) { + transStatus = mBase1_1->createComponent_1_1( + name, + hidlListener, + ClientManager::getInstance(), + [&status, component, hidlListener]( + Status s, + const sp& c) { + status = static_cast(s); + if (status != C2_OK) { + return; + } + *component = std::make_shared(c); + hidlListener->component = *component; + }); + } else if (mBase1_0) { // ver1_0 + transStatus = mBase1_0->createComponent( name, hidlListener, ClientManager::getInstance(), @@ -674,6 +707,9 @@ c2_status_t Codec2Client::createComponent( *component = std::make_shared(c); hidlListener->component = *component; }); + } else { + status = C2_CORRUPTED; + } if (!transStatus.isOk()) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- transaction failed."; @@ -1008,6 +1044,8 @@ c2_status_t Codec2Client::ForAllServices( std::scoped_lock lock{key2IndexMutex}; key2Index[key] = index; // update last known client index return C2_OK; + } else if (status == C2_NO_MEMORY) { + return C2_NO_MEMORY; } else if (status == C2_TRANSACTION_FAILED) { LOG(WARNING) << "\"" << key << "\" failed for service \"" << client->getName() @@ -1028,24 +1066,23 @@ c2_status_t Codec2Client::ForAllServices( return status; // return the last status from a valid client } -std::shared_ptr - Codec2Client::CreateComponentByName( +c2_status_t Codec2Client::CreateComponentByName( const char* componentName, const std::shared_ptr& listener, + std::shared_ptr* component, std::shared_ptr* owner, size_t numberOfAttempts) { std::string key{"create:"}; key.append(componentName); - std::shared_ptr component; c2_status_t status = ForAllServices( key, numberOfAttempts, - [owner, &component, componentName, &listener]( + [owner, component, componentName, &listener]( const std::shared_ptr &client) -> c2_status_t { c2_status_t status = client->createComponent(componentName, listener, - &component); + component); if (status == C2_OK) { if (owner) { *owner = client; @@ -1064,7 +1101,7 @@ std::shared_ptr << "\" from all known services. " "Last returned status = " << status << "."; } - return component; + return status; } std::shared_ptr @@ -1192,6 +1229,7 @@ Codec2Client::Component::Component(const sp& base) }, mBase1_0{base}, mBase1_1{Base1_1::castFrom(base)}, + mBase1_2{Base1_2::castFrom(base)}, mBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } @@ -1214,6 +1252,30 @@ Codec2Client::Component::Component(const sp& base) }, mBase1_0{base}, mBase1_1{base}, + mBase1_2{Base1_2::castFrom(base)}, + mBufferPoolSender{std::make_unique()}, + mOutputBufferQueue{std::make_unique()} { +} + +Codec2Client::Component::Component(const sp& base) + : Configurable{ + [base]() -> sp { + Return> transResult1 = + base->getInterface(); + if (!transResult1.isOk()) { + return nullptr; + } + Return> transResult2 = + static_cast>(transResult1)-> + getConfigurable(); + return transResult2.isOk() ? + static_cast>(transResult2) : + nullptr; + }() + }, + mBase1_0{base}, + mBase1_1{base}, + mBase1_2{base}, mBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } @@ -1428,7 +1490,8 @@ c2_status_t Codec2Client::Component::configureVideoTunnel( c2_status_t Codec2Client::Component::setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, - uint32_t generation) { + uint32_t generation, + int maxDequeueCount) { uint64_t bqId = 0; sp nullIgbp; sp nullHgbp; @@ -1439,21 +1502,65 @@ c2_status_t Codec2Client::Component::setOutputSurface( igbp = new B2HGraphicBufferProducer2(surface); } + std::shared_ptr syncObj; + if (!surface) { - mOutputBufferQueue->configure(nullIgbp, generation, 0); + mOutputBufferQueue->configure(nullIgbp, generation, 0, maxDequeueCount, nullptr); } else if (surface->getUniqueId(&bqId) != OK) { LOG(ERROR) << "setOutputSurface -- " "cannot obtain bufferqueue id."; bqId = 0; - mOutputBufferQueue->configure(nullIgbp, generation, 0); + mOutputBufferQueue->configure(nullIgbp, generation, 0, maxDequeueCount, nullptr); } else { - mOutputBufferQueue->configure(surface, generation, bqId); + mOutputBufferQueue->configure(surface, generation, bqId, maxDequeueCount, mBase1_2 ? + &syncObj : nullptr); } - ALOGD("generation remote change %u", generation); - Return transStatus = mBase1_0->setOutputSurface( - static_cast(blockPoolId), - bqId == 0 ? nullHgbp : igbp); + // set consumer bits + // TODO: should this get incorporated into setOutputSurface method so that consumer bits + // can be set atomically? + uint64_t consumerUsage = kDefaultConsumerUsage; + { + if (surface) { + int usage = 0; + status_t err = surface->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage); + if (err != NO_ERROR) { + ALOGD("setOutputSurface -- failed to get consumer usage bits (%d/%s). ignoring", + err, asString(err)); + } else { + // Note: we are adding the default usage because components must support + // producing output frames that can be displayed an all output surfaces. + + // TODO: do not set usage for tunneled scenario. It is unclear if consumer usage + // is meaningful in a tunneled scenario; on one hand output buffers exist, but + // they do not exist inside of C2 scope. Any buffer usage shall be communicated + // through the sideband channel. + + // do an unsigned conversion as bit-31 may be 1 + consumerUsage = (uint32_t)usage | kDefaultConsumerUsage; + } + } + + C2StreamUsageTuning::output outputUsage{ + 0u, C2AndroidMemoryUsage::FromGrallocUsage(consumerUsage).expected}; + std::vector> failures; + c2_status_t err = config({&outputUsage}, C2_MAY_BLOCK, &failures); + if (err != C2_OK) { + ALOGD("setOutputSurface -- failed to set consumer usage (%d/%s)", + err, asString(err)); + } + } + ALOGD("setOutputSurface -- generation=%u consumer usage=%#llx%s", + generation, (long long)consumerUsage, syncObj ? " sync" : ""); + + Return transStatus = syncObj ? + mBase1_2->setOutputSurfaceWithSyncObj( + static_cast(blockPoolId), + bqId == 0 ? nullHgbp : igbp, *syncObj) : + mBase1_0->setOutputSurface( + static_cast(blockPoolId), + bqId == 0 ? nullHgbp : igbp); + if (!transStatus.isOk()) { LOG(ERROR) << "setOutputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; @@ -1463,6 +1570,7 @@ c2_status_t Codec2Client::Component::setOutputSurface( if (status != C2_OK) { LOG(DEBUG) << "setOutputSurface -- call failed: " << status << "."; } + ALOGD("Surface configure completed"); return status; } @@ -1473,6 +1581,11 @@ status_t Codec2Client::Component::queueToOutputSurface( return mOutputBufferQueue->outputBuffer(block, input, output); } +void Codec2Client::Component::setOutputSurfaceMaxDequeueCount( + int maxDequeueCount) { + mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount); +} + c2_status_t Codec2Client::Component::connectToInputSurface( const std::shared_ptr& inputSurface, std::shared_ptr* connection) { @@ -1625,4 +1738,3 @@ c2_status_t Codec2Client::InputSurfaceConnection::disconnect() { } } // namespace android - diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index ffd194a7ac0506225a95f08da06ccb616fc2d708..347e58a74a482db5b38a51a86f73f826b06befb2 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -78,6 +78,11 @@ struct IComponent; struct IComponentStore; } // namespace android::hardware::media::c2::V1_1 +namespace android::hardware::media::c2::V1_2 { +struct IComponent; +struct IComponentStore; +} // namespace android::hardware::media::c2::V1_2 + namespace android::hardware::media::bufferpool::V2_0 { struct IClientManager; } // namespace android::hardware::media::bufferpool::V2_0 @@ -137,6 +142,7 @@ struct Codec2Client : public Codec2ConfigurableClient { typedef ::android::hardware::media::c2::V1_0::IComponentStore Base1_0; typedef ::android::hardware::media::c2::V1_1::IComponentStore Base1_1; + typedef ::android::hardware::media::c2::V1_2::IComponentStore Base1_2; typedef Base1_0 Base; struct Listener; @@ -156,6 +162,7 @@ struct Codec2Client : public Codec2ConfigurableClient { sp const& getBase() const; sp const& getBase1_0() const; sp const& getBase1_1() const; + sp const& getBase1_2() const; std::string const& getServiceName() const; @@ -200,9 +207,10 @@ struct Codec2Client : public Codec2ConfigurableClient { // Try to create a component with a given name from all known // IComponentStore services. numberOfAttempts determines the number of times // to retry the HIDL call if the transaction fails. - static std::shared_ptr CreateComponentByName( + static c2_status_t CreateComponentByName( char const* componentName, std::shared_ptr const& listener, + std::shared_ptr* component, std::shared_ptr* owner = nullptr, size_t numberOfAttempts = 10); @@ -227,6 +235,7 @@ struct Codec2Client : public Codec2ConfigurableClient { protected: sp mBase1_0; sp mBase1_1; + sp mBase1_2; // Finds the first store where the predicate returns C2_OK and returns the // last predicate result. The predicate will be tried on all stores. The @@ -317,6 +326,7 @@ struct Codec2Client::Component : public Codec2Client::Configurable { typedef ::android::hardware::media::c2::V1_0::IComponent Base1_0; typedef ::android::hardware::media::c2::V1_1::IComponent Base1_1; + typedef ::android::hardware::media::c2::V1_2::IComponent Base1_2; typedef Base1_0 Base; c2_status_t createBlockPool( @@ -374,7 +384,8 @@ struct Codec2Client::Component : public Codec2Client::Configurable { c2_status_t setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, - uint32_t generation); + uint32_t generation, + int maxDequeueBufferCount); // Extract a slot number from of the block, then call // IGraphicBufferProducer::queueBuffer(). @@ -397,6 +408,9 @@ struct Codec2Client::Component : public Codec2Client::Configurable { const QueueBufferInput& input, QueueBufferOutput* output); + // Set max dequeue count for output surface. + void setOutputSurfaceMaxDequeueCount(int maxDequeueCount); + // Connect to a given InputSurface. c2_status_t connectToInputSurface( const std::shared_ptr& inputSurface, @@ -412,12 +426,14 @@ struct Codec2Client::Component : public Codec2Client::Configurable { // base cannot be null. Component(const sp& base); Component(const sp& base); + Component(const sp& base); ~Component(); protected: sp mBase1_0; sp mBase1_1; + sp mBase1_2; struct BufferPoolSender; std::unique_ptr mBufferPoolSender; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h b/media/codec2/hidl/client/include/codec2/hidl/output.h similarity index 82% rename from media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h rename to media/codec2/hidl/client/include/codec2/hidl/output.h index 80368f78be1e0c3f1f2eae0260b612d89f3af987..877148a7ff60a28bb60da51fe349e0b33ffb78c6 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h +++ b/media/codec2/hidl/client/include/codec2/hidl/output.h @@ -19,16 +19,17 @@ #include #include +#include #include struct C2_HIDE _C2BlockPoolData; +class C2SurfaceSyncMemory; namespace android { namespace hardware { namespace media { namespace c2 { -namespace V1_0 { -namespace utils { + // BufferQueue-Based Block Operations // ================================== @@ -45,7 +46,9 @@ struct OutputBufferQueue { // Graphic blocks from older surface will be migrated to new surface. bool configure(const sp& igbp, uint32_t generation, - uint64_t bqId); + uint64_t bqId, + int maxDequeueBufferCount, + std::shared_ptr *syncObj); // Render a graphic block to current surface. status_t outputBuffer( @@ -61,22 +64,27 @@ struct OutputBufferQueue { void holdBufferQueueBlocks( const std::list>& workList); + // Update # of max dequeue buffer from BQ. If # of max dequeued buffer is shared + // via shared memory between HAL and framework, Update # of max dequeued buffer + // and synchronize. + void updateMaxDequeueBufferCount(int maxDequeueBufferCount); + private: std::mutex mMutex; sp mIgbp; uint32_t mGeneration; uint64_t mBqId; + int32_t mMaxDequeueBufferCount; std::shared_ptr mOwner; // To migrate existing buffers sp mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way std::weak_ptr<_C2BlockPoolData> mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS]; + std::shared_ptr mSyncMem; bool registerBuffer(const C2ConstGraphicBlock& block); }; -} // namespace utils -} // namespace V1_0 } // namespace c2 } // namespace media } // namespace hardware diff --git a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp b/media/codec2/hidl/client/output.cpp similarity index 62% rename from media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp rename to media/codec2/hidl/client/output.cpp index c4a72ef627db1b3869e7db48074389147aa0419c..de34c24aca7f8d72e2cc7c84c91582c82b2933b0 100644 --- a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp +++ b/media/codec2/hidl/client/output.cpp @@ -19,13 +19,16 @@ #include #include -#include +#include +#include #include +#include #include #include #include #include +#include #include @@ -33,8 +36,6 @@ namespace android { namespace hardware { namespace media { namespace c2 { -namespace V1_0 { -namespace utils { using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: V2_0::IGraphicBufferProducer; @@ -105,7 +106,8 @@ sp getHgbp(const sp& igbp) { status_t attachToBufferQueue(const C2ConstGraphicBlock& block, const sp& igbp, uint32_t generation, - int32_t* bqSlot) { + int32_t* bqSlot, + std::shared_ptr syncMem) { if (!igbp) { LOG(WARNING) << "attachToBufferQueue -- null producer."; return NO_INIT; @@ -126,7 +128,25 @@ status_t attachToBufferQueue(const C2ConstGraphicBlock& block, << ", stride " << graphicBuffer->getStride() << ", generation " << graphicBuffer->getGenerationNumber(); - status_t result = igbp->attachBuffer(bqSlot, graphicBuffer); + C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr; + status_t result = OK; + if (syncVar) { + syncVar->lock(); + if (!syncVar->isDequeueableLocked() || + syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_SWITCHING) { + syncVar->unlock(); + LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: " + "status = " << INVALID_OPERATION << "."; + return INVALID_OPERATION; + } + result = igbp->attachBuffer(bqSlot, graphicBuffer); + if (result == OK) { + syncVar->notifyDequeuedLocked(); + } + syncVar->unlock(); + } else { + result = igbp->attachBuffer(bqSlot, graphicBuffer); + } if (result != OK) { LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: " "status = " << result << "."; @@ -157,7 +177,41 @@ OutputBufferQueue::~OutputBufferQueue() { bool OutputBufferQueue::configure(const sp& igbp, uint32_t generation, - uint64_t bqId) { + uint64_t bqId, + int maxDequeueBufferCount, + std::shared_ptr *syncObj) { + uint64_t consumerUsage = 0; + if (igbp && igbp->getConsumerUsage(&consumerUsage) != OK) { + ALOGW("failed to get consumer usage"); + } + + // TODO : Abstract creation process into C2SurfaceSyncMemory class. + // use C2LinearBlock instead ashmem. + std::shared_ptr syncMem; + if (syncObj && igbp) { + bool mapped = false; + int memFd = ashmem_create_region("C2SurfaceMem", sizeof(C2SyncVariables)); + size_t memSize = memFd < 0 ? 0 : ashmem_get_size_region(memFd); + if (memSize > 0) { + syncMem = C2SurfaceSyncMemory::Create(memFd, memSize); + if (syncMem) { + mapped = true; + *syncObj = std::make_shared(); + (*syncObj)->syncMemory = syncMem->handle(); + (*syncObj)->bqId = bqId; + (*syncObj)->generationId = generation; + (*syncObj)->consumerUsage = consumerUsage; + ALOGD("C2SurfaceSyncMemory created %zu(%zu)", sizeof(C2SyncVariables), memSize); + } + } + if (!mapped) { + if (memFd >= 0) { + ::close(memFd); + } + ALOGW("SurfaceSyncObj creation failure"); + } + } + size_t tryNum = 0; size_t success = 0; sp buffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; @@ -166,12 +220,43 @@ bool OutputBufferQueue::configure(const sp& igbp, { std::scoped_lock l(mMutex); if (generation == mGeneration) { + // case of old BlockPool destruction + C2SyncVariables *var = mSyncMem ? mSyncMem->mem() : nullptr; + if (syncObj && var) { + *syncObj = std::make_shared(); + (*syncObj)->bqId = bqId; + (*syncObj)->syncMemory = mSyncMem->handle(); + (*syncObj)->generationId = generation; + (*syncObj)->consumerUsage = consumerUsage; + mMaxDequeueBufferCount = maxDequeueBufferCount; + var->lock(); + var->setSyncStatusLocked(C2SyncVariables::STATUS_INIT); + var->setInitialDequeueCountLocked(mMaxDequeueBufferCount, 0); + var->unlock(); + } return false; } + std::shared_ptr oldMem = mSyncMem; + C2SyncVariables *oldSync = mSyncMem ? mSyncMem->mem() : nullptr; + if (oldSync) { + oldSync->lock(); + oldSync->setSyncStatusLocked(C2SyncVariables::STATUS_SWITCHING); + oldSync->unlock(); + } + mSyncMem.reset(); + if (syncMem) { + mSyncMem = syncMem; + } + C2SyncVariables *newSync = mSyncMem ? mSyncMem->mem() : nullptr; + mIgbp = igbp; mGeneration = generation; mBqId = bqId; mOwner = std::make_shared(0); + mMaxDequeueBufferCount = maxDequeueBufferCount; + if (igbp == nullptr) { + return false; + } for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) { if (mBqId == 0 || !mBuffers[i]) { continue; @@ -183,14 +268,31 @@ bool OutputBufferQueue::configure(const sp& igbp, } ++tryNum; int bqSlot; + + // Update buffer's generation and usage. + if ((mBuffers[i]->getUsage() & consumerUsage) != consumerUsage) { + mBuffers[i] = new GraphicBuffer( + mBuffers[i]->handle, GraphicBuffer::CLONE_HANDLE, + mBuffers[i]->width, mBuffers[i]->height, + mBuffers[i]->format, mBuffers[i]->layerCount, + mBuffers[i]->getUsage() | consumerUsage, + mBuffers[i]->stride); + if (mBuffers[i]->initCheck() != OK) { + ALOGW("%s() failed to update usage, original usage=%" PRIx64 + ", consumer usage=%" PRIx64, + __func__, mBuffers[i]->getUsage(), consumerUsage); + continue; + } + } mBuffers[i]->setGenerationNumber(generation); + status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]); if (result != OK) { continue; } bool attach = _C2BlockFactory::EndAttachBlockToBufferQueue( - data, mOwner, getHgbp(mIgbp), + data, mOwner, getHgbp(mIgbp), mSyncMem, generation, bqId, bqSlot); if (!attach) { igbp->cancelBuffer(bqSlot, Fence::NO_FENCE); @@ -204,8 +306,14 @@ bool OutputBufferQueue::configure(const sp& igbp, mBuffers[i] = buffers[i]; mPoolDatas[i] = poolDatas[i]; } + if (newSync) { + newSync->lock(); + newSync->setInitialDequeueCountLocked(mMaxDequeueBufferCount, success); + newSync->unlock(); + } } - ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum); + ALOGD("remote graphic buffer migration %zu/%zu", + success, tryNum); return true; } @@ -236,7 +344,7 @@ bool OutputBufferQueue::registerBuffer(const C2ConstGraphicBlock& block) { << ", bqSlot " << oldSlot << ", generation " << mGeneration << "."; - _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp)); + _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp), mSyncMem); mPoolDatas[oldSlot] = data; mBuffers[oldSlot] = createGraphicBuffer(block); mBuffers[oldSlot]->setGenerationNumber(mGeneration); @@ -256,25 +364,39 @@ status_t OutputBufferQueue::outputBuffer( uint32_t generation; uint64_t bqId; int32_t bqSlot; - bool display = displayBufferQueueBlock(block); + bool display = V1_0::utils::displayBufferQueueBlock(block); if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) || bqId == 0) { // Block not from bufferqueue -- it must be attached before queuing. + std::shared_ptr syncMem; mMutex.lock(); sp outputIgbp = mIgbp; uint32_t outputGeneration = mGeneration; + syncMem = mSyncMem; mMutex.unlock(); status_t status = attachToBufferQueue( - block, outputIgbp, outputGeneration, &bqSlot); + block, outputIgbp, outputGeneration, &bqSlot, syncMem); + if (status != OK) { LOG(WARNING) << "outputBuffer -- attaching failed."; return INVALID_OPERATION; } - status = outputIgbp->queueBuffer(static_cast(bqSlot), - input, output); + auto syncVar = syncMem ? syncMem->mem() : nullptr; + if(syncVar) { + syncVar->lock(); + status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + if (status == OK) { + syncVar->notifyQueuedLocked(); + } + syncVar->unlock(); + } else { + status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + } if (status != OK) { LOG(ERROR) << "outputBuffer -- queueBuffer() failed " "on non-bufferqueue-based block. " @@ -284,10 +406,12 @@ status_t OutputBufferQueue::outputBuffer( return OK; } + std::shared_ptr syncMem; mMutex.lock(); sp outputIgbp = mIgbp; uint32_t outputGeneration = mGeneration; uint64_t outputBqId = mBqId; + syncMem = mSyncMem; mMutex.unlock(); if (!outputIgbp) { @@ -308,8 +432,21 @@ status_t OutputBufferQueue::outputBuffer( return DEAD_OBJECT; } - status_t status = outputIgbp->queueBuffer(static_cast(bqSlot), - input, output); + auto syncVar = syncMem ? syncMem->mem() : nullptr; + status_t status = OK; + if (syncVar) { + syncVar->lock(); + status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + if (status == OK) { + syncVar->notifyQueuedLocked(); + } + syncVar->unlock(); + } else { + status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + } + if (status != OK) { LOG(ERROR) << "outputBuffer -- queueBuffer() failed " "on bufferqueue-based block. " @@ -326,8 +463,19 @@ void OutputBufferQueue::holdBufferQueueBlocks( this, std::placeholders::_1)); } -} // namespace utils -} // namespace V1_0 +void OutputBufferQueue::updateMaxDequeueBufferCount(int maxDequeueBufferCount) { + mMutex.lock(); + mMaxDequeueBufferCount = maxDequeueBufferCount; + auto syncVar = mSyncMem ? mSyncMem->mem() : nullptr; + if (syncVar) { + syncVar->lock(); + syncVar->updateMaxDequeueCountLocked(maxDequeueBufferCount); + syncVar->unlock(); + } + mMutex.unlock(); + ALOGD("set max dequeue count %d from update", maxDequeueBufferCount); +} + } // namespace c2 } // namespace media } // namespace hardware diff --git a/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp index cd1bcb00e6107d8eab490c5511ea45030a349d31..b26e74b59d35e9c3a8651609bccf76d2e3badbf0 100644 --- a/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp +++ b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp @@ -105,4 +105,13 @@ bool DefaultFilterPlugin::isFilteringEnabled(const std::shared_ptrisFilteringEnabled(intf); } +c2_status_t DefaultFilterPlugin::queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) { + if (mInit != OK) { + return C2_NO_INIT; + } + return mPlugin->queryParamsForPreviousComponent(intf, params); +} + } // namespace android diff --git a/media/codec2/hidl/plugin/FilterWrapper.cpp b/media/codec2/hidl/plugin/FilterWrapper.cpp index bed8aeb6d7417ee287bc4408b2655db439078e50..70c63f2ff7aba6e182cd516724fffb74ffe25aaa 100644 --- a/media/codec2/hidl/plugin/FilterWrapper.cpp +++ b/media/codec2/hidl/plugin/FilterWrapper.cpp @@ -45,8 +45,9 @@ class WrappedDecoderInterface : public C2ComponentInterface { public: WrappedDecoderInterface( std::shared_ptr intf, - std::vector &&filters) - : mIntf(intf) { + std::vector &&filters, + std::weak_ptr filterWrapper) + : mIntf(intf), mFilterWrapper(filterWrapper) { takeFilters(std::move(filters)); } @@ -101,6 +102,13 @@ public: mTypeToIndexForQuery[type.type()] = i; } } + for (size_t i = mFilters.size(); i > 0; --i) { + if (i == 1) { + backPropagateParams_l(mIntf, mFilters[0].intf, C2_MAY_BLOCK); + } else { + backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, C2_MAY_BLOCK); + } + } if (!mFilters.empty()) { for (uint32_t type : kTypesForLastFilter) { mTypeToIndexForQuery[type] = mFilters.size() - 1; @@ -256,6 +264,13 @@ public: result = err; } } + for (size_t i = mFilters.size(); i > 0; --i) { + if (i == 1) { + backPropagateParams_l(mIntf, mFilters[0].intf, mayBlock); + } else { + backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, mayBlock); + } + } return result; } @@ -338,6 +353,7 @@ private: mutable std::mutex mMutex; std::shared_ptr mIntf; std::vector mFilters; + std::weak_ptr mFilterWrapper; std::map mTypeToIndexForQuery; std::map mTypeToIndexForConfig; @@ -402,6 +418,41 @@ private: } return C2_OK; } + + c2_status_t backPropagateParams_l( + const std::shared_ptr &curr, + const std::shared_ptr &next, + c2_blocking_t mayBlock) { + // NOTE: this implementation is preliminary --- it could change once + // we define what parameters needs to be propagated in component chaining. + std::shared_ptr filterWrapper = mFilterWrapper.lock(); + if (!filterWrapper) { + LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper not found"; + return C2_OK; + } + std::vector> params; + c2_status_t err = filterWrapper->queryParamsForPreviousComponent(next, ¶ms); + if (err != C2_OK) { + LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper returned error for " + << "queryParamsForPreviousComponent; intf=" << next->getName() << " err=" << err; + return C2_OK; + } + std::vector configParams; + for (size_t i = 0; i < params.size(); ++i) { + if (!params[i]) { + continue; + } + configParams.push_back(params[i].get()); + } + std::vector> failures; + curr->config_vb(configParams, mayBlock, &failures); + if (err != C2_OK && err != C2_BAD_INDEX) { + LOG(DEBUG) << "WrappedDecoderInterface: " << next->getName() + << " returned error for config_vb; err=" << err; + return err; + } + return C2_OK; + } }; class WrappedDecoder : public C2Component, public std::enable_shared_from_this { @@ -413,7 +464,7 @@ public: : mComp(comp), mFilters(std::move(filters)), mFilterWrapper(filterWrapper) { std::vector filtersDup(mFilters); mIntf = std::make_shared( - comp->intf(), std::move(filtersDup)); + comp->intf(), std::move(filtersDup), filterWrapper); } ~WrappedDecoder() override = default; @@ -844,7 +895,8 @@ std::shared_ptr FilterWrapper::maybeWrapInterface( << " is not video/image decoder; not wrapping the interface"; return intf; } - return std::make_shared(intf, createFilters()); + return std::make_shared( + intf, createFilters(), weak_from_this()); } std::shared_ptr FilterWrapper::maybeWrapComponent( @@ -917,4 +969,14 @@ c2_status_t FilterWrapper::createBlockPool( return CreateCodec2BlockPool(allocatorId, component, pool); } +c2_status_t FilterWrapper::queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) { + if (mInit != OK) { + LOG(WARNING) << "queryParamsForPreviousComponent: Wrapper not initialized: "; + return C2_NO_INIT; + } + return mPlugin->queryParamsForPreviousComponent(intf, params); +} + } // namespace android diff --git a/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h index 6f1f90721bcfaca5320729c4ae2abe560d283dbc..a1ac62487a9c9a65baec49cd1116fa56f5a2d232 100644 --- a/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h +++ b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h @@ -57,6 +57,15 @@ public: * current configuration; false if it will be no-op. */ virtual bool isFilteringEnabled(const std::shared_ptr &intf) = 0; + + /** + * Query parameters to |intf|, which the component wants applied to + * the previous component in the chain. For example, an image/video filter + * may require specific usage or pixel format from the previous component. + */ + virtual c2_status_t queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) = 0; }; } // namespace android diff --git a/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h index f856324208c3fc73830a2f19edc43cc25a222f19..0aab39fc921f658bfa10ffb4b0bd98418acc6099 100644 --- a/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h +++ b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h @@ -35,6 +35,9 @@ public: std::shared_ptr getStore() override { return mStore; } bool describe(C2String name, FilterWrapper::Descriptor *desc) override; bool isFilteringEnabled(const std::shared_ptr &intf) override; + c2_status_t queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) override; private: status_t mInit; diff --git a/media/codec2/hidl/plugin/internal/FilterWrapper.h b/media/codec2/hidl/plugin/internal/FilterWrapper.h index 5ced43546acb00dcfbd2958cc34fd3d1396a0812..cf2cc30c34a3a221252437637c45433a2eacd57b 100644 --- a/media/codec2/hidl/plugin/internal/FilterWrapper.h +++ b/media/codec2/hidl/plugin/internal/FilterWrapper.h @@ -43,6 +43,9 @@ public: virtual std::shared_ptr getStore() = 0; virtual bool describe(C2String name, Descriptor *desc) = 0; virtual bool isFilteringEnabled(const std::shared_ptr &intf) = 0; + virtual c2_status_t queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) = 0; C2_DO_NOT_COPY(Plugin); }; @@ -78,11 +81,21 @@ public: */ bool isFilteringEnabled(const std::shared_ptr &intf); + /** + * Create a C2BlockPool object with |allocatorId| for |component|. + */ c2_status_t createBlockPool( C2PlatformAllocatorStore::id_t allocatorId, std::shared_ptr component, std::shared_ptr *pool); + /** + * Query parameters that |intf| wants from the previous component. + */ + c2_status_t queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params); + private: status_t mInit; std::unique_ptr mPlugin; diff --git a/media/codec2/hidl/plugin/samples/Android.bp b/media/codec2/hidl/plugin/samples/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..32b760d1a16fdebdbf677ef9f77a5d769b4fd3a5 --- /dev/null +++ b/media/codec2/hidl/plugin/samples/Android.bp @@ -0,0 +1,58 @@ +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_defaults { + name: "sample-codec2-hidl-plugin-defaults", + + srcs: [ + "SampleFilterPlugin.cpp", + ], + + defaults: [ + "libcodec2-impl-defaults", + ], + + header_libs: [ + "libcodec2_hidl_plugin_headers", + "libgui_headers", + ], + + shared_libs: [ + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libGLESv3", + "libbase", + "libcodec2", + "libcutils", + "libprocessgroup", + "libsfplugin_ccodec_utils", + "libsync", + "libui", + "libutils", + ], + + static_libs: [ + "librenderfright", + ], + + cflags: [ + "-Werror", + "-Wall", + ], +} + +cc_library { + name: "sample-codec2-hidl-plugin", + vendor: true, + + defaults: [ + "sample-codec2-hidl-plugin-defaults", + ], +} diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b942be79c8a0f2368b6570ae574c480f62a3e4d3 --- /dev/null +++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp @@ -0,0 +1,997 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SampleFilterPlugin" +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef C2StreamParam + C2StreamColorAspectsRequestInfo; + +namespace android { + +using namespace std::literals::chrono_literals; + +class SampleToneMappingFilter + : public C2Component, public std::enable_shared_from_this { +public: + class Interface : public C2ComponentInterface { + public: + static const std::string NAME; + static const FilterPlugin_V1::Descriptor DESCRIPTOR; + + explicit Interface(c2_node_id_t id) + : mId(id), + mReflector(std::make_shared()), + mHelper(mReflector) { + } + ~Interface() override = default; + C2String getName() const override { return NAME; } + c2_node_id_t getId() const override { return mId; } + + c2_status_t query_vb( + const std::vector &stackParams, + const std::vector &heapParamIndices, + c2_blocking_t mayBlock, + std::vector>* const heapParams) const override { + return mHelper.query(stackParams, heapParamIndices, mayBlock, heapParams); + } + c2_status_t config_vb( + const std::vector ¶ms, + c2_blocking_t mayBlock, + std::vector>* const failures) override { + return mHelper.config(params, mayBlock, failures); + } + c2_status_t querySupportedParams_nb( + std::vector> * const params) const override { + return mHelper.querySupportedParams(params); + } + c2_status_t querySupportedValues_vb( + std::vector &fields, + c2_blocking_t mayBlock) const override { + return mHelper.querySupportedValues(fields, mayBlock); + } + c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; } + c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; } + + uint32_t getDataSpace() { + Helper::Lock lock = mHelper.lock(); + uint32_t dataspace = HAL_DATASPACE_UNKNOWN; + C2Mapper::map( + mHelper.mInputColorAspectInfo->range, + mHelper.mInputColorAspectInfo->primaries, + mHelper.mInputColorAspectInfo->matrix, + mHelper.mInputColorAspectInfo->transfer, + &dataspace); + return dataspace; + } + std::shared_ptr getHdrStaticMetadata() { + Helper::Lock lock = mHelper.lock(); + return mHelper.mInputHdrStaticInfo; + } + C2BlockPool::local_id_t getPoolId() { + Helper::Lock lock = mHelper.lock(); + return mHelper.mOutputPoolIds->m.values[0]; + } + + static bool IsFilteringEnabled(const std::shared_ptr &intf) { + C2StreamColorAspectsRequestInfo::output info(0u); + std::vector> heapParams; + c2_status_t err = intf->query_vb({&info}, {}, C2_MAY_BLOCK, &heapParams); + if (err != C2_OK && err != C2_BAD_INDEX) { + LOG(WARNING) << "SampleToneMappingFilter::Interface::IsFilteringEnabled: " + << "query failed for " << intf->getName(); + return false; + } + return info && info.transfer == C2Color::TRANSFER_170M; + } + + static c2_status_t QueryParamsForPreviousComponent( + [[maybe_unused]] const std::shared_ptr &intf, + std::vector> *params) { + params->emplace_back(new C2StreamUsageTuning::output( + 0u, C2AndroidMemoryUsage::HW_TEXTURE_READ)); + params->emplace_back(new C2StreamPixelFormatInfo::output( + 0u, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)); + return C2_OK; + } + private: + const c2_node_id_t mId; + std::shared_ptr mReflector; + struct Helper : public C2InterfaceHelper { + explicit Helper(std::shared_ptr reflector) + : C2InterfaceHelper(reflector) { + setDerivedInstance(this); + + addParameter( + DefineParam(mApiFeatures, C2_PARAMKEY_API_FEATURES) + .withConstValue(new C2ApiFeaturesSetting(C2Config::api_feature_t( + API_REFLECTION | + API_VALUES | + API_CURRENT_VALUES | + API_DEPENDENCY | + API_SAME_INPUT_BUFFER))) + .build()); + + mName = C2ComponentNameSetting::AllocShared(NAME.size() + 1); + strncpy(mName->m.value, NAME.c_str(), NAME.size() + 1); + addParameter( + DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME) + .withConstValue(mName) + .build()); + + addParameter( + DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND) + .withConstValue(new C2ComponentKindSetting(C2Component::KIND_OTHER)) + .build()); + + addParameter( + DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN) + .withConstValue(new C2ComponentDomainSetting(C2Component::DOMAIN_VIDEO)) + .build()); + + addParameter( + DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT) + .withConstValue(new C2PortStreamCountTuning::input(1)) + .build()); + + addParameter( + DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT) + .withConstValue(new C2PortStreamCountTuning::output(1)) + .build()); + + addParameter( + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input( + 0u, C2BufferData::GRAPHIC)) + .build()); + + static const std::string kRawMediaType = "video/raw"; + mInputMediaType = C2PortMediaTypeSetting::input::AllocShared( + kRawMediaType.size() + 1); + strncpy(mInputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1); + addParameter( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(mInputMediaType) + .build()); + + addParameter( + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output( + 0u, C2BufferData::GRAPHIC)) + .build()); + + mOutputMediaType = C2PortMediaTypeSetting::output::AllocShared( + kRawMediaType.size() + 1); + strncpy(mOutputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1); + addParameter( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(mOutputMediaType) + .build()); + + addParameter( + DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::input(0u)) + .build()); + + addParameter( + DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::output(0u)) + .build()); + + addParameter( + DefineParam(mActualPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY) + .withConstValue(new C2ActualPipelineDelayTuning(0u)) + .build()); + + C2BlockPool::local_id_t outputPoolIds[1] = { C2BlockPool::BASIC_GRAPHIC }; + addParameter( + DefineParam(mOutputPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS) + .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputPoolIds)) + .withFields({ C2F(mOutputPoolIds, m.values[0]).any(), + C2F(mOutputPoolIds, m.values).inRange(0, 1) }) + .withSetter(OutputBlockPoolSetter) + .build()); + + addParameter( + DefineParam(mInputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO) + .withDefault(new C2StreamHdrStaticInfo::input(0u)) + .withFields({ + C2F(mInputHdrStaticInfo, mastering.red.x).any(), + }) + .withSetter(HdrStaticInfoSetter) + .build()); + + addParameter( + DefineParam(mOutputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO) + .withConstValue(new C2StreamHdrStaticInfo::output(0u)) + .build()); + + addParameter( + DefineParam(mInputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS) + .withDefault(new C2StreamColorAspectsInfo::input(0u)) + .withFields({ + C2F(mInputColorAspectInfo, range).any(), + C2F(mInputColorAspectInfo, primaries).any(), + C2F(mInputColorAspectInfo, transfer).any(), + C2F(mInputColorAspectInfo, matrix).any(), + }) + .withSetter(InputColorAspectsSetter) + .build()); + + addParameter( + DefineParam( + mColorAspectRequestInfo, + (std::string(C2_PARAMKEY_COLOR_ASPECTS) + ".request").c_str()) + .withDefault(new C2StreamColorAspectsRequestInfo::output(0u)) + .withFields({ + C2F(mColorAspectRequestInfo, range).any(), + C2F(mColorAspectRequestInfo, primaries).any(), + C2F(mColorAspectRequestInfo, transfer).oneOf({ + C2Color::TRANSFER_UNSPECIFIED, + C2Color::TRANSFER_170M, + }), + C2F(mColorAspectRequestInfo, matrix).any(), + }) + .withSetter(ColorAspectsRequestSetter) + .build()); + + addParameter( + DefineParam(mOutputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS) + .withDefault(new C2StreamColorAspectsInfo::output(0u)) + .withFields({ + C2F(mOutputColorAspectInfo, range).any(), + C2F(mOutputColorAspectInfo, primaries).any(), + C2F(mOutputColorAspectInfo, transfer).any(), + C2F(mOutputColorAspectInfo, matrix).any(), + }) + .withSetter(OutputColorAspectsSetter, + mInputColorAspectInfo, + mColorAspectRequestInfo) + .build()); + } + + static C2R OutputBlockPoolSetter( + bool mayBlock, + C2P &me) { + (void)mayBlock, (void)me; + return C2R::Ok(); + } + + static C2R HdrStaticInfoSetter( + bool mayBlock, + C2P &me) { + (void)mayBlock, (void)me; + return C2R::Ok(); + } + + static C2R InputColorAspectsSetter( + bool mayBlock, + C2P &me) { + (void)mayBlock, (void)me; + return C2R::Ok(); + } + + static C2R OutputColorAspectsSetter( + bool mayBlock, + C2P &me, + const C2P &inputColor, + const C2P &request) { + (void)mayBlock; + me.set().range = inputColor.v.range; + me.set().primaries = inputColor.v.primaries; + me.set().transfer = inputColor.v.transfer; + if (request.v.transfer == C2Color::TRANSFER_170M) { + me.set().transfer = C2Color::TRANSFER_170M; + } + me.set().matrix = inputColor.v.matrix; + return C2R::Ok(); + } + + static C2R ColorAspectsRequestSetter( + bool mayBlock, + C2P &me) { + (void)mayBlock; + if (me.v.range != C2Color::RANGE_UNSPECIFIED) { + me.set().range = C2Color::RANGE_UNSPECIFIED; + } + if (me.v.primaries != C2Color::PRIMARIES_UNSPECIFIED) { + me.set().primaries = C2Color::PRIMARIES_UNSPECIFIED; + } + if (me.v.transfer != C2Color::TRANSFER_170M) { + me.set().transfer = C2Color::TRANSFER_UNSPECIFIED; + } + if (me.v.matrix != C2Color::MATRIX_UNSPECIFIED) { + me.set().matrix = C2Color::MATRIX_UNSPECIFIED; + } + return C2R::Ok(); + } + + std::shared_ptr mApiFeatures; + + std::shared_ptr mName; + std::shared_ptr mAliases; + std::shared_ptr mKind; + std::shared_ptr mDomain; + + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + + std::shared_ptr mActualInputDelay; + std::shared_ptr mActualOutputDelay; + std::shared_ptr mActualPipelineDelay; + + std::shared_ptr mInputStreamCount; + std::shared_ptr mOutputStreamCount; + + std::shared_ptr mOutputPoolIds; + + std::shared_ptr mInputHdrStaticInfo; + std::shared_ptr mOutputHdrStaticInfo; + std::shared_ptr mInputColorAspectInfo; + std::shared_ptr mOutputColorAspectInfo; + std::shared_ptr mColorAspectRequestInfo; + } mHelper; + }; + + explicit SampleToneMappingFilter(c2_node_id_t id) + : mIntf(std::make_shared(id)) { + } + ~SampleToneMappingFilter() override { + if (mProcessingThread.joinable()) { + mProcessingThread.join(); + } + } + + c2_status_t setListener_vb( + const std::shared_ptr &listener, c2_blocking_t mayBlock) override { + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 5ms; + { + std::unique_lock lock(mStateMutex); + if (mState == RELEASED) { + return C2_BAD_STATE; + } + if (mState == RUNNING && listener) { + return C2_BAD_STATE; + } + if (mState != STOPPED) { + return C2_BAD_STATE; + } + } + std::unique_lock lock(mListenerMutex, std::try_to_lock); + if (lock) { + mListener = listener; + return C2_OK; + } + if (mayBlock == C2_DONT_BLOCK) { + return C2_BLOCKING; + } + lock.try_lock_until(deadline); + if (!lock) { + return C2_TIMED_OUT; + } + mListener = listener; + return C2_OK; + } + + c2_status_t queue_nb(std::list>* const items) override { + if (!items) { + return C2_BAD_VALUE; + } + { + std::unique_lock lock(mStateMutex); + if (mState != RUNNING) { + return C2_BAD_STATE; + } + } + std::unique_lock lock(mQueueMutex); + mQueue.splice(mQueue.end(), *items); + return C2_OK; + } + + c2_status_t announce_nb(const std::vector &) override { return C2_OMITTED; } + + c2_status_t flush_sm( + flush_mode_t mode, + std::list>* const flushedWork) override { + if (!flushedWork) { + return C2_BAD_VALUE; + } + if (mode == FLUSH_CHAIN) { + return C2_BAD_VALUE; + } + { + std::unique_lock lock(mStateMutex); + if (mState != RUNNING) { + return C2_BAD_STATE; + } + } + { + std::unique_lock lock(mQueueMutex); + mQueue.swap(*flushedWork); + } + // NOTE: this component does not have internal state to flush. + return C2_OK; + } + + c2_status_t drain_nb(drain_mode_t mode) override { + if (mode == DRAIN_CHAIN) { + return C2_BAD_VALUE; + } + { + std::unique_lock lock(mStateMutex); + if (mState != RUNNING) { + return C2_BAD_STATE; + } + } + // NOTE: this component does not wait for work items before processing. + return C2_OK; + } + + c2_status_t start() override { + //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms; + { + std::unique_lock lock(mStateMutex); + if (mState == STARTING) { + return C2_DUPLICATE; + } + if (mState != STOPPED) { + return C2_BAD_STATE; + } + mState = STARTING; + } + { + std::unique_lock lock(mProcessingMutex); + if (!mProcessingThread.joinable()) { + mProcessingThread = std::thread([this]() { + processLoop(shared_from_this()); + }); + } + } + { + std::unique_lock lock(mStateMutex); + mState = RUNNING; + } + return C2_OK; + } + + c2_status_t stop() override { + //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms; + { + std::unique_lock lock(mStateMutex); + if (mState == STOPPING) { + return C2_DUPLICATE; + } + if (mState != RUNNING) { + return C2_BAD_STATE; + } + mState = STOPPING; + } + { + std::unique_lock lock(mQueueMutex); + mQueueCondition.notify_all(); + } + { + std::unique_lock lock(mProcessingMutex); + if (mProcessingThread.joinable()) { + mProcessingThread.join(); + } + } + { + std::unique_lock lock(mStateMutex); + mState = STOPPED; + } + return C2_OK; + } + + c2_status_t reset() override { + //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms; + { + std::unique_lock lock(mStateMutex); + if (mState == RESETTING) { + return C2_DUPLICATE; + } + if (mState == RELEASED) { + return C2_BAD_STATE; + } + mState = RESETTING; + } + { + std::unique_lock lock(mQueueMutex); + mQueueCondition.notify_all(); + } + { + std::unique_lock lock(mProcessingMutex); + if (mProcessingThread.joinable()) { + mProcessingThread.join(); + } + } + { + std::unique_lock lock(mStateMutex); + mState = STOPPED; + } + return C2_OK; + } + + c2_status_t release() override { + //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms; + { + std::unique_lock lock(mStateMutex); + if (mState == RELEASED || mState == RELEASING) { + return C2_DUPLICATE; + } + // TODO: return C2_BAD_STATE if not stopped + mState = RELEASING; + } + { + std::unique_lock lock(mQueueMutex); + mQueueCondition.notify_all(); + } + { + std::unique_lock lock(mProcessingMutex); + if (mProcessingThread.joinable()) { + mProcessingThread.join(); + } + } + { + std::unique_lock lock(mStateMutex); + mState = RELEASED; + } + return C2_OK; + } + + std::shared_ptr intf() override { + return mIntf; + } + +private: + void processLoop(std::shared_ptr thiz) { + constexpr float kDefaultMaxLumiance = 500.0; + constexpr float kDefaultMaxMasteringLuminance = 1000.0; + constexpr float kDefaultMaxContentLuminance = 1000.0; + constexpr uint32_t kDstUsage = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + + int32_t workCount = 0; + std::unique_ptr renderEngine = renderengine::RenderEngine::create( + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(2 /*maxFrameBufferAcquiredBuffers*/) + .setUseColorManagerment(true) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::LOW) + .build()); + if (!renderEngine) { + std::unique_lock lock(mListenerMutex); + mListener->onError_nb(thiz, C2_CORRUPTED); + return; + } + uint32_t textureName = 0; + renderEngine->genTextures(1, &textureName); + + while (true) { + // Before doing anything, verify the state + { + std::unique_lock lock(mStateMutex); + if (mState != RUNNING) { + break; + } + } + // Extract one work item + std::unique_ptr work; + { + std::unique_lock lock(mQueueMutex); + if (mQueue.empty()) { + mQueueCondition.wait_for(lock, 1s); + } + if (mQueue.empty()) { + continue; + } + mQueue.front().swap(work); + mQueue.pop_front(); + ++workCount; + } + LOG(VERBOSE) << "work #" << workCount << ": flags=" << work->input.flags + << " timestamp=" << work->input.ordinal.timestamp.peek();; + + std::vector configUpdate; + for (const std::unique_ptr ¶m : work->input.configUpdate) { + configUpdate.push_back(param.get()); + } + std::vector> failures; + mIntf->config_vb(configUpdate, C2_MAY_BLOCK, &failures); + + std::shared_ptr hdrStaticInfo = + mIntf->getHdrStaticMetadata(); + uint32_t dataspace = mIntf->getDataSpace(); + + std::shared_ptr buffer; + if (!work->input.buffers.empty()) { + buffer = work->input.buffers.front(); + } + std::shared_ptr outC2Buffer; + status_t err = OK; + if (buffer) { + if (buffer->hasInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE)) { + std::shared_ptr info = + buffer->getInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE); + std::unique_ptr flipped = C2Param::CopyAsStream( + *info, false /* output */, info->stream()); + hdrStaticInfo.reset(static_cast( + flipped.release())); + } + const C2Handle *c2Handle = + buffer->data().graphicBlocks().front().handle(); + uint32_t width, height, format, stride, igbp_slot, generation; + uint64_t usage, igbp_id; + _UnwrapNativeCodec2GrallocMetadata( + c2Handle, &width, &height, &format, &usage, &stride, &generation, + &igbp_id, &igbp_slot); + native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle); + sp srcBuffer = new GraphicBuffer( + grallocHandle, GraphicBuffer::CLONE_HANDLE, + width, height, format, 1, usage, stride); + + std::shared_ptr dstBlock; + C2BlockPool::local_id_t poolId = mIntf->getPoolId(); + std::shared_ptr pool; + GetCodec2BlockPool(poolId, thiz, &pool); + pool->fetchGraphicBlock( + width, height, HAL_PIXEL_FORMAT_RGBA_8888, C2AndroidMemoryUsage::FromGrallocUsage(kDstUsage), + &dstBlock); + outC2Buffer = C2Buffer::CreateGraphicBuffer( + dstBlock->share(C2Rect(width, height), C2Fence())); + c2Handle = dstBlock->handle(); + _UnwrapNativeCodec2GrallocMetadata( + c2Handle, &width, &height, &format, &usage, &stride, &generation, + &igbp_id, &igbp_slot); + grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle); + sp dstBuffer = new GraphicBuffer( + grallocHandle, GraphicBuffer::CLONE_HANDLE, + width, height, format, 1, usage, stride); + + Rect sourceCrop(0, 0, width, height); + + renderengine::DisplaySettings clientCompositionDisplay; + std::vector clientCompositionLayers; + + clientCompositionDisplay.physicalDisplay = sourceCrop; + clientCompositionDisplay.clip = sourceCrop; + + clientCompositionDisplay.outputDataspace = ui::Dataspace::V0_SRGB; + clientCompositionDisplay.maxLuminance = kDefaultMaxLumiance; + clientCompositionDisplay.clearRegion = Region::INVALID_REGION; + renderengine::LayerSettings layerSettings; + layerSettings.geometry.boundaries = sourceCrop.toFloatRect(); + layerSettings.alpha = 1.0f; + + layerSettings.sourceDataspace = static_cast(dataspace); + + // from BufferLayer + layerSettings.source.buffer.buffer = srcBuffer; + layerSettings.source.buffer.isOpaque = true; + // TODO: fence + layerSettings.source.buffer.fence = Fence::NO_FENCE; + layerSettings.source.buffer.textureName = textureName; + layerSettings.source.buffer.usePremultipliedAlpha = false; + layerSettings.source.buffer.isY410BT2020 = + (layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_PQ || + layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_HLG) && + format == HAL_PIXEL_FORMAT_RGBA_1010102; + layerSettings.source.buffer.maxMasteringLuminance = + (hdrStaticInfo && *hdrStaticInfo && + hdrStaticInfo->mastering.maxLuminance > 0 && + hdrStaticInfo->mastering.minLuminance > 0) + ? hdrStaticInfo->mastering.maxLuminance : kDefaultMaxMasteringLuminance; + layerSettings.source.buffer.maxContentLuminance = + (hdrStaticInfo && *hdrStaticInfo && hdrStaticInfo->maxCll > 0) + ? hdrStaticInfo->maxCll : kDefaultMaxContentLuminance; + + // Set filtering to false since the capture itself doesn't involve + // any scaling, metadata retriever JNI is scaling the bitmap if + // display size is different from decoded size. If that scaling + // needs to be handled by server side, consider enable this based + // display size vs decoded size. + layerSettings.source.buffer.useTextureFiltering = false; + layerSettings.source.buffer.textureTransform = mat4(); + clientCompositionLayers.push_back(&layerSettings); + + // Use an empty fence for the buffer fence, since we just created the buffer so + // there is no need for synchronization with the GPU. + base::unique_fd bufferFence; + base::unique_fd drawFence; + renderEngine->useProtectedContext(false); + err = renderEngine->drawLayers( + clientCompositionDisplay, clientCompositionLayers, dstBuffer.get(), + /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence); + + sp fence = new Fence(std::move(drawFence)); + + // We can move waiting for fence & sending it back on a separate thread to improve + // efficiency, but leaving it here for simplicity. + if (err != OK) { + LOG(ERROR) << "drawLayers returned err " << err; + } else { + err = fence->wait(500); + if (err != OK) { + LOG(WARNING) << "wait for fence returned err " << err; + } + } + renderEngine->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL); + } + + work->worklets.front()->output.ordinal = work->input.ordinal; + work->worklets.front()->output.flags = work->input.flags; + if (err == OK) { + work->workletsProcessed = 1; + if (outC2Buffer) { + work->worklets.front()->output.buffers.push_back(outC2Buffer); + } + work->result = C2_OK; + } else { + work->result = C2_CORRUPTED; + } + std::list> items; + items.push_back(std::move(work)); + + std::unique_lock lock(mListenerMutex); + mListener->onWorkDone_nb(thiz, std::move(items)); + LOG(VERBOSE) << "sent work #" << workCount; + } + } + + mutable std::timed_mutex mListenerMutex; + std::shared_ptr mListener; + + mutable std::mutex mQueueMutex; + mutable std::condition_variable mQueueCondition; + std::list> mQueue; + + const std::shared_ptr mIntf; + + mutable std::mutex mStateMutex; + enum State { + STOPPED, + RUNNING, + RELEASED, + STARTING, // STOPPED -> RUNNING + STOPPING, // RUNNING -> STOPPED + RESETTING, // <> -> STOPPED + RELEASING, // STOPPED -> RELEASED + } mState; + + mutable std::mutex mProcessingMutex; + std::thread mProcessingThread; + +}; + +// static +const std::string SampleToneMappingFilter::Interface::NAME = "c2.sample.tone-mapper"; +// static +const FilterPlugin_V1::Descriptor SampleToneMappingFilter::Interface::DESCRIPTOR = { + // controlParams + { C2StreamColorAspectsRequestInfo::output::PARAM_TYPE }, + // affectedParams + { + C2StreamHdrStaticInfo::output::PARAM_TYPE, + C2StreamHdr10PlusInfo::output::PARAM_TYPE, + C2StreamColorAspectsInfo::output::PARAM_TYPE, + }, +}; + +class SampleC2ComponentStore : public C2ComponentStore { +public: + SampleC2ComponentStore() + : mReflector(std::make_shared()), + mIntf(mReflector), + mFactories(CreateFactories()) { + } + ~SampleC2ComponentStore() = default; + + C2String getName() const override { return "android.sample.filter-plugin-store"; } + c2_status_t createComponent( + C2String name, std::shared_ptr* const component) override { + if (mFactories.count(name) == 0) { + return C2_BAD_VALUE; + } + return mFactories.at(name)->createComponent(++mNodeId, component); + } + c2_status_t createInterface( + C2String name, std::shared_ptr* const interface) override { + if (mFactories.count(name) == 0) { + return C2_BAD_VALUE; + } + return mFactories.at(name)->createInterface(++mNodeId, interface); + } + std::vector> listComponents() override { + std::vector> ret; + for (const auto &[name, factory] : mFactories) { + ret.push_back(factory->getTraits()); + } + return ret; + } + c2_status_t copyBuffer( + std::shared_ptr, std::shared_ptr) override { + return C2_OMITTED; + } + c2_status_t query_sm( + const std::vector &stackParams, + const std::vector &heapParamIndices, + std::vector>* const heapParams) const override { + return mIntf.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams); + } + c2_status_t config_sm( + const std::vector ¶ms, + std::vector>* const failures) override { + return mIntf.config(params, C2_MAY_BLOCK, failures); + } + std::shared_ptr getParamReflector() const override { + return mReflector; + } + c2_status_t querySupportedParams_nb( + std::vector> * const params) const override { + return mIntf.querySupportedParams(params); + } + c2_status_t querySupportedValues_sm( + std::vector &fields) const override { + return mIntf.querySupportedValues(fields, C2_MAY_BLOCK); + } + +private: + class ComponentFactory { + public: + virtual ~ComponentFactory() = default; + + const std::shared_ptr &getTraits() { return mTraits; } + + virtual c2_status_t createComponent( + c2_node_id_t id, + std::shared_ptr* const component) const = 0; + virtual c2_status_t createInterface( + c2_node_id_t id, + std::shared_ptr* const interface) const = 0; + protected: + ComponentFactory(const std::shared_ptr &traits) + : mTraits(traits) { + } + private: + const std::shared_ptr mTraits; + }; + + template + struct ComponentFactoryImpl : public ComponentFactory { + public: + ComponentFactoryImpl(const std::shared_ptr &traits) + : ComponentFactory(traits) { + } + ~ComponentFactoryImpl() override = default; + c2_status_t createComponent( + c2_node_id_t id, + std::shared_ptr* const component) const override { + *component = std::make_shared(id); + return C2_OK; + } + c2_status_t createInterface( + c2_node_id_t id, + std::shared_ptr* const interface) const override { + *interface = std::make_shared(id); + return C2_OK; + } + }; + + template + static void AddFactory(std::map> *factories) { + std::shared_ptr intf{new typename T::Interface(0)}; + std::shared_ptr traits(new (std::nothrow) C2Component::Traits); + CHECK(C2InterfaceUtils::FillTraitsFromInterface(traits.get(), intf)) + << "Failed to fill traits from interface"; + factories->emplace(traits->name, new ComponentFactoryImpl(traits)); + } + + static std::map> CreateFactories() { + std::map> factories; + AddFactory(&factories); + return factories; + } + + + std::shared_ptr mReflector; + struct Interface : public C2InterfaceHelper { + explicit Interface(std::shared_ptr reflector) + : C2InterfaceHelper(reflector) { + } + } mIntf; + + const std::map> mFactories; + + std::atomic_int32_t mNodeId{0}; +}; + +class SampleFilterPlugin : public FilterPlugin_V1 { +public: + SampleFilterPlugin() : mStore(new SampleC2ComponentStore) {} + ~SampleFilterPlugin() override = default; + + std::shared_ptr getComponentStore() override { + return mStore; + } + + bool describe(C2String name, Descriptor *desc) override { + if (name == SampleToneMappingFilter::Interface::NAME) { + *desc = SampleToneMappingFilter::Interface::DESCRIPTOR; + return true; + } + return false; + } + + bool isFilteringEnabled(const std::shared_ptr &intf) override { + if (intf->getName() == SampleToneMappingFilter::Interface::NAME) { + return SampleToneMappingFilter::Interface::IsFilteringEnabled(intf); + } + return false; + } + + c2_status_t queryParamsForPreviousComponent( + const std::shared_ptr &intf, + std::vector> *params) override { + if (intf->getName() == SampleToneMappingFilter::Interface::NAME) { + return SampleToneMappingFilter::Interface::QueryParamsForPreviousComponent( + intf, params); + } + return C2_BAD_VALUE; + } + +private: + std::shared_ptr mStore; +}; + +} // namespace android + +extern "C" { + +int32_t GetFilterPluginVersion() { + return ::android::SampleFilterPlugin::VERSION; +} + +void *CreateFilterPlugin() { + return new ::android::SampleFilterPlugin; +} + +void DestroyFilterPlugin(void *plugin) { + delete (::android::SampleFilterPlugin *)plugin; +} + +} // extern "C" diff --git a/media/codec2/hidl/services/Android.bp b/media/codec2/hidl/services/Android.bp index 4ec67b006f1daa1de43563f8afa86e242045126c..b36e80ab10a133ea9097c6dd006c6ac8a1ef617b 100644 --- a/media/codec2/hidl/services/Android.bp +++ b/media/codec2/hidl/services/Android.bp @@ -39,11 +39,11 @@ package { } cc_binary { - name: "android.hardware.media.c2@1.1-default-service", + name: "android.hardware.media.c2@1.2-default-service", vendor: true, relative_install_path: "hw", - init_rc: ["android.hardware.media.c2@1.1-default-service.rc"], + init_rc: ["android.hardware.media.c2@1.2-default-service.rc"], defaults: ["libcodec2-hidl-defaults"], srcs: [ @@ -55,7 +55,7 @@ cc_binary { "libavservices_minijail", "libbinder", ], - required: ["android.hardware.media.c2@1.1-default-seccomp_policy"], + required: ["android.hardware.media.c2@1.2-default-seccomp_policy"], // The content in manifest_media_c2_V1_1_default.xml can be included // directly in the main device manifest.xml file or via vintf_fragments. @@ -73,23 +73,23 @@ cc_binary { // Files in the "seccomp_policy" subdirectory are only provided as examples. // They may not work on some devices and/or architectures without modification. prebuilt_etc { - name: "android.hardware.media.c2@1.1-default-seccomp_policy", + name: "android.hardware.media.c2@1.2-default-seccomp_policy", vendor: true, sub_dir: "seccomp_policy", // If a specific architecture is targeted, multiple choices are not needed. arch: { arm: { - src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy", + src: "seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy", }, arm64: { - src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy", + src: "seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy", }, x86: { - src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy", + src: "seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy", }, x86_64: { - src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy", + src: "seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy", }, }, diff --git a/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc b/media/codec2/hidl/services/android.hardware.media.c2@1.2-default-service.rc similarity index 55% rename from media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc rename to media/codec2/hidl/services/android.hardware.media.c2@1.2-default-service.rc index 44f2d8e37d234a71b2b188dd02b1db7bf7e1b232..03f6e3d012ccc1fced7a78983141279e0df97bcc 100644 --- a/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc +++ b/media/codec2/hidl/services/android.hardware.media.c2@1.2-default-service.rc @@ -1,4 +1,4 @@ -service android-hardware-media-c2-hal-1-1 /vendor/bin/hw/android.hardware.media.c2@1.1-default-service +service android-hardware-media-c2-hal-1-2 /vendor/bin/hw/android.hardware.media.c2@1.2-default-service class hal user mediacodec group camera mediadrm drmrpc diff --git a/media/codec2/hidl/services/manifest_media_c2_V1_2_default.xml b/media/codec2/hidl/services/manifest_media_c2_V1_2_default.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5e8d8724b21b26cdc7c2b5c11734a7b53ebfc03 --- /dev/null +++ b/media/codec2/hidl/services/manifest_media_c2_V1_2_default.xml @@ -0,0 +1,11 @@ + + + android.hardware.media.c2 + hwbinder + 1.2 + + IComponentStore + default + + + diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy similarity index 100% rename from media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy similarity index 97% rename from media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy index 85fd28d7bc9ff83c69416838f7d4f069c7d87f86..5d0284f6d913a4969385deefc835dc8621d3804b 100644 --- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy +++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy @@ -1,4 +1,4 @@ -# Copyright (C) 2019 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy similarity index 95% rename from media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy index d9c4045f825878c2d61226bd35b2c41711525bc5..056c690b1c43cbbc38fa6c916f6664edae137d66 100644 --- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy +++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy @@ -1,4 +1,4 @@ -# Copyright (C) 2017 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy similarity index 95% rename from media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy index d9c4045f825878c2d61226bd35b2c41711525bc5..056c690b1c43cbbc38fa6c916f6664edae137d66 100644 --- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy +++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy @@ -1,4 +1,4 @@ -# Copyright (C) 2017 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/media/codec2/hidl/services/vendor.cpp b/media/codec2/hidl/services/vendor.cpp index 3ddb039e78753604ac13f0e91a8e5e9b61637090..0d0684d2362397b36a95b6781fa011e7f24d4063 100644 --- a/media/codec2/hidl/services/vendor.cpp +++ b/media/codec2/hidl/services/vendor.cpp @@ -15,11 +15,11 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "android.hardware.media.c2@1.1-service" +#define LOG_TAG "android.hardware.media.c2@1.2-service" #include #include -#include +#include #include #include @@ -31,13 +31,13 @@ // "android.hardware.media.c2@1.1-default-seccomp_policy" in Android.bp. static constexpr char kBaseSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/" - "android.hardware.media.c2@1.1-default-seccomp-policy"; + "android.hardware.media.c2@1.2-default-seccomp-policy"; // Additional seccomp permissions can be added in this file. // This file does not exist by default. static constexpr char kExtSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/" - "android.hardware.media.c2@1.1-extended-seccomp-policy"; + "android.hardware.media.c2@1.2-extended-seccomp-policy"; class StoreImpl : public C2ComponentStore { public: @@ -164,7 +164,7 @@ private: int main(int /* argc */, char** /* argv */) { using namespace ::android; - LOG(DEBUG) << "android.hardware.media.c2@1.1-service starting..."; + LOG(DEBUG) << "android.hardware.media.c2@1.2-service starting..."; // Set up minijail to limit system calls. signal(SIGPIPE, SIG_IGN); @@ -180,7 +180,7 @@ int main(int /* argc */, char** /* argv */) { // Create IComponentStore service. { - using namespace ::android::hardware::media::c2::V1_1; + using namespace ::android::hardware::media::c2::V1_2; sp store; // TODO: Replace this with diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 24604905b563a9e829e8a456ae60cfda6889cc82..c049187c6a60318fd72702ba21f4ceac7379541d 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -90,6 +90,10 @@ public: } } + void setPriority(int priority) { + androidSetThreadPriority(getTid(), priority); + } + protected: bool threadLoop() override { constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms @@ -419,6 +423,8 @@ status_t C2OMXNode::emptyBuffer( if (err != OK) { (void)fd0.release(); (void)fd1.release(); + native_handle_close(handle); + native_handle_delete(handle); return UNKNOWN_ERROR; } block = _C2BlockFactory::CreateGraphicBlock(alloc); @@ -527,4 +533,8 @@ android_dataspace C2OMXNode::getDataspace() { return *mDataspace.lock(); } +void C2OMXNode::setPriority(int priority) { + mQueueThread->setPriority(priority); +} + } // namespace android diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h index 9c04969d7433aea7b33434e5b835f08fbd0d5196..6669318e6e9cf6954e4dba2ab2f2d85d1e38b198 100644 --- a/media/codec2/sfplugin/C2OMXNode.h +++ b/media/codec2/sfplugin/C2OMXNode.h @@ -98,6 +98,11 @@ struct C2OMXNode : public BnOMXNode { */ android_dataspace getDataspace(); + /** + * Sets priority of the queue thread. + */ + void setPriority(int priority); + private: std::weak_ptr mComp; sp mBufferSource; diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 824793788b15da75c54e81edbfc5954435bb2d3d..754cf21ccfa6faab9e53b5ccae9cbebb604eeb33 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -398,6 +398,14 @@ public: // consumer usage is queried earlier. + // priority + if (mConfig.mPriority != config.mPriority) { + if (config.mPriority != INT_MAX) { + mNode->setPriority(config.mPriority); + } + mConfig.mPriority = config.mPriority; + } + if (status.str().empty()) { ALOGD("ISConfig not changed"); } else { @@ -736,17 +744,18 @@ void CCodec::allocate(const sp &codecInfo) { std::make_shared(client)); } - std::shared_ptr comp = - Codec2Client::CreateComponentByName( + std::shared_ptr comp; + c2_status_t status = Codec2Client::CreateComponentByName( componentName.c_str(), mClientListener, + &comp, &client); - if (!comp) { - ALOGE("Failed Create component: %s", componentName.c_str()); + if (status != C2_OK) { + ALOGE("Failed Create component: %s, error=%d", componentName.c_str(), status); Mutexed::Locked state(mState); state->set(RELEASED); state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + mCallback->onError((status == C2_NO_MEMORY ? NO_MEMORY : UNKNOWN_ERROR), ACTION_CODE_FATAL); state.lock(); return; } @@ -986,6 +995,7 @@ void CCodec::configure(const sp &msg) { } } config->mISConfig->mUsage = 0; + config->mISConfig->mPriority = INT_MAX; } /* @@ -1177,6 +1187,16 @@ void CCodec::configure(const sp &msg) { configUpdate.push_back(std::move(qp)); } + int32_t background = 0; + if ((config->mDomain & Config::IS_VIDEO) + && msg->findInt32("android._background-mode", &background) + && background) { + androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND); + if (config->mISConfig) { + config->mISConfig->mPriority = ANDROID_PRIORITY_BACKGROUND; + } + } + err = config->setParameters(comp, configUpdate, C2_DONT_BLOCK); if (err != OK) { ALOGW("failed to configure c2 params"); @@ -1210,6 +1230,7 @@ void CCodec::configure(const sp &msg) { C2AndroidMemoryUsage androidUsage(C2MemoryUsage(usage.value)); config->mISConfig->mUsage = androidUsage.asGrallocUsage(); } + config->mInputFormat->setInt64("android._C2MemoryUsage", usage.value); } // NOTE: we don't blindly use client specified input size if specified as clients @@ -2453,7 +2474,18 @@ void CCodec::initiateReleaseIfStuck() { return; } - ALOGW("previous call to %s exceeded timeout", name.c_str()); + C2String compName; + { + Mutexed::Locked state(mState); + if (!state->comp) { + ALOGD("previous call to %s exceeded timeout " + "and the component is already released", name.c_str()); + return; + } + compName = state->comp->getName(); + } + ALOGW("[%s] previous call to %s exceeded timeout", compName.c_str(), name.c_str()); + initiateRelease(false); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); } @@ -2703,15 +2735,17 @@ status_t CCodec::CanFetchLinearBlock( return OK; } } - uint64_t minUsage = usage.expected; - uint64_t maxUsage = ~0ull; std::set allocators; GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators); if (allocators.empty()) { *isCompatible = false; return OK; } + + uint64_t minUsage = 0; + uint64_t maxUsage = ~0ull; CalculateMinMaxUsage(names, &minUsage, &maxUsage); + minUsage |= usage.expected; *isCompatible = ((maxUsage & minUsage) == minUsage); return OK; } @@ -2738,14 +2772,16 @@ static std::shared_ptr GetPool(C2Allocator::id_t allocId) { // static std::shared_ptr CCodec::FetchLinearBlock( size_t capacity, const C2MemoryUsage &usage, const std::vector &names) { - uint64_t minUsage = usage.expected; - uint64_t maxUsage = ~0ull; std::set allocators; GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators); if (allocators.empty()) { allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR); } + + uint64_t minUsage = 0; + uint64_t maxUsage = ~0ull; CalculateMinMaxUsage(names, &minUsage, &maxUsage); + minUsage |= usage.expected; if ((maxUsage & minUsage) != minUsage) { allocators.clear(); allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index ba3c9cadfae46f54d2d18a5e8f1293c7f964d069..e29ec1123caf7c83e030661c338b55a0c0b6a8f9 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -161,6 +161,10 @@ CCodecBufferChannel::CCodecBufferChannel( output->outputDelay = 0u; output->numSlots = kSmoothnessFactor; } + { + Mutexed::Locked pools(mBlockPools); + pools->outputPoolId = C2BlockPool::BASIC_LINEAR; + } } CCodecBufferChannel::~CCodecBufferChannel() { @@ -1190,9 +1194,10 @@ status_t CCodecBufferChannel::start( if (outputFormat != nullptr) { sp outputSurface; uint32_t outputGeneration; + int maxDequeueCount = 0; { Mutexed::Locked output(mOutputSurface); - output->maxDequeueBuffers = numOutputSlots + + maxDequeueCount = output->maxDequeueBuffers = numOutputSlots + reorderDepth.value + kRenderingDepth; outputSurface = output->surface ? output->surface->getIGraphicBufferProducer() : nullptr; @@ -1204,10 +1209,13 @@ status_t CCodecBufferChannel::start( bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC); C2BlockPool::local_id_t outputPoolId_; + C2BlockPool::local_id_t prevOutputPoolId; { Mutexed::Locked pools(mBlockPools); + prevOutputPoolId = pools->outputPoolId; + // set default allocator ID. pools->outputAllocatorId = (graphic) ? C2PlatformAllocatorStore::GRALLOC : preferredLinearId; @@ -1301,6 +1309,15 @@ status_t CCodecBufferChannel::start( outputPoolId_ = pools->outputPoolId; } + if (prevOutputPoolId != C2BlockPool::BASIC_LINEAR + && prevOutputPoolId != C2BlockPool::BASIC_GRAPHIC) { + c2_status_t err = mComponent->destroyBlockPool(prevOutputPoolId); + if (err != C2_OK) { + ALOGW("Failed to clean up previous block pool %llu - %s (%d)\n", + (unsigned long long) prevOutputPoolId, asString(err), err); + } + } + Mutexed::Locked output(mOutput); output->outputDelay = outputDelayValue; output->numSlots = numOutputSlots; @@ -1328,7 +1345,17 @@ status_t CCodecBufferChannel::start( mComponent->setOutputSurface( outputPoolId_, outputSurface, - outputGeneration); + outputGeneration, + maxDequeueCount); + } else { + // configure CPU read consumer usage + C2StreamUsageTuning::output outputUsage{0u, C2MemoryUsage::CPU_READ}; + std::vector> failures; + err = mComponent->config({ &outputUsage }, C2_MAY_BLOCK, &failures); + // do not print error message for now as most components may not yet + // support this setting + ALOGD_IF(err != C2_BAD_INDEX, "[%s] Configured output usage [%#llx]", + mName, (long long)outputUsage.value); } if (oStreamFormat.value == C2BufferData::LINEAR) { @@ -1799,10 +1826,17 @@ bool CCodecBufferChannel::handleWork( mCCodecCallback->onOutputBuffersChanged(); } if (needMaxDequeueBufferCountUpdate) { - Mutexed::Locked output(mOutputSurface); - output->maxDequeueBuffers = numOutputSlots + reorderDepth + kRenderingDepth; - if (output->surface) { - output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); + int maxDequeueCount = 0; + { + Mutexed::Locked output(mOutputSurface); + maxDequeueCount = output->maxDequeueBuffers = + numOutputSlots + reorderDepth + kRenderingDepth; + if (output->surface) { + output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); + } + } + if (maxDequeueCount > 0) { + mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount); } } @@ -1961,10 +1995,11 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { & ((1 << 10) - 1)); sp producer; + int maxDequeueCount = mOutputSurface.lock()->maxDequeueBuffers; if (newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); newSurface->setDequeueTimeout(kDequeueTimeoutNs); - newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers); + newSurface->setMaxDequeuedBufferCount(maxDequeueCount); producer = newSurface->getIGraphicBufferProducer(); producer->setGenerationNumber(generation); } else { @@ -1984,7 +2019,8 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { if (mComponent->setOutputSurface( outputPoolId, producer, - generation) != C2_OK) { + generation, + maxDequeueCount) != C2_OK) { ALOGI("[%s] setSurface: component setOutputSurface failed", mName); return INVALID_OPERATION; } diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp index cfa89cf413670759cbb1e2bf01ed7f4db9d24712..d7e0b1eb8dd9c9da5393cdae919185236d9bb5e7 100644 --- a/media/codec2/sfplugin/CCodecBuffers.cpp +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -792,8 +792,9 @@ sp LinearInputBuffers::Alloc( capacity = kMaxLinearBufferSize; } - // TODO: read usage from intf - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + int64_t usageValue = 0; + (void)format->findInt64("android._C2MemoryUsage", &usageValue); + C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}; std::shared_ptr block; c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block); @@ -1039,8 +1040,9 @@ size_t GraphicInputBuffers::numActiveSlots() const { } sp GraphicInputBuffers::createNewBuffer() { - // TODO: read usage from intf - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + int64_t usageValue = 0; + (void)mFormat->findInt64("android._C2MemoryUsage", &usageValue); + C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}; return AllocateGraphicBuffer( mPool, mFormat, extractPixelFormat(mFormat), usage, mLocalBufferPool); } @@ -1300,17 +1302,7 @@ RawGraphicOutputBuffers::RawGraphicOutputBuffers( sp RawGraphicOutputBuffers::wrap(const std::shared_ptr &buffer) { if (buffer == nullptr) { - sp c2buffer = ConstGraphicBlockBuffer::AllocateEmpty( - mFormat, - [lbp = mLocalBufferPool](size_t capacity) { - return lbp->newBuffer(capacity); - }); - if (c2buffer == nullptr) { - ALOGD("[%s] ConstGraphicBlockBuffer::AllocateEmpty failed", mName); - return nullptr; - } - c2buffer->setRange(0, 0); - return c2buffer; + return new Codec2Buffer(mFormat, new ABuffer(nullptr, 0)); } else { return ConstGraphicBlockBuffer::Allocate( mFormat, diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index 5646095db9922704113ffb7507fdd48d4cdb9e71..c2751873c2e7c96ab0b374917382baa8e025b860 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -909,6 +909,8 @@ void CCodecConfig::initializeStandardParams() { } })); + add(ConfigMapper("android._encoding-quality-level", C2_PARAMKEY_ENCODING_QUALITY_LEVEL, "value") + .limitTo(D::ENCODER & (D::CONFIG | D::PARAM))); add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value") .limitTo(D::ENCODER & (D::CONFIG | D::PARAM))); add(ConfigMapper(KEY_FLAC_COMPRESSION_LEVEL, C2_PARAMKEY_COMPLEXITY, "value") @@ -939,12 +941,12 @@ void CCodecConfig::initializeStandardParams() { })); add(ConfigMapper("android._trigger-tunnel-peek", C2_PARAMKEY_TUNNEL_START_RENDER, "value") - .limitTo(D::PARAM & D::VIDEO & D::DECODER) - .withMapper([](C2Value v) -> C2Value { - int32_t value = 0; - (void)v.get(&value); - return value == 0 ? C2_FALSE : C2_TRUE; - })); + .limitTo(D::PARAM & D::VIDEO & D::DECODER) + .withMapper([](C2Value v) -> C2Value { + int32_t value = 0; + (void)v.get(&value); + return value == 0 ? C2_FALSE : C2_TRUE; + })); /* still to do constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown"; diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 8fc2ef55ad6fb58e7d5ecee6ab5fd125f1875daf..4070478918468191376093673c286b5424ed9539 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -491,7 +491,7 @@ public: * align(mHeight, 64) / plane.rowSampling; } - if ((maxPtr - minPtr + 1) <= planeSize) { + if (minPtr == mView.data()[0] && (maxPtr - minPtr + 1) <= planeSize) { // FIXME: this is risky as reading/writing data out of bound results // in an undefined behavior, but gralloc does assume a // contiguous mapping @@ -716,6 +716,8 @@ std::shared_ptr GraphicMetadataBuffer::asC2Buffer() { c2_status_t err = mAlloc->priorGraphicAllocation(handle, &alloc); if (err != C2_OK) { ALOGD("Failed to wrap VideoNativeMetadata into C2GraphicAllocation"); + native_handle_close(handle); + native_handle_delete(handle); return nullptr; } std::shared_ptr block = _C2BlockFactory::CreateGraphicBlock(alloc); diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 9b3d3fee6f6508a330610ddda16b011471da4f89..7c4bfb606d4b77dd1217ea5d98af162e1c131953 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -343,6 +343,59 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // parse default XML files parser.parseXmlFilesInSearchDirs(); + // The mainline modules for media may optionally include some codec shaping information. + // Based on vendor partition SDK, and the brand/product/device information + // (expect to be empty in almost always) + // + { + // get build info so we know what file to search + // ro.vendor.build.fingerprint + std::string fingerprint = base::GetProperty("ro.vendor.build.fingerprint", + "brand/product/device:"); + ALOGV("property_get for ro.vendor.build.fingerprint == '%s'", fingerprint.c_str()); + + // ro.vendor.build.version.sdk + std::string sdk = base::GetProperty("ro.vendor.build.version.sdk", "0"); + ALOGV("property_get for ro.vendor.build.version.sdk == '%s'", sdk.c_str()); + + std::string brand; + std::string product; + std::string device; + size_t pos1; + pos1 = fingerprint.find('/'); + if (pos1 != std::string::npos) { + brand = fingerprint.substr(0, pos1); + size_t pos2 = fingerprint.find('/', pos1+1); + if (pos2 != std::string::npos) { + product = fingerprint.substr(pos1+1, pos2 - pos1 - 1); + size_t pos3 = fingerprint.find('/', pos2+1); + if (pos3 != std::string::npos) { + device = fingerprint.substr(pos2+1, pos3 - pos2 - 1); + size_t pos4 = device.find(':'); + if (pos4 != std::string::npos) { + device.resize(pos4); + } + } + } + } + + ALOGV("parsed: sdk '%s' brand '%s' product '%s' device '%s'", + sdk.c_str(), brand.c_str(), product.c_str(), device.c_str()); + + std::string base = "/apex/com.android.media/etc/formatshaper"; + + // looking in these directories within the apex + const std::vector modulePathnames = { + base + "/" + sdk + "/" + brand + "/" + product + "/" + device, + base + "/" + sdk + "/" + brand + "/" + product, + base + "/" + sdk + "/" + brand, + base + "/" + sdk, + base + }; + + parser.parseXmlFilesInSearchDirs( { "media_codecs_shaping.xml" }, modulePathnames); + } + if (parser.getParsingStatus() != OK) { ALOGD("XML parser no good"); return OK; diff --git a/media/codec2/sfplugin/FrameReassembler.cpp b/media/codec2/sfplugin/FrameReassembler.cpp index b52506cc8e69f0f411575051dc8faec86f85dbc8..cb8b6ab0260c00d976a68b26503deae36f50f03f 100644 --- a/media/codec2/sfplugin/FrameReassembler.cpp +++ b/media/codec2/sfplugin/FrameReassembler.cpp @@ -160,7 +160,7 @@ c2_status_t FrameReassembler::process( if (mWriteView->error() != C2_OK) { return mWriteView->error(); } - ALOGV("buffer={offset=%zu size=%zu) copySize=%zu", + ALOGV("buffer={offset=%zu size=%zu} copySize=%zu", buffer->offset(), buffer->size(), copySize); memcpy(mWriteView->base(), buffer->data(), copySize); mWriteView->setOffset(0u); diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h index 50d600cc1d680dc312fe5a44dc7547fbbeaf40bc..44ba78aef83c391d278a11460a67215d02b97ba1 100644 --- a/media/codec2/sfplugin/InputSurfaceWrapper.h +++ b/media/codec2/sfplugin/InputSurfaceWrapper.h @@ -79,6 +79,7 @@ public: float mFixedAdjustedFps = 0.0; // fixed fps via PTS manipulation float mMinAdjustedFps = 0.0; // minimum fps via PTS manipulation uint64_t mUsage = 0; // consumer usage + int mPriority = INT_MAX; // priority of queue thread (if any); INT_MAX for no-op }; /** diff --git a/media/codec2/sfplugin/TEST_MAPPING b/media/codec2/sfplugin/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..045e5b59633e145d09bd724f7421250920028c83 --- /dev/null +++ b/media/codec2/sfplugin/TEST_MAPPING @@ -0,0 +1,12 @@ +// mappings for frameworks/av/media/codec2/sfplugin +{ + "presubmit": [ + // failing 1 of 11 + // TODO(b/156167471) + // { "name": "ccodec_unit_test" }, + + // failing 4 of 17, around max-input-size defaults & overrides + // TODO(b/156167471) + //{ "name": "mc_sanity_test"} + ] +} diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp index f9c79693612465b1377452e563ac0484442339cb..92f37540aa1a45e7615898ba4692efd0feb4e2a4 100644 --- a/media/codec2/sfplugin/tests/Android.bp +++ b/media/codec2/sfplugin/tests/Android.bp @@ -9,6 +9,7 @@ package { cc_test { name: "ccodec_unit_test", + test_suites: ["device-tests"], srcs: [ "CCodecBuffers_test.cpp", @@ -53,6 +54,7 @@ cc_test { cc_test { name: "mc_sanity_test", + test_suites: ["device-tests"], srcs: [ "MediaCodec_sanity_test.cpp", diff --git a/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp b/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp index 66b76228129af301c214ed496e44b583dd59d151..41e4fffc9ca116cc681b9d9987760ec6a7e80352 100644 --- a/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp +++ b/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp @@ -106,6 +106,19 @@ TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) { } } +TEST(RawGraphicOutputBuffersTest, WrapNullBuffer) { + constexpr int32_t kWidth = 320; + constexpr int32_t kHeight = 240; + + std::shared_ptr buffers = + GetRawGraphicOutputBuffers(kWidth, kHeight); + + sp buffer = buffers->wrap(nullptr); + ASSERT_EQ(nullptr, buffer->base()); + ASSERT_EQ(0, buffer->size()); + ASSERT_EQ(0, buffer->offset()); +} + TEST(RawGraphicOutputBuffersTest, FlexYuvColorFormat) { constexpr int32_t kWidth = 320; constexpr int32_t kHeight = 240; diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp index 8ebb6c08a0a9aa93d46528a2f54980d2a8ceb78a..b858fa56b69325d8ac3994a82a45ace183259a96 100644 --- a/media/codec2/tests/Android.bp +++ b/media/codec2/tests/Android.bp @@ -9,6 +9,7 @@ package { cc_test { name: "codec2_core_param_test", + test_suites: ["device-tests"], srcs: [ "C2Param_test.cpp", @@ -37,6 +38,7 @@ cc_test { cc_test { name: "codec2_vndk_test", + test_suites: ["device-tests"], srcs: [ "C2_test.cpp", diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index 0401c1d76a2277b6b1295fcbb624f343cb364a36..be81c8445b55f654927118498984a884c5f32e57 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -36,9 +36,11 @@ cc_library { "C2Buffer.cpp", "C2Config.cpp", "C2DmaBufAllocator.cpp", + "C2Fence.cpp", "C2PlatformStorePluginLoader.cpp", "C2Store.cpp", "platform/C2BqBuffer.cpp", + "platform/C2SurfaceSyncObj.cpp", "types.cpp", "util/C2Debug.cpp", "util/C2InterfaceHelper.cpp", diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index 54f4cff9a2bf07c7c2e88c1c09f8b67133048c01..6a7f19cc2be41b6f094b6efad4de818f01755cb3 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -42,7 +42,9 @@ namespace /* unnamed */ { * Usage mask that is passed through from gralloc to Codec 2.0 usage. */ PASSTHROUGH_USAGE_MASK = - ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED) + ~static_cast(GRALLOC_USAGE_SW_READ_MASK | + GRALLOC_USAGE_SW_WRITE_MASK | + GRALLOC_USAGE_PROTECTED) }; // verify that passthrough mask is within the platform mask diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c5183ea9484912887004b81aaa597d023df37ed --- /dev/null +++ b/media/codec2/vndk/C2Fence.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "C2FenceFactory" +#include + +#include +#include + +class C2Fence::Impl { +public: + virtual c2_status_t wait(c2_nsecs_t timeoutNs) = 0; + + virtual bool valid() const = 0; + + virtual bool ready() const = 0; + + virtual int fd() const = 0; + + virtual bool isHW() const = 0; + + virtual ~Impl() = default; + + Impl() = default; +}; + +c2_status_t C2Fence::wait(c2_nsecs_t timeoutNs) { + if (mImpl) { + return mImpl->wait(timeoutNs); + } + // null fence is always signalled. + return C2_OK; +} + +bool C2Fence::valid() const { + if (mImpl) { + return mImpl->valid(); + } + // null fence is always valid. + return true; +} + +bool C2Fence::ready() const { + if (mImpl) { + return mImpl->ready(); + } + // null fence is always signalled. + return true; +} + +int C2Fence::fd() const { + if (mImpl) { + return mImpl->fd(); + } + // null fence does not have fd. + return -1; +} + +bool C2Fence::isHW() const { + if (mImpl) { + return mImpl->isHW(); + } + return false; +} + +/** + * Fence implementation for C2BufferQueueBlockPool based block allocation. + * The implementation supports all C2Fence interface except fd(). + */ +class _C2FenceFactory::SurfaceFenceImpl: public C2Fence::Impl { +public: + virtual c2_status_t wait(c2_nsecs_t timeoutNs) { + if (mPtr) { + return mPtr->waitForChange(mWaitId, timeoutNs); + } + return C2_OK; + } + + virtual bool valid() const { + return mPtr; + } + + virtual bool ready() const { + uint32_t status; + if (mPtr) { + mPtr->lock(); + status = mPtr->getWaitIdLocked(); + mPtr->unlock(); + + return status != mWaitId; + } + return true; + } + + virtual int fd() const { + // does not support fd, since this is shared mem and futex based + return -1; + } + + virtual bool isHW() const { + return false; + } + + virtual ~SurfaceFenceImpl() {}; + + SurfaceFenceImpl(std::shared_ptr syncMem, uint32_t waitId) : + mSyncMem(syncMem), + mPtr(syncMem ? syncMem->mem() : nullptr), + mWaitId(syncMem ? waitId : 0) {} +private: + const std::shared_ptr mSyncMem; // This is for life-cycle guarantee + C2SyncVariables *const mPtr; + const uint32_t mWaitId; +}; + +C2Fence::C2Fence(std::shared_ptr impl) : mImpl(impl) {} + +C2Fence _C2FenceFactory::CreateSurfaceFence( + std::shared_ptr syncMem, + uint32_t waitId) { + if (syncMem) { + C2Fence::Impl *p + = new _C2FenceFactory::SurfaceFenceImpl(syncMem, waitId); + if (p->valid()) { + return C2Fence(std::shared_ptr(p)); + } else { + delete p; + } + } + return C2Fence(); +} diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h index 578cf766531424e03bd2ca538f6600a59c59b1ea..1da3e1416640afc88207035b828499d01183f17b 100644 --- a/media/codec2/vndk/include/C2AllocatorGralloc.h +++ b/media/codec2/vndk/include/C2AllocatorGralloc.h @@ -37,7 +37,8 @@ native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle); * Wrap the gralloc handle and metadata into Codec2 handle recognized by * C2AllocatorGralloc. * - * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete. + * @return a new NON-OWNING C2Handle that must be closed and deleted using native_handle_close and + * native_handle_delete. */ C2Handle *WrapNativeCodec2GrallocHandle( const native_handle_t *const handle, diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h index e1a81386660e7ce9a4fab4219786c2f82a9541f2..b2636e963091f96e15fd78b7ac780f04ba5a69bd 100644 --- a/media/codec2/vndk/include/C2BqBufferPriv.h +++ b/media/codec2/vndk/include/C2BqBufferPriv.h @@ -20,9 +20,14 @@ #include #include +#include #include +namespace android { +class GraphicBuffer; +} // namespace android + class C2BufferQueueBlockPool : public C2BlockPool { public: C2BufferQueueBlockPool(const std::shared_ptr &allocator, const local_id_t localId); @@ -44,6 +49,14 @@ public: C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) override; + virtual c2_status_t fetchGraphicBlock( + uint32_t width, + uint32_t height, + uint32_t format, + C2MemoryUsage usage, + std::shared_ptr *block /* nonnull */, + C2Fence *fence /* nonnull */) override; + typedef std::function OnRenderCallback; /** @@ -67,6 +80,27 @@ public: */ virtual void configureProducer(const android::sp &producer); + /** + * Configures an IGBP in order to create blocks. A newly created block is + * dequeued from the configured IGBP. Unique Id of IGBP and the slot number of + * blocks are passed via native_handle. Managing IGBP is responsibility of caller. + * When IGBP is not configured, block will be created via allocator. + * Since zero is not used for Unique Id of IGBP, if IGBP is not configured or producer + * is configured as nullptr, unique id which is bundled in native_handle is zero. + * + * \param producer the IGBP, which will be used to fetch blocks + * \param syncMemory Shared memory for synchronization of allocation & deallocation. + * \param bqId Id of IGBP + * \param generationId Generation Id for rendering output + * \param consumerUsage consumerUsage flagof the IGBP + */ + virtual void configureProducer( + const android::sp &producer, + native_handle_t *syncMemory, + uint64_t bqId, + uint32_t generationId, + uint64_t consumerUsage); + private: const std::shared_ptr mAllocator; const local_id_t mLocalId; @@ -77,4 +111,72 @@ private: friend struct C2BufferQueueBlockPoolData; }; +class C2SurfaceSyncMemory; + +struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { +public: + typedef ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer HGraphicBufferProducer; + + // Create a remote BlockPoolData. + C2BufferQueueBlockPoolData( + uint32_t generation, uint64_t bqId, int32_t bqSlot, + const std::shared_ptr &owner, + const android::sp& producer); + + // Create a local BlockPoolData. + C2BufferQueueBlockPoolData( + uint32_t generation, uint64_t bqId, int32_t bqSlot, + const android::sp& producer, + std::shared_ptr, int noUse); + + virtual ~C2BufferQueueBlockPoolData() override; + + virtual type_t getType() const override; + + int migrate(const android::sp& producer, + uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId, + android::sp& graphicBuffer, uint32_t oldGeneration, + std::shared_ptr syncMem); + +private: + friend struct _C2BlockFactory; + + // Methods delegated from _C2BlockFactory. + void getBufferQueueData(uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) const; + bool holdBlockFromBufferQueue(const std::shared_ptr& owner, + const android::sp& igbp, + std::shared_ptr syncMem); + bool beginTransferBlockToClient(); + bool endTransferBlockToClient(bool transfer); + bool beginAttachBlockToBufferQueue(); + bool endAttachBlockToBufferQueue(const std::shared_ptr& owner, + const android::sp& igbp, + std::shared_ptr syncMem, + uint32_t generation, uint64_t bqId, int32_t bqSlot); + bool displayBlockToBufferQueue(); + + const bool mLocal; + bool mHeld; + + // Data of the corresponding buffer. + uint32_t mGeneration; + uint64_t mBqId; + int32_t mBqSlot; + + // Data of the current IGBP, updated at migrate(). If the values are + // mismatched, then the corresponding buffer will not be cancelled back to + // IGBP at the destructor. + uint32_t mCurrentGeneration; + uint64_t mCurrentBqId; + + bool mTransfer; // local transfer to remote + bool mAttach; // attach on remote + bool mDisplay; // display on remote; + std::weak_ptr mOwner; + android::sp mIgbp; + std::shared_ptr mSyncMem; + mutable std::mutex mLock; +}; + #endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_ diff --git a/media/codec2/vndk/include/C2FenceFactory.h b/media/codec2/vndk/include/C2FenceFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..d4bed26d3aa84bfa460f56e01129ca71a0404816 --- /dev/null +++ b/media/codec2/vndk/include/C2FenceFactory.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_ +#define STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_ + + +#include + +class C2SurfaceSyncMemory; + +/** + * C2Fence implementation factory + */ +struct _C2FenceFactory { + + class SurfaceFenceImpl; + + /* + * Create C2Fence for BufferQueueBased blockpool. + * + * \param syncMem Shared memory object for synchronization between processes. + * \param waitId wait id for tracking status change for C2Fence. + */ + static C2Fence CreateSurfaceFence( + std::shared_ptr syncMem, + uint32_t waitId); +}; + + +#endif // STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_ diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h new file mode 100644 index 0000000000000000000000000000000000000000..ac87fe4c89068237a3d44331952d8f1511500b8f --- /dev/null +++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_ +#define STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_ + +#include +#include +#include + +#include + +/** + * Futex based lock / wait implementation for sharing output buffer allocation + * information between Framework and HAL. + */ +struct C2SyncVariables { + enum SyncStatus : uint32_t { + STATUS_INIT = 0, // When surface configuration starts. + STATUS_ACTIVE = 1, // When surface configuration finishs. + // STATUS_INIT -> STATUS_ACTIVE + STATUS_SWITCHING = 2, // When the surface is replaced by a new surface + // during surface configuration. + // STATUS_ACTIVE -> STATUS_SWITCHING + }; + + /** + * Lock the memory region + */ + int lock(); + + /** + * Unlock the memory region + */ + int unlock(); + + /** + * Set initial dequeued buffer count. + * + * \param maxDequeueCount Initial value of # of max dequeued buffer count + * \param curDequeueCount Initial value of # of current dequeued buffer count + */ + void setInitialDequeueCountLocked(int32_t maxDequeueCount, int32_t curDequeueCount); + + /** + * Get a waitId which will be used to implement fence. + */ + uint32_t getWaitIdLocked(); + + /** + * Return whether the upcoming dequeue operation is not blocked. + * if it's blocked and waitId is non-null, waitId is returned to be used for waiting. + * + * \retval false dequeue operation is blocked now. + * \retval true dequeue operation is possible. + */ + bool isDequeueableLocked(uint32_t *waitId = nullptr); + + /** + * Notify a buffer is queued. Return whether the upcoming dequeue operation + * is not blocked. if it's blocked and waitId is non-null, waitId is returned + * to be used for waiting. + * + * \retval false dequeue operation is blocked now. + * \retval true dequeue operation is possible. + */ + bool notifyQueuedLocked(uint32_t *waitId = nullptr); + + /** + * Notify a buffer is dequeued. + */ + void notifyDequeuedLocked(); + + /** + * Set sync status. + */ + void setSyncStatusLocked(SyncStatus status); + + /** + * Get sync status. + */ + C2SyncVariables::SyncStatus getSyncStatusLocked(); + + /** + * Update current max dequeue count. + */ + void updateMaxDequeueCountLocked(int32_t maxDequeueCount); + + /** + * Wait until status is no longer equal to waitId, or until timeout. + * + * \param waitId internal status for waiting until it is changed. + * \param timeousNs nano seconds to timeout. + * + * \retval C2_TIMEDOUT change does not happen during waiting. + * \retval C2_BAD_VALUE invalid event waiting. + * \retval C2_OK change was signalled. + */ + c2_status_t waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs); + + C2SyncVariables() {} + +private: + /** + * signal one waiter to wake up. + */ + int signal(); + + /** + * signal all waiter to wake up. + */ + int broadcast(); + + /** + * wait for signal or broadcast. + */ + int wait(); + + std::atomic mLock; + std::atomic mCond; + int32_t mMaxDequeueCount; + int32_t mCurDequeueCount; + SyncStatus mStatus; +}; + +/** + * Shared memory in order to synchronize information for Surface(IGBP) + * based output buffer allocation. + */ +class C2SurfaceSyncMemory { +public: + /** + * Shared memory handle in order to synchronize information for + * Surface based output buffer allocation. + */ + struct HandleSyncMem : public native_handle_t { + HandleSyncMem(int fd, size_t size) : + native_handle_t(cHeader), + mFds{fd}, + mInts{int(size & 0xFFFFFFFF), + int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic} {} + + /** Returns a file descriptor of the shared memory + * \return a file descriptor representing the shared memory + */ + int memFd() const {return mFds.mMem;} + + /** Returns the size of the shared memory */ + size_t size() const { + return size_t(unsigned(mInts.mSizeLo)) + | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32); + } + + /** Check whether the native handle is in the form of HandleSyncMem + * + * \return whether the native handle is compatible + */ + static bool isValid(const native_handle_t * const o); + + protected: + struct { + int mMem; + } mFds; + struct { + int mSizeLo; + int mSizeHi; + int mMagic; + } mInts; + private: + enum { + kMagic = 'ssm\x00', + numFds = sizeof(mFds) / sizeof(int), + numInts = sizeof(mInts) / sizeof(int), + version = sizeof(native_handle_t) + }; + const static native_handle_t cHeader; + }; + + /** + * Imports a shared memory object from a native handle(The shared memory is already existing). + * This is usually used after native_handle_t is passed via RPC. + * + * \param handle handle representing shared memory for output buffer allocation. + */ + static std::shared_ptr Import(native_handle_t *handle); + + /** + * Creats a shared memory object for synchronization of output buffer allocation. + * Shared memory creation should be done explicitly. + * + * \param fd file descriptor to shared memory + * \param size size of the shared memory + */ + static std::shared_ptr Create(int fd, size_t size); + + /** + * Returns a handle representing the shread memory for synchronization of + * output buffer allocation. + */ + native_handle_t *handle(); + + /** + * Returns synchronization object which will provide synchronization primitives. + * + * \return a ptr to synchronization primitive class + */ + C2SyncVariables *mem(); + + ~C2SurfaceSyncMemory(); + +private: + bool mInit; + HandleSyncMem *mHandle; + C2SyncVariables *mMem; + + C2SurfaceSyncMemory(); +}; + +#endif // STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_ diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h index 4ae946ab7bdbe1ed3697cbed2e229babb49b79bb..c510fca3147986c7876ab81e4d84f623a6249855 100644 --- a/media/codec2/vndk/internal/C2BlockInternal.h +++ b/media/codec2/vndk/internal/C2BlockInternal.h @@ -52,6 +52,8 @@ protected: struct C2BufferQueueBlockPoolData; +class C2SurfaceSyncMemory; + /** * Internal only interface for creating blocks by block pool/buffer passing implementations. * @@ -279,6 +281,8 @@ struct _C2BlockFactory { * anymore. * \param igbp \c IGraphicBufferProducer instance to be assigned to the * block. This is not needed when the block is local. + * \param syncMem Memory block which will support synchronization + * between Framework and HAL. * * \return The previous held status. */ @@ -287,7 +291,8 @@ struct _C2BlockFactory { const std::shared_ptr<_C2BlockPoolData>& poolData, const std::shared_ptr& owner, const ::android::sp<::android::hardware::graphics::bufferqueue:: - V2_0::IGraphicBufferProducer>& igbp = nullptr); + V2_0::IGraphicBufferProducer>& igbp = nullptr, + std::shared_ptr syncMem = nullptr); /** * Prepare a block to be transferred to other process. This blocks @@ -358,6 +363,7 @@ struct _C2BlockFactory { const std::shared_ptr& owner, const ::android::sp<::android::hardware::graphics::bufferqueue:: V2_0::IGraphicBufferProducer>& igbp, + std::shared_ptr, uint32_t generation, uint64_t bqId, int32_t bqSlot); diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index fff12c4b8cd1d95ee98dd63280e5586be7f28f88..169de0c57967e85cc3632a715e846303e53676b8 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -54,59 +56,13 @@ using ::android::hardware::graphics::bufferqueue::V2_0::utils::HFenceWrapper; using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::V2_0 ::IGraphicBufferProducer; -struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { - - bool held; - bool local; - uint32_t generation; - uint64_t bqId; - int32_t bqSlot; - bool transfer; // local transfer to remote - bool attach; // attach on remote - bool display; // display on remote; - std::weak_ptr owner; - sp igbp; - std::shared_ptr localPool; - mutable std::mutex lock; - - virtual type_t getType() const override { - return TYPE_BUFFERQUEUE; - } - - // Create a remote BlockPoolData. - C2BufferQueueBlockPoolData( - uint32_t generation, uint64_t bqId, int32_t bqSlot, - const std::shared_ptr &owner, - const sp& producer); - - // Create a local BlockPoolData. - C2BufferQueueBlockPoolData( - uint32_t generation, uint64_t bqId, int32_t bqSlot, - const std::shared_ptr& pool); - - virtual ~C2BufferQueueBlockPoolData() override; - - int migrate(const sp& producer, - uint32_t toGeneration, uint64_t toBqId, - sp *buffers, uint32_t oldGeneration); -}; - bool _C2BlockFactory::GetBufferQueueData( const std::shared_ptr& data, uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) { if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) { - if (generation) { - const std::shared_ptr poolData = - std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - *generation = poolData->generation; - if (bqId) { - *bqId = poolData->bqId; - } - if (bqSlot) { - *bqSlot = poolData->bqSlot; - } - } + const std::shared_ptr poolData = + std::static_pointer_cast(data); + poolData->getBufferQueueData(generation, bqId, bqSlot); return true; } return false; @@ -115,29 +71,18 @@ bool _C2BlockFactory::GetBufferQueueData( bool _C2BlockFactory::HoldBlockFromBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data, const std::shared_ptr& owner, - const sp& igbp) { + const sp& igbp, + std::shared_ptr syncMem) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - if (!poolData->local) { - poolData->owner = owner; - poolData->igbp = igbp; - } - if (poolData->held) { - poolData->held = true; - return false; - } - poolData->held = true; - return true; + return poolData->holdBlockFromBufferQueue(owner, igbp, syncMem); } bool _C2BlockFactory::BeginTransferBlockToClient( const std::shared_ptr<_C2BlockPoolData>& data) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - poolData->transfer = true; - return true; + return poolData->beginTransferBlockToClient(); } bool _C2BlockFactory::EndTransferBlockToClient( @@ -145,28 +90,14 @@ bool _C2BlockFactory::EndTransferBlockToClient( bool transfer) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - poolData->transfer = false; - if (transfer) { - poolData->held = false; - } - return true; + return poolData->endTransferBlockToClient(transfer); } bool _C2BlockFactory::BeginAttachBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - if (poolData->local || poolData->display || - poolData->attach || !poolData->held) { - return false; - } - if (poolData->bqId == 0) { - return false; - } - poolData->attach = true; - return true; + return poolData->beginAttachBlockToBufferQueue(); } // if display was tried during attach, buffer should be retired ASAP. @@ -174,47 +105,20 @@ bool _C2BlockFactory::EndAttachBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data, const std::shared_ptr& owner, const sp& igbp, + std::shared_ptr syncMem, uint32_t generation, uint64_t bqId, int32_t bqSlot) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - if (poolData->local || !poolData->attach ) { - return false; - } - if (poolData->display) { - poolData->attach = false; - poolData->held = false; - return false; - } - poolData->attach = false; - poolData->held = true; - poolData->owner = owner; - poolData->igbp = igbp; - poolData->generation = generation; - poolData->bqId = bqId; - poolData->bqSlot = bqSlot; - return true; + return poolData->endAttachBlockToBufferQueue(owner, igbp, syncMem, generation, bqId, bqSlot); } bool _C2BlockFactory::DisplayBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data) { const std::shared_ptr poolData = std::static_pointer_cast(data); - std::scoped_lock lock(poolData->lock); - if (poolData->local || poolData->display || !poolData->held) { - return false; - } - if (poolData->bqId == 0) { - return false; - } - poolData->display = true; - if (poolData->attach) { - return false; - } - poolData->held = false; - return true; + return poolData->displayBlockToBufferQueue(); } std::shared_ptr _C2BlockFactory::CreateGraphicBlock( @@ -267,8 +171,8 @@ int64_t getTimestampNow() { return stamp; } -bool getGenerationNumber(const sp &producer, - uint32_t *generation) { +bool getGenerationNumberAndUsage(const sp &producer, + uint32_t *generation, uint64_t *usage) { status_t status{}; int slot{}; bool bufferNeedsReallocation{}; @@ -302,7 +206,7 @@ bool getGenerationNumber(const sp &producer, // instead of a new allocation. transResult = producer->requestBuffer( slot, - [&status, &slotBuffer, &generation]( + [&status, &slotBuffer, &generation, &usage]( HStatus hStatus, HBuffer const& hBuffer, uint32_t generationNumber){ @@ -310,6 +214,7 @@ bool getGenerationNumber(const sp &producer, h2b(hBuffer, &slotBuffer) && slotBuffer) { *generation = generationNumber; + *usage = slotBuffer->getUsage(); slotBuffer->setGenerationNumber(generationNumber); } else { status = android::BAD_VALUE; @@ -330,12 +235,58 @@ bool getGenerationNumber(const sp &producer, class C2BufferQueueBlockPool::Impl : public std::enable_shared_from_this { private: + c2_status_t dequeueBuffer( + uint32_t width, + uint32_t height, + uint32_t format, + C2AndroidMemoryUsage androidUsage, + int *slot, bool *needsRealloc, sp *fence) { + status_t status{}; + using Input = HGraphicBufferProducer::DequeueBufferInput; + using Output = HGraphicBufferProducer::DequeueBufferOutput; + Return transResult = mProducer->dequeueBuffer( + Input{ + width, + height, + format, + androidUsage.asGrallocUsage()}, + [&status, slot, needsRealloc, + fence](HStatus hStatus, + int32_t hSlot, + Output const& hOutput) { + *slot = static_cast(hSlot); + if (!h2b(hStatus, &status) || + !h2b(hOutput.fence, fence)) { + status = ::android::BAD_VALUE; + } else { + *needsRealloc = + hOutput.bufferNeedsReallocation; + } + }); + if (!transResult.isOk() || status != android::OK) { + if (transResult.isOk()) { + ++mDqFailure; + if (status == android::INVALID_OPERATION || + status == android::TIMED_OUT || + status == android::WOULD_BLOCK) { + // Dequeue buffer is blocked temporarily. Retrying is + // required. + return C2_BLOCKING; + } + } + ALOGD("cannot dequeue buffer %d", status); + return C2_BAD_VALUE; + } + return C2_OK; + } + c2_status_t fetchFromIgbp_l( uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, - std::shared_ptr *block /* nonnull */) { + std::shared_ptr *block /* nonnull */, + C2Fence *c2Fence) { // We have an IGBP now. C2AndroidMemoryUsage androidUsage = usage; status_t status{}; @@ -344,41 +295,42 @@ private: sp fence = new Fence(); ALOGV("tries to dequeue buffer"); + C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem(): nullptr; { // Call dequeueBuffer(). - using Input = HGraphicBufferProducer::DequeueBufferInput; - using Output = HGraphicBufferProducer::DequeueBufferOutput; - Return transResult = mProducer->dequeueBuffer( - Input{ - width, - height, - format, - androidUsage.asGrallocUsage()}, - [&status, &slot, &bufferNeedsReallocation, - &fence](HStatus hStatus, - int32_t hSlot, - Output const& hOutput) { - slot = static_cast(hSlot); - if (!h2b(hStatus, &status) || - !h2b(hOutput.fence, &fence)) { - status = ::android::BAD_VALUE; - } else { - bufferNeedsReallocation = - hOutput.bufferNeedsReallocation; - } - }); - if (!transResult.isOk() || status != android::OK) { - if (transResult.isOk()) { - ++mDqFailure; - if (status == android::INVALID_OPERATION || - status == android::TIMED_OUT || - status == android::WOULD_BLOCK) { - // Dequeue buffer is blocked temporarily. Retrying is - // required. - return C2_BLOCKING; + c2_status_t c2Status; + if (syncVar) { + uint32_t waitId; + syncVar->lock(); + if (!syncVar->isDequeueableLocked(&waitId)) { + syncVar->unlock(); + if (c2Fence) { + *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId); } + return C2_BLOCKING; } - ALOGD("cannot dequeue buffer %d", status); - return C2_BAD_VALUE; + if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) { + waitId = syncVar->getWaitIdLocked(); + syncVar->unlock(); + if (c2Fence) { + *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId); + } + return C2_BLOCKING; + } + syncVar->notifyDequeuedLocked(); + syncVar->unlock(); + c2Status = dequeueBuffer(width, height, format, androidUsage, + &slot, &bufferNeedsReallocation, &fence); + if (c2Status != C2_OK) { + syncVar->lock(); + syncVar->notifyQueuedLocked(); + syncVar->unlock(); + } + } else { + c2Status = dequeueBuffer(width, height, format, usage, + &slot, &bufferNeedsReallocation, &fence); + } + if (c2Status != C2_OK) { + return c2Status; } mDqFailure = 0; mLastDqTs = getTimestampNow(); @@ -389,18 +341,41 @@ private: return C2_BAD_VALUE; } ALOGV("dequeued a buffer successfully"); + bool dequeueable = false; + uint32_t waitId; if (fence) { static constexpr int kFenceWaitTimeMs = 10; status_t status = fence->wait(kFenceWaitTimeMs); if (status == -ETIME) { // fence is not signalled yet. - (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + if (syncVar) { + syncVar->lock(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + dequeueable = syncVar->notifyQueuedLocked(&waitId); + syncVar->unlock(); + if (c2Fence) { + *c2Fence = dequeueable ? C2Fence() : + _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId); + } + } else { + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + } return C2_BLOCKING; } if (status != android::NO_ERROR) { ALOGD("buffer fence wait error %d", status); - (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + if (syncVar) { + syncVar->lock(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + syncVar->notifyQueuedLocked(); + syncVar->unlock(); + if (c2Fence) { + *c2Fence = C2Fence(); + } + } else { + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + } return C2_BAD_VALUE; } else if (mRenderCallback) { nsecs_t signalTime = fence->getSignalTime(); @@ -440,7 +415,17 @@ private: return C2_BAD_VALUE; } else if (status != android::NO_ERROR) { slotBuffer.clear(); - (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + if (syncVar) { + syncVar->lock(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + syncVar->notifyQueuedLocked(); + syncVar->unlock(); + if (c2Fence) { + *c2Fence = C2Fence(); + } + } else { + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + } return C2_BAD_VALUE; } if (mGeneration == 0) { @@ -463,20 +448,36 @@ private: std::shared_ptr alloc; c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc); if (err != C2_OK) { + native_handle_close(c2Handle); + native_handle_delete(c2Handle); return err; } std::shared_ptr poolData = std::make_shared( slotBuffer->getGenerationNumber(), mProducerId, slot, - shared_from_this()); + mProducer, mSyncMem, 0); mPoolDatas[slot] = poolData; *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); return C2_OK; } // Block was not created. call requestBuffer# again next time. slotBuffer.clear(); - (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + if (syncVar) { + syncVar->lock(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + syncVar->notifyQueuedLocked(); + syncVar->unlock(); + if (c2Fence) { + *c2Fence = C2Fence(); + } + } else { + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); + } + return C2_BAD_VALUE; + } + if (c2Fence) { + *c2Fence = C2Fence(); } return C2_BAD_VALUE; } @@ -506,7 +507,8 @@ public: uint32_t height, uint32_t format, C2MemoryUsage usage, - std::shared_ptr *block /* nonnull */) { + std::shared_ptr *block /* nonnull */, + C2Fence *fence) { block->reset(); if (mInit != C2_OK) { return mInit; @@ -537,17 +539,19 @@ public: } std::shared_ptr poolData = std::make_shared( - 0, (uint64_t)0, ~0, shared_from_this()); + 0, (uint64_t)0, ~0, nullptr, nullptr, 0); *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); ALOGV("allocated a buffer successfully"); return C2_OK; } - c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block); + c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block, fence); if (status == C2_BLOCKING) { lock.unlock(); - // in order not to drain cpu from component's spinning - ::usleep(kMaxIgbpRetryDelayUs); + if (!fence) { + // in order not to drain cpu from component's spinning + ::usleep(kMaxIgbpRetryDelayUs); + } } return status; } @@ -557,10 +561,12 @@ public: mRenderCallback = renderCallback; } + /* This is for Old HAL request for compatibility */ void configureProducer(const sp &producer) { uint64_t producerId = 0; uint32_t generation = 0; - bool haveGeneration = false; + uint64_t usage = 0; + bool bqInformation = false; if (producer) { Return transResult = producer->getUniqueId(); if (!transResult.isOk()) { @@ -568,14 +574,32 @@ public: return; } producerId = static_cast(transResult); - // TODO: provide gneration number from parameter. - haveGeneration = getGenerationNumber(producer, &generation); - if (!haveGeneration) { + bqInformation = getGenerationNumberAndUsage(producer, &generation, &usage); + if (!bqInformation) { ALOGW("get generationNumber failed %llu", (unsigned long long)producerId); } } + configureProducer(producer, nullptr, producerId, generation, usage, bqInformation); + } + + void configureProducer(const sp &producer, + native_handle_t *syncHandle, + uint64_t producerId, + uint32_t generation, + uint64_t usage, + bool bqInformation) { + std::shared_ptr c2SyncMem; + if (syncHandle) { + if (!producer) { + native_handle_close(syncHandle); + native_handle_delete(syncHandle); + } else { + c2SyncMem = C2SurfaceSyncMemory::Import(syncHandle); + } + } int migrated = 0; + std::shared_ptr oldMem; // poolDatas dtor should not be called during lock is held. std::shared_ptr poolDatas[NUM_BUFFER_SLOTS]; @@ -595,22 +619,30 @@ public: if (producer) { mProducer = producer; mProducerId = producerId; - mGeneration = haveGeneration ? generation : 0; + mGeneration = bqInformation ? generation : 0; } else { mProducer = nullptr; mProducerId = 0; mGeneration = 0; ALOGW("invalid producer producer(%d), generation(%d)", - (bool)producer, haveGeneration); + (bool)producer, bqInformation); + } + oldMem = mSyncMem; // preven destruction while locked. + mSyncMem = c2SyncMem; + C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr; + if (syncVar) { + syncVar->lock(); + syncVar->setSyncStatusLocked(C2SyncVariables::STATUS_ACTIVE); + syncVar->unlock(); } - if (mProducer && haveGeneration) { // migrate buffers + if (mProducer && bqInformation) { // migrate buffers for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { std::shared_ptr data = mPoolDatas[i].lock(); if (data) { int slot = data->migrate( - mProducer, generation, - producerId, mBuffers, oldGeneration); + mProducer, generation, usage, + producerId, mBuffers[i], oldGeneration, mSyncMem); if (slot >= 0) { buffers[slot] = mBuffers[i]; poolDatas[slot] = data; @@ -624,7 +656,7 @@ public: mPoolDatas[i] = poolDatas[i]; } } - if (producer && haveGeneration) { + if (producer && bqInformation) { ALOGD("local generation change %u , " "bqId: %llu migrated buffers # %d", generation, (unsigned long long)producerId, migrated); @@ -634,17 +666,6 @@ public: private: friend struct C2BufferQueueBlockPoolData; - void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) { - bool cancelled = false; - { - std::scoped_lock lock(mMutex); - if (generation == mGeneration && igbp_id == mProducerId && mProducer) { - (void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk(); - cancelled = true; - } - } - } - c2_status_t mInit; uint64_t mProducerId; uint32_t mGeneration; @@ -662,71 +683,123 @@ private: sp mBuffers[NUM_BUFFER_SLOTS]; std::weak_ptr mPoolDatas[NUM_BUFFER_SLOTS]; + + std::shared_ptr mSyncMem; }; C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( uint32_t generation, uint64_t bqId, int32_t bqSlot, const std::shared_ptr& owner, const sp& producer) : - held(producer && bqId != 0), local(false), - generation(generation), bqId(bqId), bqSlot(bqSlot), - transfer(false), attach(false), display(false), - owner(owner), igbp(producer), - localPool() { + mLocal(false), mHeld(producer && bqId != 0), + mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot), + mCurrentGeneration(generation), mCurrentBqId(bqId), + mTransfer(false), mAttach(false), mDisplay(false), + mOwner(owner), mIgbp(producer) { } C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( uint32_t generation, uint64_t bqId, int32_t bqSlot, - const std::shared_ptr& pool) : - held(true), local(true), - generation(generation), bqId(bqId), bqSlot(bqSlot), - transfer(false), attach(false), display(false), - igbp(pool ? pool->mProducer : nullptr), - localPool(pool) { + const android::sp& producer, + std::shared_ptr syncMem, int noUse) : + mLocal(true), mHeld(true), + mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot), + mCurrentGeneration(generation), mCurrentBqId(bqId), + mTransfer(false), mAttach(false), mDisplay(false), + mIgbp(producer), mSyncMem(syncMem) { + (void)noUse; } C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() { - if (!held || bqId == 0) { + if (!mHeld || mBqId == 0 || !mIgbp) { return; } - if (local) { - if (localPool) { - localPool->cancel(generation, bqId, bqSlot); + + if (mLocal) { + if (mGeneration == mCurrentGeneration && mBqId == mCurrentBqId) { + C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr; + if (syncVar) { + syncVar->lock(); + if (syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_ACTIVE) { + mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk(); + syncVar->notifyQueuedLocked(); + } + syncVar->unlock(); + } else { + mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk(); + } + } + } else if (!mOwner.expired()) { + C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr; + if (syncVar) { + syncVar->lock(); + if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_SWITCHING) { + mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk(); + syncVar->notifyQueuedLocked(); + } + syncVar->unlock(); + } else { + mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk(); } - } else if (igbp && !owner.expired()) { - igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk(); } } + +C2BufferQueueBlockPoolData::type_t C2BufferQueueBlockPoolData::getType() const { + return TYPE_BUFFERQUEUE; +} + int C2BufferQueueBlockPoolData::migrate( const sp& producer, - uint32_t toGeneration, uint64_t toBqId, - sp *buffers, uint32_t oldGeneration) { - std::scoped_lock l(lock); - if (!held || bqId == 0) { + uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId, + sp& graphicBuffer, uint32_t oldGeneration, + std::shared_ptr syncMem) { + std::scoped_lock l(mLock); + + mCurrentBqId = toBqId; + mCurrentGeneration = toGeneration; + + if (!mHeld || mBqId == 0) { ALOGV("buffer is not owned"); return -1; } - if (!local || !localPool) { + if (!mLocal) { ALOGV("pool is not local"); return -1; } - if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) { + if (mBqSlot < 0 || mBqSlot >= NUM_BUFFER_SLOTS) { ALOGV("slot is not in effect"); return -1; } - if (toGeneration == generation && bqId == toBqId) { + if (!graphicBuffer) { + ALOGV("buffer is null"); + return -1; + } + if (toGeneration == mGeneration && mBqId == toBqId) { ALOGV("cannot migrate to same bufferqueue"); return -1; } - if (oldGeneration != generation) { + if (oldGeneration != mGeneration) { ALOGV("cannot migrate stale buffer"); + return -1; } - if (transfer) { + if (mTransfer) { // either transferred or detached. ALOGV("buffer is in transfer"); return -1; } - sp const& graphicBuffer = buffers[bqSlot]; + + if (toUsage != graphicBuffer->getUsage()) { + sp newBuffer = new GraphicBuffer( + graphicBuffer->handle, GraphicBuffer::CLONE_HANDLE, + graphicBuffer->width, graphicBuffer->height, graphicBuffer->format, + graphicBuffer->layerCount, toUsage | graphicBuffer->getUsage(), graphicBuffer->stride); + if (newBuffer->initCheck() == android::NO_ERROR) { + graphicBuffer = std::move(newBuffer); + } else { + ALOGW("%s() failed to update usage, original usage=%" PRIx64 ", toUsage=%" PRIx64, + __func__, graphicBuffer->getUsage(), toUsage); + } + } graphicBuffer->setGenerationNumber(toGeneration); HBuffer hBuffer{}; @@ -755,13 +828,124 @@ int C2BufferQueueBlockPoolData::migrate( return -1; } ALOGV("local migration from gen %u : %u slot %d : %d", - generation, toGeneration, bqSlot, slot); - generation = toGeneration; - bqId = toBqId; - bqSlot = slot; + mGeneration, toGeneration, mBqSlot, slot); + mIgbp = producer; + mGeneration = toGeneration; + mBqId = toBqId; + mBqSlot = slot; + mSyncMem = syncMem; + + C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr; + if (syncVar) { + syncVar->lock(); + syncVar->notifyDequeuedLocked(); + syncVar->unlock(); + } return slot; } +void C2BufferQueueBlockPoolData::getBufferQueueData( + uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) const { + if (generation) { + std::scoped_lock lock(mLock); + *generation = mGeneration; + if (bqId) { + *bqId = mBqId; + } + if (bqSlot) { + *bqSlot = mBqSlot; + } + } +} + +bool C2BufferQueueBlockPoolData::holdBlockFromBufferQueue( + const std::shared_ptr& owner, + const sp& igbp, + std::shared_ptr syncMem) { + std::scoped_lock lock(mLock); + if (!mLocal) { + mOwner = owner; + mIgbp = igbp; + mSyncMem = syncMem; + } + if (mHeld) { + return false; + } + mHeld = true; + return true; +} + +bool C2BufferQueueBlockPoolData::beginTransferBlockToClient() { + std::scoped_lock lock(mLock); + mTransfer = true; + return true; +} + +bool C2BufferQueueBlockPoolData::endTransferBlockToClient(bool transfer) { + std::scoped_lock lock(mLock); + mTransfer = false; + if (transfer) { + mHeld = false; + } + return true; +} + +bool C2BufferQueueBlockPoolData::beginAttachBlockToBufferQueue() { + std::scoped_lock lock(mLock); + if (mLocal || mDisplay || + mAttach || !mHeld) { + return false; + } + if (mBqId == 0) { + return false; + } + mAttach = true; + return true; +} + +bool C2BufferQueueBlockPoolData::endAttachBlockToBufferQueue( + const std::shared_ptr& owner, + const sp& igbp, + std::shared_ptr syncMem, + uint32_t generation, + uint64_t bqId, + int32_t bqSlot) { + std::scoped_lock lock(mLock); + if (mLocal || !mAttach) { + return false; + } + if (mDisplay) { + mAttach = false; + mHeld = false; + return false; + } + mAttach = false; + mHeld = true; + mOwner = owner; + mIgbp = igbp; + mSyncMem = syncMem; + mGeneration = generation; + mBqId = bqId; + mBqSlot = bqSlot; + return true; +} + +bool C2BufferQueueBlockPoolData::displayBlockToBufferQueue() { + std::scoped_lock lock(mLock); + if (mLocal || mDisplay || !mHeld) { + return false; + } + if (mBqId == 0) { + return false; + } + mDisplay = true; + if (mAttach) { + return false; + } + mHeld = false; + return true; +} + C2BufferQueueBlockPool::C2BufferQueueBlockPool( const std::shared_ptr &allocator, const local_id_t localId) : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {} @@ -775,7 +959,20 @@ c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock( C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { if (mImpl) { - return mImpl->fetchGraphicBlock(width, height, format, usage, block); + return mImpl->fetchGraphicBlock(width, height, format, usage, block, nullptr); + } + return C2_CORRUPTED; +} + +c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock( + uint32_t width, + uint32_t height, + uint32_t format, + C2MemoryUsage usage, + std::shared_ptr *block /* nonnull */, + C2Fence *fence /* nonnull */) { + if (mImpl) { + return mImpl->fetchGraphicBlock(width, height, format, usage, block, fence); } return C2_CORRUPTED; } @@ -786,6 +983,18 @@ void C2BufferQueueBlockPool::configureProducer(const sp } } +void C2BufferQueueBlockPool::configureProducer( + const sp &producer, + native_handle_t *syncMemory, + uint64_t bqId, + uint32_t generationId, + uint64_t consumerUsage) { + if (mImpl) { + mImpl->configureProducer( + producer, syncMemory, bqId, generationId, consumerUsage, true); + } +} + void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) { if (mImpl) { mImpl->setRenderCallback(renderCallback); diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e55bdc004717f310bca4a10486ccb818810ad70e --- /dev/null +++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "C2SurfaceSyncObj" +#include +#include +#include +#include +#include +#include + +#include +#include + +const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = { + C2SurfaceSyncMemory::HandleSyncMem::version, + C2SurfaceSyncMemory::HandleSyncMem::numFds, + C2SurfaceSyncMemory::HandleSyncMem::numInts, + {} +}; + +bool C2SurfaceSyncMemory::HandleSyncMem::isValid(const native_handle_t * const o) { + if (!o || memcmp(o, &cHeader, sizeof(cHeader))) { + return false; + } + + const HandleSyncMem *other = static_cast(o); + return other->mInts.mMagic == kMagic; +} + +C2SurfaceSyncMemory::C2SurfaceSyncMemory() + : mInit(false), mHandle(nullptr), mMem(nullptr) {} + +C2SurfaceSyncMemory::~C2SurfaceSyncMemory() { + if (mInit) { + if (mMem) { + munmap(static_cast(mMem), mHandle->size()); + } + if (mHandle) { + native_handle_close(mHandle); + native_handle_delete(mHandle); + } + } +} + +std::shared_ptr C2SurfaceSyncMemory::Import( + native_handle_t *handle) { + if (!HandleSyncMem::isValid(handle)) { + return nullptr; + } + + HandleSyncMem *o = static_cast(handle); + void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0); + + if (ptr == MAP_FAILED) { + native_handle_close(handle); + native_handle_delete(handle); + return nullptr; + } + + std::shared_ptr syncMem(new C2SurfaceSyncMemory); + syncMem->mInit = true; + syncMem->mHandle = o; + syncMem->mMem = static_cast(ptr); + return syncMem; +} + +std::shared_ptr C2SurfaceSyncMemory::Create(int fd, size_t size) { + if (fd < 0 || size == 0) { + return nullptr; + } + HandleSyncMem *handle = new HandleSyncMem(fd, size); + + void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + native_handle_close(handle); + native_handle_delete(handle); + return nullptr; + } + memset(ptr, 0, size); + + std::shared_ptr syncMem(new C2SurfaceSyncMemory); + syncMem->mInit = true; + syncMem->mHandle = handle; + syncMem->mMem = static_cast(ptr); + return syncMem; +} + +native_handle_t *C2SurfaceSyncMemory::handle() { + return !mInit ? nullptr : mHandle; +} + +C2SyncVariables *C2SurfaceSyncMemory::mem() { + return !mInit ? nullptr : mMem; +} + +namespace { + constexpr int kSpinNumForLock = 100; + constexpr int kSpinNumForUnlock = 200; + + enum : uint32_t { + FUTEX_UNLOCKED = 0, + FUTEX_LOCKED_UNCONTENDED = 1, // user-space locking + FUTEX_LOCKED_CONTENDED = 2, // futex locking + }; +} + +int C2SyncVariables::lock() { + uint32_t old; + for (int i = 0; i < kSpinNumForLock; i++) { + old = 0; + if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) { + return 0; + } + sched_yield(); + } + + if (old == FUTEX_LOCKED_UNCONTENDED) + old = mLock.exchange(FUTEX_LOCKED_CONTENDED); + + while (old) { + (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0); + old = mLock.exchange(FUTEX_LOCKED_CONTENDED); + } + return 0; +} + +int C2SyncVariables::unlock() { + if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0; + + for (int i = 0; i < kSpinNumForUnlock; i++) { + if (mLock.load()) { + uint32_t old = FUTEX_LOCKED_UNCONTENDED; + mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED); + if (old) { + return 0; + } + } + sched_yield(); + } + + (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0); + return 0; +} + +void C2SyncVariables::setInitialDequeueCountLocked( + int32_t maxDequeueCount, int32_t curDequeueCount) { + mMaxDequeueCount = maxDequeueCount; + mCurDequeueCount = curDequeueCount; +} + +uint32_t C2SyncVariables::getWaitIdLocked() { + return mCond.load(); +} + +bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) { + if (mMaxDequeueCount <= mCurDequeueCount) { + if (waitId) { + *waitId = getWaitIdLocked(); + } + return false; + } + return true; +} + +bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId) { + // Note. thundering herds may occur. Edge trigged signalling. + // But one waiter will guarantee to dequeue. others may wait again. + // Minimize futex syscall(trap) for the main use case(one waiter case). + if (mMaxDequeueCount == mCurDequeueCount--) { + broadcast(); + return true; + } + + if (mCurDequeueCount >= mMaxDequeueCount) { + if (waitId) { + *waitId = getWaitIdLocked(); + } + ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount); + return false; + } + return true; +} + +void C2SyncVariables::notifyDequeuedLocked() { + mCurDequeueCount++; + ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount); +} + +void C2SyncVariables::setSyncStatusLocked(SyncStatus status) { + mStatus = status; + if (mStatus == STATUS_ACTIVE) { + broadcast(); + } +} + +C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() { + return mStatus; +} + +void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) { + mMaxDequeueCount = maxDequeueCount; + if (mStatus == STATUS_ACTIVE) { + broadcast(); + } +} + +c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) { + if (timeoutNs < 0) { + timeoutNs = 0; + } + struct timespec tv; + tv.tv_sec = timeoutNs / 1000000000; + tv.tv_nsec = timeoutNs % 1000000000; + + int ret = syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0); + if (ret == 0 || ret == EAGAIN) { + return C2_OK; + } + if (ret == EINTR || ret == ETIMEDOUT) { + return C2_TIMED_OUT; + } + return C2_BAD_VALUE; +} + +int C2SyncVariables::signal() { + mCond++; + + (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0); + return 0; +} + +int C2SyncVariables::broadcast() { + mCond++; + + (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0); + return 0; +} + +int C2SyncVariables::wait() { + uint32_t old = mCond.load(); + unlock(); + + (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0); + while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) { + (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0); + } + return 0; +} diff --git a/media/codecs/amrnb/enc/src/pitch_fr.cpp b/media/codecs/amrnb/enc/src/pitch_fr.cpp index 5a846fa624f631542ba5d2dbf284fbe0e328652e..584f79b727e8e499f663dafdd8a991cab1b0dd98 100644 --- a/media/codecs/amrnb/enc/src/pitch_fr.cpp +++ b/media/codecs/amrnb/enc/src/pitch_fr.cpp @@ -570,12 +570,14 @@ static void searchFrac( Word16 corr[], /* i : normalized correlation */ Word16 flag3, /* i : subsample resolution (3: =1 / 6: =0) */ - Flag *pOverflow + Flag *pOverflow, + enum Mode mode ) { Word16 i; Word16 max; Word16 corr_int; + Word16 minPitch; /* Test the fractions around T0 and choose the one which maximizes */ /* the interpolated normalized correlation. */ @@ -593,14 +595,22 @@ static void searchFrac( } } + minPitch = (mode == MR122) ? PIT_MIN_MR122 : PIT_MIN; if (flag3 == 0) { /* Limit the fraction value in the interval [-2,-1,0,1,2,3] */ if (*frac == -3) { - *frac = 3; - (*lag)--; + if (*lag > minPitch) + { + *frac = 3; + (*lag)--; + } + else + { + *frac = -2; + } } } else @@ -609,13 +619,27 @@ static void searchFrac( if (*frac == -2) { - *frac = 1; - (*lag)--; + if (*lag > minPitch) + { + *frac = 1; + (*lag)--; + } + else + { + *frac = -1; + } } - if (*frac == 2) + else if (*frac == 2) { - *frac = -1; - (*lag)++; + if (*lag < PIT_MAX) + { + *frac = -1; + (*lag)++; + } + else + { + *frac = 1; + } } } } @@ -1533,20 +1557,20 @@ Word16 Pitch_fr( /* o : pitch period (integer) */ /* normal search in fractions around T0 */ - searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow); + searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode); } else if (lag == (tmp_lag - 2)) { /* limit search around T0 to the right side */ frac = 0; - searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow); + searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode); } else if (lag == (tmp_lag + 1)) { /* limit search around T0 to the left side */ last_frac = 0; - searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow); + searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode); } else { @@ -1556,7 +1580,7 @@ Word16 Pitch_fr( /* o : pitch period (integer) */ } else /* test the fractions around T0 */ - searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow); + searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode); } /*-----------------------------------------------------------------------* diff --git a/media/codecs/amrwb/dec/test/Android.bp b/media/codecs/amrwb/dec/test/Android.bp index cc120d3221d6493d796124d059a9c0c0a875b178..7d0c9642c3a3f60d0c173f0ad97268e8f675f236 100644 --- a/media/codecs/amrwb/dec/test/Android.bp +++ b/media/codecs/amrwb/dec/test/Android.bp @@ -28,6 +28,7 @@ package { cc_test { name: "AmrwbDecoderTest", + test_suites: ["device-tests"], gtest: true, srcs: [ diff --git a/media/codecs/amrwb/enc/test/Android.bp b/media/codecs/amrwb/enc/test/Android.bp index e4b7771d8a2d4c68d4230ebea16558c3d8c327c8..942f6c92d5059c2347687e00f0a22fdf63e9293f 100644 --- a/media/codecs/amrwb/enc/test/Android.bp +++ b/media/codecs/amrwb/enc/test/Android.bp @@ -27,6 +27,7 @@ package { cc_test { name: "AmrwbEncoderTest", + test_suites: ["device-tests"], gtest: true, srcs: [ diff --git a/media/codecs/g711/decoder/Android.bp b/media/codecs/g711/decoder/Android.bp index 07f7ed47661640151d2fd27aa1adcc214d4f2fad..0cd97405bf02647c4c87b5112eff86fa233247e8 100644 --- a/media/codecs/g711/decoder/Android.bp +++ b/media/codecs/g711/decoder/Android.bp @@ -44,7 +44,13 @@ cc_library_static { ], cfi: true, }, - apex_available: ["com.android.media.swcodec"], + + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + "test_com.android.media.swcodec", + ], + min_sdk_version: "29", target: { diff --git a/media/codecs/m4v_h263/dec/test/Android.bp b/media/codecs/m4v_h263/dec/test/Android.bp index 6eed66f30a864d2cbc613e9973ed771a24a4a810..d8de5693f2b77dd017ac4f36edee4b713a4b05a4 100644 --- a/media/codecs/m4v_h263/dec/test/Android.bp +++ b/media/codecs/m4v_h263/dec/test/Android.bp @@ -47,6 +47,10 @@ cc_test { }, }, + // this unit test also runs within the mainline tests (MTS), + // so it must be compatible back to Android Q/10 (sdk 29) + min_sdk_version: "29", + srcs: [ "Mpeg4H263DecoderTest.cpp", ], diff --git a/media/codecs/m4v_h263/enc/src/motion_est.cpp b/media/codecs/m4v_h263/enc/src/motion_est.cpp index 997b78d2291a7f3746191243773f1e21048cb2c9..9deb02367b0459f1a7bc8ec790e6a2b165f6fa83 100644 --- a/media/codecs/m4v_h263/enc/src/motion_est.cpp +++ b/media/codecs/m4v_h263/enc/src/motion_est.cpp @@ -1576,7 +1576,7 @@ void RasterIntraUpdate(UChar *intraArray, UChar *Mode, Int totalMB, Int numRefre /* find the last refresh MB */ indx = 0; - while (intraArray[indx] == 1 && indx < totalMB) + while (indx < totalMB && intraArray[indx] == 1) indx++; /* add more */ diff --git a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp index cc4f2225d49e4ec69ecdefa4aa5b6efd1bd273d5..ca58837256ca5210f7b331c3d9295c18abfb0c68 100644 --- a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp +++ b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp @@ -491,6 +491,9 @@ OSCL_EXPORT_REF Bool PVInitVideoEncoder(VideoEncControls *encoderControl, Vid } for (i = 0; i < encParams->nLayers; i++) { + if (encOption->encHeight[i] == 0 || encOption->encWidth[i] == 0 || + encOption->encHeight[i] % 16 != 0 || encOption->encWidth[i] % 16 != 0) + goto CLEAN_UP; encParams->LayerHeight[i] = encOption->encHeight[i]; encParams->LayerWidth[i] = encOption->encWidth[i]; } diff --git a/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp index a5c7f5ee5c0de265eb5f75ea796747f1021d378c..5cf1ed3aa3424518d3ba71a228185e0e389eded7 100644 --- a/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp +++ b/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp @@ -219,6 +219,11 @@ ERROR_CODE pvmp3_framedecoder(tPVMP3DecoderExternal *pExt, if (info->error_protection) { + if (!bitsAvailable(&pVars->inputStream, 16)) + { + return SIDE_INFO_ERROR; + } + /* * Get crc content */ @@ -593,18 +598,10 @@ void fillMainDataBuf(void *pMem, int32 temp) } else { - int32 tmp1 = *(ptr++); - for (int32 nBytes = temp >> 1; nBytes != 0; nBytes--) /* read main data. */ + for (int32 nBytes = temp; nBytes != 0; nBytes--) /* read main data. */ { - int32 tmp2 = *(ptr++); - fillDataBuf(&pVars->mainDataStream, tmp1); - fillDataBuf(&pVars->mainDataStream, tmp2); - tmp1 = *(ptr++); - } - - if (temp&1) - { - fillDataBuf(&pVars->mainDataStream, tmp1); + int32 tmp = *(ptr++); + fillDataBuf(&pVars->mainDataStream, tmp); } /* adjust circular buffer counter */ @@ -613,14 +610,9 @@ void fillMainDataBuf(void *pMem, int32 temp) } else { - for (int32 nBytes = temp >> 1; nBytes != 0; nBytes--) /* read main data. */ + for (int32 nBytes = temp; nBytes != 0; nBytes--) /* read main data. */ { fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset++ , BUFSIZE))); - fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset++ , BUFSIZE))); - } - if (temp&1) - { - fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset , BUFSIZE))); } } diff --git a/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp b/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp index d644207924e0eca99c38f7160950091486f1934b..1a3fca5eddb4e813b277630fd7765668c630e4c6 100644 --- a/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp +++ b/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp @@ -73,6 +73,7 @@ Input #include "pvmp3_get_side_info.h" #include "pvmp3_crc.h" +#include "pvmp3_getbits.h" /*---------------------------------------------------------------------------- @@ -125,12 +126,22 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, { if (stereo == 1) { + if (!bitsAvailable(inputStream, 14)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 14, crc, info->error_protection); si->main_data_begin = (tmp << 18) >> 23; /* 9 */ si->private_bits = (tmp << 27) >> 27; /* 5 */ } else { + if (!bitsAvailable(inputStream, 12)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 12, crc, info->error_protection); si->main_data_begin = (tmp << 20) >> 23; /* 9 */ si->private_bits = (tmp << 29) >> 29; /* 3 */ @@ -139,6 +150,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, for (ch = 0; ch < stereo; ch++) { + if (!bitsAvailable(inputStream, 4)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 4, crc, info->error_protection); si->ch[ch].scfsi[0] = (tmp << 28) >> 31; /* 1 */ si->ch[ch].scfsi[1] = (tmp << 29) >> 31; /* 1 */ @@ -150,6 +166,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, { for (ch = 0; ch < stereo; ch++) { + if (!bitsAvailable(inputStream, 34)) + { + return SIDE_INFO_ERROR; + } + si->ch[ch].gran[gr].part2_3_length = getbits_crc(inputStream, 12, crc, info->error_protection); tmp = getbits_crc(inputStream, 22, crc, info->error_protection); @@ -160,6 +181,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, if (si->ch[ch].gran[gr].window_switching_flag) { + if (!bitsAvailable(inputStream, 22)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 22, crc, info->error_protection); si->ch[ch].gran[gr].block_type = (tmp << 10) >> 30; /* 2 */; @@ -192,6 +218,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, } else { + if (!bitsAvailable(inputStream, 22)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 22, crc, info->error_protection); si->ch[ch].gran[gr].table_select[0] = (tmp << 10) >> 27; /* 5 */; @@ -204,6 +235,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, si->ch[ch].gran[gr].block_type = 0; } + if (!bitsAvailable(inputStream, 3)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 3, crc, info->error_protection); si->ch[ch].gran[gr].preflag = (tmp << 29) >> 31; /* 1 */ si->ch[ch].gran[gr].scalefac_scale = (tmp << 30) >> 31; /* 1 */ @@ -213,11 +249,21 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, } else /* Layer 3 LSF */ { + if (!bitsAvailable(inputStream, 8 + stereo)) + { + return SIDE_INFO_ERROR; + } + si->main_data_begin = getbits_crc(inputStream, 8, crc, info->error_protection); si->private_bits = getbits_crc(inputStream, stereo, crc, info->error_protection); for (ch = 0; ch < stereo; ch++) { + if (!bitsAvailable(inputStream, 39)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 21, crc, info->error_protection); si->ch[ch].gran[0].part2_3_length = (tmp << 11) >> 20; /* 12 */ si->ch[ch].gran[0].big_values = (tmp << 23) >> 23; /* 9 */ @@ -230,6 +276,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, if (si->ch[ch].gran[0].window_switching_flag) { + if (!bitsAvailable(inputStream, 22)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 22, crc, info->error_protection); si->ch[ch].gran[0].block_type = (tmp << 10) >> 30; /* 2 */; @@ -262,6 +313,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, } else { + if (!bitsAvailable(inputStream, 22)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 22, crc, info->error_protection); si->ch[ch].gran[0].table_select[0] = (tmp << 10) >> 27; /* 5 */; @@ -274,6 +330,11 @@ ERROR_CODE pvmp3_get_side_info(tmp3Bits *inputStream, si->ch[ch].gran[0].block_type = 0; } + if (!bitsAvailable(inputStream, 2)) + { + return SIDE_INFO_ERROR; + } + tmp = getbits_crc(inputStream, 2, crc, info->error_protection); si->ch[ch].gran[0].scalefac_scale = tmp >> 1; /* 1 */ si->ch[ch].gran[0].count1table_select = tmp & 1; /* 1 */ diff --git a/media/codecs/mp3dec/src/pvmp3_getbits.cpp b/media/codecs/mp3dec/src/pvmp3_getbits.cpp index 8ff79538b5db56a1300d6883ce500dfca4bbfefe..4d252efdbe77ec458ac11555ea3b4e53cb9ed421 100644 --- a/media/codecs/mp3dec/src/pvmp3_getbits.cpp +++ b/media/codecs/mp3dec/src/pvmp3_getbits.cpp @@ -113,10 +113,11 @@ uint32 getNbits(tmp3Bits *ptBitStream, uint32 offset; uint32 bitIndex; - uint8 Elem; /* Needs to be same type as pInput->pBuffer */ - uint8 Elem1; - uint8 Elem2; - uint8 Elem3; + uint32 bytesToFetch; + uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */ + uint8 Elem1 = 0; + uint8 Elem2 = 0; + uint8 Elem3 = 0; uint32 returnValue = 0; if (!neededBits) @@ -126,10 +127,25 @@ uint32 getNbits(tmp3Bits *ptBitStream, offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT; - Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE)); - Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); - Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE)); - Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE)); + /* Remove extra high bits by shifting up */ + bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); + + bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ; + + switch (bytesToFetch) + { + case 4: + Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE)); + [[fallthrough]]; + case 3: + Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE)); + [[fallthrough]]; + case 2: + Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); + [[fallthrough]]; + case 1: + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + } returnValue = (((uint32)(Elem)) << 24) | @@ -137,9 +153,6 @@ uint32 getNbits(tmp3Bits *ptBitStream, (((uint32)(Elem2)) << 8) | ((uint32)(Elem3)); - /* Remove extra high bits by shifting up */ - bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); - /* This line is faster than to mask off the high bits. */ returnValue <<= bitIndex; @@ -161,22 +174,32 @@ uint16 getUpTo9bits(tmp3Bits *ptBitStream, uint32 offset; uint32 bitIndex; - uint8 Elem; /* Needs to be same type as pInput->pBuffer */ - uint8 Elem1; + uint32 bytesToFetch; + uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */ + uint8 Elem1 = 0; uint16 returnValue; offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT; - Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE)); - Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); + /* Remove extra high bits by shifting up */ + bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); + + bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ; + + if (bytesToFetch > 1) + { + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); + } + else if (bytesToFetch > 0) + { + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + } returnValue = (((uint16)(Elem)) << 8) | ((uint16)(Elem1)); - /* Remove extra high bits by shifting up */ - bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); - ptBitStream->usedBits += neededBits; /* This line is faster than to mask off the high bits. */ returnValue = (returnValue << (bitIndex)); @@ -197,25 +220,40 @@ uint32 getUpTo17bits(tmp3Bits *ptBitStream, uint32 offset; uint32 bitIndex; - uint8 Elem; /* Needs to be same type as pInput->pBuffer */ - uint8 Elem1; - uint8 Elem2; + uint32 bytesToFetch; + uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */ + uint8 Elem1 = 0; + uint8 Elem2 = 0; uint32 returnValue; offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT; - Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE)); - Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); - Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE)); + /* Remove extra high bits by shifting up */ + bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); + + bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ; + + if (bytesToFetch > 2) + { + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); + Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE)); + } + else if (bytesToFetch > 1) + { + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE)); + } + else if (bytesToFetch > 0) + { + Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE)); + } returnValue = (((uint32)(Elem)) << 16) | (((uint32)(Elem1)) << 8) | ((uint32)(Elem2)); - /* Remove extra high bits by shifting up */ - bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH); - ptBitStream->usedBits += neededBits; /* This line is faster than to mask off the high bits. */ returnValue = 0xFFFFFF & (returnValue << (bitIndex)); diff --git a/media/codecs/mp3dec/src/pvmp3_getbits.h b/media/codecs/mp3dec/src/pvmp3_getbits.h index b058b007c81944576f3bc68c569d347821d5640f..b04fe6d7eac5bb55463013644ca11af4b07a0e85 100644 --- a/media/codecs/mp3dec/src/pvmp3_getbits.h +++ b/media/codecs/mp3dec/src/pvmp3_getbits.h @@ -104,6 +104,11 @@ extern "C" ; Function Prototype declaration ----------------------------------------------------------------------------*/ +static inline bool bitsAvailable(tmp3Bits *inputStream, uint32 neededBits) +{ + return (inputStream->inputBufferCurrentLength << 3) >= (neededBits + inputStream->usedBits); +} + /*---------------------------------------------------------------------------- ; END ----------------------------------------------------------------------------*/ diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING index abefb0f83b5b4a25d9a789317deb7187fa9142cb..4984b8fccdeb617b086fcd626c2c203694ebb8d4 100644 --- a/media/extractors/TEST_MAPPING +++ b/media/extractors/TEST_MAPPING @@ -1,5 +1,6 @@ { "presubmit": [ + // TODO(b/153661591) enable test once the bug is fixed // This tests the extractor path // { @@ -13,5 +14,14 @@ // } // ] // } + ], + + // tests which require dynamic content + // invoke with: atest -- --enable-module-dynamic-download=true + // TODO(b/148094059): unit tests not allowed to download content + "dynamic-presubmit": [ + { "name": "ExtractorUnitTest" } ] + + } diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index 8f60f6b7c1afa07ae86beebe1aa99a3dd80c5bff..2fc458430157e2741c2fddb95551f1662e8fe5a4 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "AACExtractor" #include +#include + #include "AACExtractor.h" #include #include @@ -277,7 +279,22 @@ media_status_t AACSource::read( ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { if (mFrameDurationUs > 0) { - int64_t seekFrame = seekTimeUs / mFrameDurationUs; + int64_t seekFrame = 0; + switch(mode & 0x7) { + case ReadOptions::SEEK_NEXT_SYNC: + // "at or after" + seekFrame = (seekTimeUs + mFrameDurationUs - 1) / mFrameDurationUs; + break; + case ReadOptions::SEEK_CLOSEST_SYNC: + case ReadOptions::SEEK_CLOSEST: + seekFrame = (seekTimeUs + mFrameDurationUs/2) / mFrameDurationUs; + break; + case ReadOptions::SEEK_PREVIOUS_SYNC: + default: + // 'at or before' + seekFrame = seekTimeUs / mFrameDurationUs; + break; + } if (seekFrame < 0 || seekFrame >= (int64_t)mOffsetVector.size()) { android_errorWriteLog(0x534e4554, "70239507"); return AMEDIA_ERROR_MALFORMED; diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index 26431a4b36eb040b8951f4cdbce44ea9094e8035..e26ff0aff3ccc1f6964085eea9b348671531074c 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "AMRExtractor" #include +#include + #include "AMRExtractor.h" #include @@ -283,8 +285,22 @@ media_status_t AMRSource::read( ReadOptions::SeekMode mode; if (mOffsetTableLength > 0 && options && options->getSeekTo(&seekTimeUs, &mode)) { size_t size; - int64_t seekFrame = seekTimeUs / 20000LL; // 20ms per frame. - mCurrentTimeUs = seekFrame * 20000LL; + const int64_t frameDurationUs = 20000LL; // 20ms per frame. + int64_t seekFrame = 0; + switch(mode & 0x7) { + case ReadOptions::SEEK_NEXT_SYNC: + seekFrame = (seekTimeUs + frameDurationUs - 1) / frameDurationUs; + break; + case ReadOptions::SEEK_CLOSEST_SYNC: + case ReadOptions::SEEK_CLOSEST: + seekFrame = (seekTimeUs + frameDurationUs/2) / frameDurationUs; + break; + case ReadOptions::SEEK_PREVIOUS_SYNC: + default: + seekFrame = seekTimeUs / frameDurationUs; + break; + } + mCurrentTimeUs = seekFrame * frameDurationUs; size_t index = seekFrame < 0 ? 0 : seekFrame / 50; if (index >= mOffsetTableLength) { diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 0617e88b8439cf8b6e34408dd30f73b91c5de62f..ec7cb24b936b59d174571605b25a7a573df7d24b 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -561,6 +561,8 @@ status_t FLACParser::init() AMediaFormat_setString(mFileMetadata, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_FLAC); } + mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize(); + AMediaFormat_setInt32(mTrackMetadata, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, mMaxBufferSize); return OK; } @@ -568,8 +570,6 @@ void FLACParser::allocateBuffers(MediaBufferGroupHelper *group) { CHECK(mGroup == NULL); mGroup = group; - mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize(); - AMediaFormat_setInt32(mTrackMetadata, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, mMaxBufferSize); mGroup->add_buffer(mMaxBufferSize); } diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp index f9e82fbf8f9be47005af94d5b306b5d69374695b..0e54b58d4a9ea86709a48bef32a2565d9583d24a 100644 --- a/media/extractors/fuzzers/Android.bp +++ b/media/extractors/fuzzers/Android.bp @@ -39,6 +39,7 @@ cc_defaults { static_libs: [ "liblog", + "libstagefright_foundation_colorutils_ndk", "libstagefright_foundation", "libmediandk_format", "libmedia_ndkformatpriv", @@ -319,3 +320,27 @@ cc_fuzz { dictionary: "flac_extractor_fuzzer.dict", } + +cc_fuzz { + name: "midi_extractor_fuzzer", + defaults: ["extractor-fuzzer-defaults"], + + srcs: [ + "midi_extractor_fuzzer.cpp", + ], + + include_dirs: [ + "frameworks/av/media/extractors/midi", + ], + + static_libs: [ + "libsonivox", + "libmedia_midiiowrapper", + "libmidiextractor", + "libwatchdog", + ], + + dictionary: "midi_extractor_fuzzer.dict", + + host_supported: true, +} diff --git a/media/extractors/fuzzers/README.md b/media/extractors/fuzzers/README.md index 4223b5e88175a3835ebbbe02167bac165032c3dd..fb1d52fb70f7e818c78b58bc0cad6042293e9730 100644 --- a/media/extractors/fuzzers/README.md +++ b/media/extractors/fuzzers/README.md @@ -11,6 +11,7 @@ + [libmp3extractor](#mp3ExtractorFuzzer) + [libaacextractor](#aacExtractorFuzzer) + [libflacextractor](#flacExtractor) ++ [libmidiextractor](#midiExtractorFuzzer) # Fuzzer for libextractorfuzzerbase All the extractors have a common API - creating a data source, extraction @@ -321,6 +322,41 @@ To run on device $ adb shell /data/fuzz/arm64/flac_extractor_fuzzer/flac_extractor_fuzzer CORPUS_DIR ``` +# Fuzzer for libmidiextractor + +## Plugin Design Considerations +The fuzzer plugin for MIDI extractor uses the `ExtractorFuzzerBase` class and +implements only the `createExtractor` to create the MIDI extractor class. + +##### Maximize code coverage +Dict file (dictionary file) is created for MIDI to ensure that the required MIDI +headers are present in every input file that goes to the fuzzer. +This ensures that larger code gets covered as a range of MIDI headers will be +present in the input data. + + +## Build + +This describes steps to build midi_extractor_fuzzer binary. + +### Android + +#### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) midi_extractor_fuzzer +``` + +#### Steps to run +Create a directory CORPUS_DIR and copy some MIDI files to that folder +Push this directory to device. + +To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/midi_extractor_fuzzer/midi_extractor_fuzzer CORPUS_DIR +``` + ## References: * http://llvm.org/docs/LibFuzzer.html * https://github.com/google/oss-fuzz diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.cpp b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e02a12b3dc66c75fe735e352bd00154ac926ef31 --- /dev/null +++ b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp @@ -0,0 +1,54 @@ +/****************************************************************************** + * + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore + */ + +#include "ExtractorFuzzerBase.h" + +#include "MidiExtractor.h" + +using namespace android; + +class MIDIExtractor : public ExtractorFuzzerBase { + public: + MIDIExtractor() = default; + ~MIDIExtractor() = default; + + bool createExtractor(); +}; + +bool MIDIExtractor::createExtractor() { + mExtractor = new MidiExtractor(mDataSource->wrap()); + if (!mExtractor) { + return false; + } + mExtractor->name(); + return true; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if ((!data) || (size == 0)) { + return 0; + } + MIDIExtractor* extractor = new MIDIExtractor(); + if (extractor) { + extractor->processData(data, size); + delete extractor; + } + return 0; +} diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.dict b/media/extractors/fuzzers/midi_extractor_fuzzer.dict new file mode 100644 index 0000000000000000000000000000000000000000..5b6bb8bf2d1f701c73c1dcceb5762c0ca80dc63e --- /dev/null +++ b/media/extractors/fuzzers/midi_extractor_fuzzer.dict @@ -0,0 +1,3 @@ +# MIDI Chunks +kw1="MThd" +kw2="MTrk" diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp index f82fc419531daaf423ce3ff72886e9d06dd689f8..08a6fa04e224ae8df6c9a620b46f517cfa10d497 100644 --- a/media/extractors/midi/Android.bp +++ b/media/extractors/midi/Android.bp @@ -24,7 +24,7 @@ cc_library { srcs: ["MidiExtractor.cpp"], header_libs: [ - "libmedia_headers", + "libmedia_datasource_headers", ], static_libs: [ @@ -37,4 +37,12 @@ cc_library { shared_libs: [ "libbase", ], + + host_supported: true, + + target: { + darwin: { + enabled: false, + }, + }, } diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp index 840c9fc0ff07f5ca2c498a1242a0fd3c956e3a8a..54c5b271eb4b0bb97c73998cde2fd243da3b54b8 100644 --- a/media/extractors/mkv/Android.bp +++ b/media/extractors/mkv/Android.bp @@ -32,6 +32,7 @@ cc_library { ], static_libs: [ + "libstagefright_foundation_colorutils_ndk", // for mainline-safe ColorUtils "libstagefright_foundation", "libstagefright_metadatautils", "libwebm", diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 4fd3a569b0a94235c6669f78a100ef4e855de7d0..443e26c85c7f9307f22fd3ec375839d3425484b7 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1700,17 +1700,17 @@ status_t MatroskaExtractor::synthesizeMPEG2(TrackInfo *trackInfo, size_t index) return ERROR_MALFORMED; } - size_t header_start = 0; - size_t header_lenth = 0; + long header_start = 0; + long header_length = 0; for (header_start = 0; header_start < frame.len - 4; header_start++) { if (ntohl(0x000001b3) == *(uint32_t*)((uint8_t*)tmpData.get() + header_start)) { break; } } bool isComplete_csd = false; - for (header_lenth = 0; header_lenth < frame.len - 4 - header_start; header_lenth++) { + for (header_length = 0; header_length < frame.len - 4 - header_start; header_length++) { if (ntohl(0x000001b8) == *(uint32_t*)((uint8_t*)tmpData.get() - + header_start + header_lenth)) { + + header_start + header_length)) { isComplete_csd = true; break; } @@ -1720,7 +1720,7 @@ status_t MatroskaExtractor::synthesizeMPEG2(TrackInfo *trackInfo, size_t index) return ERROR_MALFORMED; } addESDSFromCodecPrivate(trackInfo->mMeta, false, - (uint8_t*)(tmpData.get()) + header_start, header_lenth); + (uint8_t*)(tmpData.get()) + header_start, header_length); return OK; diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 51658225e12cfa48d55dc423de38a100e1c8edf6..248a39c7ae1d31ccb9cd1bc1d6379dcb93ba7a0f 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -425,8 +425,7 @@ media_status_t MP3Extractor::getTrackMetaData( if (mInitCheck != OK || index != 0) { return AMEDIA_ERROR_UNKNOWN; } - AMediaFormat_copy(meta, mMeta); - return AMEDIA_OK; + return AMediaFormat_copy(meta, mMeta); } //////////////////////////////////////////////////////////////////////////////// @@ -505,7 +504,14 @@ media_status_t MP3Source::read( } mCurrentTimeUs = seekTimeUs; - mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000; + int64_t seekTimeUsTimesBitrate; + if (__builtin_mul_overflow(seekTimeUs, bitrate, &seekTimeUsTimesBitrate)) { + return AMEDIA_ERROR_UNSUPPORTED; + } + if (__builtin_add_overflow( + mFirstFramePos, seekTimeUsTimesBitrate / 8000000, &mCurrentPos)) { + return AMEDIA_ERROR_UNSUPPORTED; + } seekCBR = true; } else { mCurrentTimeUs = actualSeekTimeUs; diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index 2599c2cb56860fbc20c5dd9b18a2f4e3bdb9914d..444664c2322dfe2025f3a2144e377681750f475f 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -76,16 +76,19 @@ struct ImageItem { size_t size; sp hvcc; sp icc; + sp av1c; Vector thumbnails; Vector dimgRefs; - Vector cdscRefs; + Vector exifRefs; + Vector xmpRefs; size_t nextTileIndex; }; -struct ExifItem { +struct ExternalMetaItem { off64_t offset; size_t size; + bool isExif; }; ///////////////////////////////////////////////////////////////////// @@ -481,7 +484,7 @@ struct ItemReference : public Box, public RefBase { void apply( KeyedVector &itemIdToItemMap, - KeyedVector &itemIdToExifMap) const; + KeyedVector &itemIdToMetaMap) const; private: uint32_t mItemId; @@ -493,7 +496,7 @@ private: void ItemReference::apply( KeyedVector &itemIdToItemMap, - KeyedVector &itemIdToExifMap) const { + KeyedVector &itemIdToMetaMap) const { ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId); switch(type()) { @@ -555,15 +558,15 @@ void ItemReference::apply( break; } case FOURCC("cdsc"): { - ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId); + ssize_t metaIndex = itemIdToMetaMap.indexOfKey(mItemId); - // ignore non-exif block items - if (itemIndex < 0) { + // ignore non-meta items + if (metaIndex < 0) { return; } for (size_t i = 0; i < mRefs.size(); i++) { - itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]); + ssize_t itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]); // ignore non-image items if (itemIndex < 0) { @@ -571,7 +574,11 @@ void ItemReference::apply( } ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId); ImageItem &image = itemIdToItemMap.editValueAt(itemIndex); - image.cdscRefs.push_back(mItemId); + if (itemIdToMetaMap[metaIndex].isExif) { + image.exifRefs.push_back(mItemId); + } else { + image.xmpRefs.push_back(mItemId); + } } break; } @@ -764,6 +771,39 @@ status_t HvccBox::parse(off64_t offset, size_t size) { return OK; } +struct Av1cBox : public Box, public ItemProperty { + Av1cBox(DataSourceHelper *source) : + Box(source, FOURCC("av1C")) {} + + status_t parse(off64_t offset, size_t size) override; + + void attachTo(ImageItem &image) const override { + image.av1c = mAv1c; + } + +private: + sp mAv1c; +}; + +status_t Av1cBox::parse(off64_t offset, size_t size) { + ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); + + mAv1c = new ABuffer(size); + + if (mAv1c->data() == NULL) { + ALOGE("b/28471206"); + return NO_MEMORY; + } + + if (source()->readAt(offset, mAv1c->data(), size) < (ssize_t)size) { + return ERROR_IO; + } + + ALOGV("property av1C"); + + return OK; +} + struct IrotBox : public Box, public ItemProperty { IrotBox(DataSourceHelper *source) : Box(source, FOURCC("irot")), mAngle(0) {} @@ -957,6 +997,11 @@ status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) { itemProperty = new ColrBox(source()); break; } + case FOURCC("av1C"): + { + itemProperty = new Av1cBox(source()); + break; + } default: { // push dummy to maintain correct item property index @@ -1026,7 +1071,21 @@ status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) { struct ItemInfo { uint32_t itemId; uint32_t itemType; + String8 contentType; bool hidden; + + bool isXmp() const { + return itemType == FOURCC("mime") && contentType == String8("application/rdf+xml"); + } + bool isExif() const { + return itemType == FOURCC("Exif"); + } + bool isGrid() const { + return itemType == FOURCC("grid"); + } + bool isSample() const { + return itemType == FOURCC("av01") || itemType == FOURCC("hvc1"); + } }; struct InfeBox : public FullBox { @@ -1116,6 +1175,7 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { if (!parseNullTerminatedString(&offset, &size, &content_type)) { return ERROR_MALFORMED; } + itemInfo->contentType = content_type; // content_encoding is optional; can be omitted if would be empty if (size > 0) { @@ -1136,18 +1196,18 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { struct IinfBox : public FullBox { IinfBox(DataSourceHelper *source, Vector *itemInfos) : - FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {} + FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos), mNeedIref(false) {} status_t parse(off64_t offset, size_t size); - bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; } + bool needIrefBox() { return mNeedIref; } protected: status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; private: Vector *mItemInfos; - std::unordered_set mFourCCSeen; + bool mNeedIref; }; status_t IinfBox::parse(off64_t offset, size_t size) { @@ -1194,7 +1254,7 @@ status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) { status_t err = infeBox.parse(offset, size, &itemInfo); if (err == OK) { mItemInfos->push_back(itemInfo); - mFourCCSeen.insert(itemInfo.itemType); + mNeedIref |= (itemInfo.isExif() || itemInfo.isXmp() || itemInfo.isGrid()); } // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported // version. Ignore this error as it's not fatal. @@ -1203,8 +1263,9 @@ status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) { ////////////////////////////////////////////////////////////////// -ItemTable::ItemTable(DataSourceHelper *source) +ItemTable::ItemTable(DataSourceHelper *source, bool isHeif) : mDataSource(source), + mIsHeif(isHeif), mPrimaryItemId(0), mIdatOffset(0), mIdatSize(0), @@ -1283,7 +1344,7 @@ status_t ItemTable::parseIinfBox(off64_t offset, size_t size) { return err; } - if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) { + if (iinfBox.needIrefBox()) { mRequiredBoxes.insert('iref'); } @@ -1359,11 +1420,9 @@ status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { // Only handle 3 types of items, all others are ignored: // 'grid': derived image from tiles - // 'hvc1': coded image (or tile) - // 'Exif': EXIF metadata - if (info.itemType != FOURCC("grid") && - info.itemType != FOURCC("hvc1") && - info.itemType != FOURCC("Exif")) { + // 'hvc1' or 'av01': coded image (or tile) + // 'Exif' or XMP: metadata + if (!info.isGrid() && !info.isSample() && !info.isExif() && !info.isXmp()) { continue; } @@ -1386,15 +1445,18 @@ status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { return ERROR_MALFORMED; } - if (info.itemType == FOURCC("Exif")) { - // Only add if the Exif data is non-empty. The first 4 bytes contain + if (info.isExif() || info.isXmp()) { + // Only add if the meta is non-empty. For Exif, the first 4 bytes contain // the offset to TIFF header, which the Exif parser doesn't use. - if (size > 4) { - ExifItem exifItem = { + ALOGV("adding meta to mItemIdToMetaMap: isExif %d, offset %lld, size %lld", + 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, }; - mItemIdToExifMap.add(info.itemId, exifItem); + mItemIdToMetaMap.add(info.itemId, metaItem); } continue; } @@ -1429,7 +1491,7 @@ status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { } for (size_t i = 0; i < mItemReferences.size(); i++) { - mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap); + mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToMetaMap); } bool foundPrimary = false; @@ -1509,7 +1571,9 @@ AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) { } AMediaFormat *meta = AMediaFormat_new(); - AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); + AMediaFormat_setString( + meta, AMEDIAFORMAT_KEY_MIME, + mIsHeif ? MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC : MEDIA_MIMETYPE_IMAGE_AVIF); if (image->itemId == mPrimaryItemId) { AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1); @@ -1539,15 +1603,24 @@ AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) { ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]); if (thumbItemIndex >= 0) { const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex]; - - if (thumbnail.hvcc != NULL) { + if (thumbnail.hvcc != NULL || thumbnail.av1c != NULL) { AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width); AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height); - AMediaFormat_setBuffer(meta, - AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC, - thumbnail.hvcc->data(), thumbnail.hvcc->size()); + if (thumbnail.hvcc != NULL) { + AMediaFormat_setBuffer(meta, + AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC, + thumbnail.hvcc->data(), thumbnail.hvcc->size()); + } else { + // We use a hard-coded string here instead of + // AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C. The key is available only from SDK 31. + // The mp4 extractor is part of mainline and builds against SDK 29 as of + // writing. This hard-coded string can be replaced with the named constant once + // the mp4 extractor is built against SDK >= 31. + AMediaFormat_setBuffer(meta, + "thumbnail-csd-av1c", thumbnail.av1c->data(), thumbnail.av1c->size()); + } ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd", imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex); } else { @@ -1574,12 +1647,21 @@ AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) { AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2); } - if (image->hvcc == NULL) { - ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex); - return NULL; + if (mIsHeif) { + if (image->hvcc == NULL) { + ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex); + return NULL; + } + AMediaFormat_setBuffer(meta, + AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size()); + } else { + if (image->av1c == NULL) { + ALOGE("%s: av1c is missing for image[%u]!", __FUNCTION__, imageIndex); + return NULL; + } + AMediaFormat_setBuffer(meta, + AMEDIAFORMAT_KEY_CSD_0, image->av1c->data(), image->av1c->size()); } - AMediaFormat_setBuffer(meta, - AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size()); if (image->icc != NULL) { AMediaFormat_setBuffer(meta, @@ -1686,11 +1768,11 @@ status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) { } const ImageItem &image = mItemIdToItemMap[itemIndex]; - if (image.cdscRefs.size() == 0) { + if (image.exifRefs.size() == 0) { return NAME_NOT_FOUND; } - ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]); + ssize_t exifIndex = mItemIdToMetaMap.indexOfKey(image.exifRefs[0]); if (exifIndex < 0) { return NAME_NOT_FOUND; } @@ -1698,7 +1780,7 @@ status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) { // skip the first 4-byte of the offset to TIFF header uint32_t tiffOffset; if (!mDataSource->readAt( - mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) { + mItemIdToMetaMap[exifIndex].offset, &tiffOffset, 4)) { return ERROR_IO; } @@ -1711,16 +1793,43 @@ status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) { // exif data. The size of the item should be > 4 for a non-empty exif (this // was already checked when the item was added). Also check that the tiff // header offset is valid. - if (mItemIdToExifMap[exifIndex].size <= 4 || - tiffOffset > mItemIdToExifMap[exifIndex].size - 4) { + if (mItemIdToMetaMap[exifIndex].size <= 4 || + tiffOffset > mItemIdToMetaMap[exifIndex].size - 4) { return ERROR_MALFORMED; } // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item // (first 4-byte is the tiff header offset) uint32_t exifOffset = 4 + tiffOffset - 6; - *offset = mItemIdToExifMap[exifIndex].offset + exifOffset; - *size = mItemIdToExifMap[exifIndex].size - exifOffset; + *offset = mItemIdToMetaMap[exifIndex].offset + exifOffset; + *size = mItemIdToMetaMap[exifIndex].size - exifOffset; + return OK; +} + +status_t ItemTable::getXmpOffsetAndSize(off64_t *offset, size_t *size) { + if (!mImageItemsValid) { + return INVALID_OPERATION; + } + + ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId); + + // this should not happen, something's seriously wrong. + if (itemIndex < 0) { + return INVALID_OPERATION; + } + + const ImageItem &image = mItemIdToItemMap[itemIndex]; + if (image.xmpRefs.size() == 0) { + return NAME_NOT_FOUND; + } + + ssize_t xmpIndex = mItemIdToMetaMap.indexOfKey(image.xmpRefs[0]); + if (xmpIndex < 0) { + return NAME_NOT_FOUND; + } + + *offset = mItemIdToMetaMap[xmpIndex].offset; + *size = mItemIdToMetaMap[xmpIndex].size; return OK; } diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h index be81b59f63fba35edc22712a387cf4dc05713d8c..62826b6b2548b211f3e81b7bbab7aa0755344227 100644 --- a/media/extractors/mp4/ItemTable.h +++ b/media/extractors/mp4/ItemTable.h @@ -34,7 +34,7 @@ namespace heif { struct AssociationEntry; struct ImageItem; -struct ExifItem; +struct ExternalMetaItem; struct ItemLoc; struct ItemInfo; struct ItemProperty; @@ -42,12 +42,12 @@ struct ItemReference; /* * ItemTable keeps track of all image items (including coded images, grids and - * tiles) inside a HEIF still image (ISO/IEC FDIS 23008-12.2:2017(E)). + * tiles) inside a HEIF/AVIF still image (ISO/IEC FDIS 23008-12.2:2017(E)). */ class ItemTable : public RefBase { public: - explicit ItemTable(DataSourceHelper *source); + ItemTable(DataSourceHelper *source, bool isHeif); status_t parse(uint32_t type, off64_t offset, size_t size); @@ -59,12 +59,15 @@ public: status_t getImageOffsetAndSize( uint32_t *itemIndex, off64_t *offset, size_t *size); status_t getExifOffsetAndSize(off64_t *offset, size_t *size); + status_t getXmpOffsetAndSize(off64_t *offset, size_t *size); protected: ~ItemTable(); private: DataSourceHelper *mDataSource; + // If this is true, then this item table is for a HEIF image. Otherwise it is for an AVIF image. + bool mIsHeif; KeyedVector mItemLocs; Vector mItemInfos; @@ -82,7 +85,7 @@ private: bool mImageItemsValid; uint32_t mCurrentItemIndex; KeyedVector mItemIdToItemMap; - KeyedVector mItemIdToExifMap; + KeyedVector mItemIdToMetaMap; Vector mDisplayables; status_t parseIlocBox(off64_t offset, size_t size); diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index f725a8cac7e7d18593e83f3c1207d025f64f742a..fbcd554046338a54ce26985f9b536f644e6d794b 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -160,6 +160,7 @@ private: uint8_t *mSrcBuffer; bool mIsHeif; + bool mIsAvif; bool mIsAudio; bool mIsUsac = false; sp mItemTable; @@ -429,6 +430,7 @@ MPEG4Extractor::MPEG4Extractor(DataSourceHelper *source, const char *mime) mIsHeif(false), mHasMoovBox(false), mPreferHeif(mime != NULL && !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEIF)), + mIsAvif(false), mFirstTrack(NULL), mLastTrack(NULL) { ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif); @@ -653,8 +655,7 @@ media_status_t MPEG4Extractor::getTrackMetaData( } } - AMediaFormat_copy(meta, track->meta); - return AMEDIA_OK; + return AMediaFormat_copy(meta, track->meta); } status_t MPEG4Extractor::readMetaData() { @@ -685,7 +686,7 @@ status_t MPEG4Extractor::readMetaData() { } } - if (mIsHeif && (mItemTable != NULL) && (mItemTable->countImages() > 0)) { + if ((mIsAvif || mIsHeif) && (mItemTable != NULL) && (mItemTable->countImages() > 0)) { off64_t exifOffset; size_t exifSize; if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) { @@ -694,6 +695,19 @@ status_t MPEG4Extractor::readMetaData() { AMediaFormat_setInt64(mFileMetaData, AMEDIAFORMAT_KEY_EXIF_SIZE, (int64_t)exifSize); } + off64_t xmpOffset; + size_t xmpSize; + if (mItemTable->getXmpOffsetAndSize(&xmpOffset, &xmpSize) == OK) { + // TODO(chz): b/175717339 + // Use a hard-coded string here instead of named keys. The keys are available + // only on API 31+. The mp4 extractor is part of mainline and has min_sdk_version + // of 29. This hard-coded string can be replaced with the named constant once + // the mp4 extractor is built against API 31+. + AMediaFormat_setInt64(mFileMetaData, + "xmp-offset" /*AMEDIAFORMAT_KEY_XMP_OFFSET*/, (int64_t)xmpOffset); + AMediaFormat_setInt64(mFileMetaData, + "xmp-size" /*AMEDIAFORMAT_KEY_XMP_SIZE*/, (int64_t)xmpSize); + } for (uint32_t imageIndex = 0; imageIndex < mItemTable->countImages(); imageIndex++) { AMediaFormat *meta = mItemTable->getImageMeta(imageIndex); @@ -711,7 +725,7 @@ status_t MPEG4Extractor::readMetaData() { } mInitCheck = OK; - ALOGV("adding HEIF image track %u", imageIndex); + ALOGV("adding %s image track %u", mIsHeif ? "HEIF" : "AVIF", imageIndex); Track *track = new Track; if (mLastTrack != NULL) { mLastTrack->next = track; @@ -737,6 +751,10 @@ status_t MPEG4Extractor::readMetaData() { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) { AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_HEIF); + } else if (findTrackByMimePrefix( + MEDIA_MIMETYPE_IMAGE_AVIF) != NULL) { + AMediaFormat_setString(mFileMetaData, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_AVIF); } else { AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_MIME, "application/octet-stream"); @@ -1108,7 +1126,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { void *data; size_t size; - if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { + if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, + &data, &size) + && size >= 5) { const uint8_t *ptr = (const uint8_t *)data; const uint8_t profile = ptr[2] >> 1; const uint8_t bl_compatibility_id = (ptr[4]) >> 4; @@ -1145,8 +1165,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->next = track_b; track_b->next = NULL; - auto id = track_b->meta->mFormat->findEntryByName(AMEDIAFORMAT_KEY_CSD_2); - track_b->meta->mFormat->removeEntryAt(id); + // we want to remove the csd-2 key from the metadata, but + // don't have an AMediaFormat_* function to do so. Settle + // for replacing this csd-2 with an empty csd-2. + uint8_t emptybuffer[8] = {}; + AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_2, + emptybuffer, 0); if (4 == profile || 7 == profile || 8 == profile ) { AMediaFormat_setString(track_b->meta, @@ -2431,7 +2455,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; - AMediaFormat_setBuffer(mLastTrack->meta, + AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4); if (mPath.size() >= 2 @@ -2513,7 +2537,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; - AMediaFormat_setBuffer(mLastTrack->meta, + AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_AVC, buffer.get(), chunk_data_size); break; @@ -2535,7 +2559,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; - AMediaFormat_setBuffer(mLastTrack->meta, + AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_HEVC, buffer.get(), chunk_data_size); *offset += chunk_size; @@ -2569,7 +2593,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("dvcC"): case FOURCC("dvvC"): { - CHECK_EQ(chunk_data_size, 24); + if (chunk_data_size != 24) { + return ERROR_MALFORMED; + } auto buffer = heapbuffer(chunk_data_size); @@ -2680,9 +2706,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("iref"): case FOURCC("ipro"): { - if (mIsHeif) { + if (mIsHeif || mIsAvif) { if (mItemTable == NULL) { - mItemTable = new ItemTable(mDataSource); + mItemTable = new ItemTable(mDataSource, mIsHeif); } status_t err = mItemTable->parse( chunk_type, data_offset, chunk_data_size); @@ -2984,6 +3010,21 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC("pasp"): + { + *offset += chunk_size; + // this must be in a VisualSampleEntry box under the Sample Description Box ('stsd') + // ignore otherwise + if (depth >= 2 && mPath[depth - 2] == FOURCC("stsd")) { + status_t err = parsePaspBox(data_offset, chunk_data_size); + if (err != OK) { + return err; + } + } + + break; + } + case FOURCC("titl"): case FOURCC("perf"): case FOURCC("auth"): @@ -3108,14 +3149,20 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mIsHeif = true; brandSet.erase(FOURCC("mif1")); brandSet.erase(FOURCC("heic")); + } else if (brandSet.count(FOURCC("avif")) > 0 || + brandSet.count(FOURCC("avis")) > 0) { + ALOGV("identified AVIF image"); + mIsAvif = true; + brandSet.erase(FOURCC("avif")); + brandSet.erase(FOURCC("avis")); } if (!brandSet.empty()) { // This means that the file should have moov box. // It could be any iso files (mp4, heifs, etc.) mHasMoovBox = true; - if (mIsHeif) { - ALOGV("identified HEIF image with other tracks"); + if (mIsHeif || mIsAvif) { + ALOGV("identified %s image with other tracks", mIsHeif ? "HEIF" : "AVIF"); } } } @@ -4086,13 +4133,13 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { // custom genre string buffer[size] = '\0'; - AMediaFormat_setString(mFileMetaData, + AMediaFormat_setString(mFileMetaData, metadataKey, (const char *)buffer + 8); } } else { buffer[size] = '\0'; - AMediaFormat_setString(mFileMetaData, + AMediaFormat_setString(mFileMetaData, metadataKey, (const char *)buffer + 8); } } @@ -4156,6 +4203,26 @@ status_t MPEG4Extractor::parseColorInfo(off64_t offset, size_t size) { return OK; } +status_t MPEG4Extractor::parsePaspBox(off64_t offset, size_t size) { + if (size < 8 || size == SIZE_MAX || mLastTrack == NULL) { + return ERROR_MALFORMED; + } + + uint32_t data[2]; // hSpacing, vSpacing + if (mDataSource->readAt(offset, data, 8) < 8) { + return ERROR_IO; + } + uint32_t hSpacing = ntohl(data[0]); + uint32_t vSpacing = ntohl(data[1]); + + if (hSpacing != 0 && vSpacing != 0) { + AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_WIDTH, hSpacing); + AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, vSpacing); + } + + return OK; +} + status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) { if (size < 4 || size == SIZE_MAX) { return ERROR_MALFORMED; @@ -4433,7 +4500,8 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) { return NULL; } - } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1) + || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) { void *data; size_t size; if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) { @@ -4442,9 +4510,12 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { const uint8_t *ptr = (const uint8_t *)data; - if (size < 5 || ptr[0] != 0x81) { // configurationVersion == 1 + if (size < 4 || ptr[0] != 0x81) { // configurationVersion == 1 return NULL; } + if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) { + itemTable = mItemTable; + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_VP9)) { void *data; size_t size; @@ -5013,7 +5084,6 @@ MPEG4Source::MPEG4Source( mStarted(false), mBuffer(NULL), mSrcBuffer(NULL), - mIsHeif(itemTable != NULL), mItemTable(itemTable), mElstShiftStartTicks(elstShiftStartTicks), mElstInitialEmptyEditTicks(elstInitialEmptyEditTicks) { @@ -5050,6 +5120,8 @@ MPEG4Source::MPEG4Source( !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4); mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION); + mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL; + mIsAvif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF) && mItemTable != NULL; if (mIsAVC) { void *data; @@ -6044,7 +6116,7 @@ media_status_t MPEG4Source::read( if (options && options->getSeekTo(&seekTimeUs, &mode)) { ALOGV("seekTimeUs:%" PRId64, seekTimeUs); - if (mIsHeif) { + if (mIsHeif || mIsAvif) { CHECK(mSampleTable == NULL); CHECK(mItemTable != NULL); int32_t imageIndex; @@ -6190,7 +6262,7 @@ media_status_t MPEG4Source::read( newBuffer = true; status_t err; - if (!mIsHeif) { + if (!mIsHeif && !mIsAvif) { err = mSampleTable->getMetaDataForSample(mCurrentSampleIndex, &offset, &size, (uint64_t*)&cts, &isSyncSample, &stts); if(err == OK) { @@ -6237,9 +6309,13 @@ media_status_t MPEG4Source::read( if (newBuffer) { if (mIsPcm) { // The twos' PCM block reader assumes that all samples has the same size. - - uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk() - - mCurrentSampleIndex + 1; + uint32_t lastSampleIndexInChunk = mSampleTable->getLastSampleIndexInChunk(); + if (lastSampleIndexInChunk < mCurrentSampleIndex) { + mBuffer->release(); + mBuffer = nullptr; + return AMEDIA_ERROR_UNKNOWN; + } + uint32_t samplesToRead = lastSampleIndexInChunk - mCurrentSampleIndex + 1; if (samplesToRead > kMaxPcmFrameSize) { samplesToRead = kMaxPcmFrameSize; } @@ -6248,13 +6324,17 @@ media_status_t MPEG4Source::read( samplesToRead, size, mCurrentSampleIndex, mSampleTable->getLastSampleIndexInChunk()); - size_t totalSize = samplesToRead * size; + size_t totalSize = samplesToRead * size; + if (mBuffer->size() < totalSize) { + mBuffer->release(); + mBuffer = nullptr; + return AMEDIA_ERROR_UNKNOWN; + } uint8_t* buf = (uint8_t *)mBuffer->data(); ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize); if (bytesRead < (ssize_t)totalSize) { mBuffer->release(); mBuffer = NULL; - return AMEDIA_ERROR_IO; } @@ -6308,7 +6388,19 @@ media_status_t MPEG4Source::read( if (isSyncSample) { AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } - + + AMediaFormat_setInt64( + meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/, + offset); + + if (mSampleTable != nullptr && + mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) { + AMediaFormat_setInt64( + meta, + "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/, + mSampleTable->getLastSampleIndexInChunk()); + } + ++mCurrentSampleIndex; } } @@ -6458,6 +6550,17 @@ media_status_t MPEG4Source::read( AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } + AMediaFormat_setInt64( + meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/, offset); + + if (mSampleTable != nullptr && + mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) { + AMediaFormat_setInt64( + meta, + "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/, + mSampleTable->getLastSampleIndexInChunk()); + } + ++mCurrentSampleIndex; *out = mBuffer; @@ -6829,7 +6932,8 @@ static bool LegacySniffMPEG4(DataSourceHelper *source, float *confidence) { || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8) || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8) || !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8) - || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)) { + || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8) + || !memcmp(header, "ftypavif", 8) || !memcmp(header, "ftypavis", 8)) { *confidence = 0.4; return true; @@ -6865,6 +6969,8 @@ static bool isCompatibleBrand(uint32_t fourcc) { FOURCC("heic"), // HEIF image FOURCC("msf1"), // HEIF image sequence FOURCC("hevc"), // HEIF image sequence + FOURCC("avif"), // AVIF image + FOURCC("avis"), // AVIF image sequence }; for (size_t i = 0; diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 1e49d504eef834921e84e766308397acc7dbcd81..542a3e6e53c825b8dcc4583a80019ec0ad1bce1a 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -144,6 +144,7 @@ private: bool mIsHeif; bool mHasMoovBox; bool mPreferHeif; + bool mIsAvif; Track *mFirstTrack, *mLastTrack; @@ -160,6 +161,7 @@ private: status_t parseChunk(off64_t *offset, int depth); status_t parseITunesMetaData(off64_t offset, size_t size); status_t parseColorInfo(off64_t offset, size_t size); + status_t parsePaspBox(off64_t offset, size_t size); status_t parse3GPPMetaData(off64_t offset, size_t size, int depth); void parseID3v2MetaData(off64_t offset, uint64_t size); status_t parseQTMetaKey(off64_t data_offset, size_t data_size); diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 9e093eb625c8f13455807702691fe13777e2cb81..2e68809b6f2c9becc5c531aa30d74c16fd53b32d 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -268,6 +268,9 @@ MediaTrackHelper *MPEG2TSExtractor::getTrack(size_t index) { media_status_t MPEG2TSExtractor::getTrackMetaData( AMediaFormat *meta, size_t index, uint32_t /* flags */) { + if (meta == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } sp implMeta = index < mSourceImpls.size() ? mSourceImpls.editItemAt(index)->getFormat() : NULL; if (implMeta == NULL) { diff --git a/media/extractors/tests/Android.bp b/media/extractors/tests/Android.bp index f46fa7bfd16a5ded22892d477bcf370cf02504ea..5d97d9a576cb407551116316939c5b8fa53ad67c 100644 --- a/media/extractors/tests/Android.bp +++ b/media/extractors/tests/Android.bp @@ -26,6 +26,7 @@ package { cc_test { name: "ExtractorUnitTest", gtest: true, + test_suites: ["device-tests"], srcs: ["ExtractorUnitTest.cpp"], @@ -50,6 +51,7 @@ cc_test { "libstagefright_esds", "libstagefright_mpeg2support", "libstagefright_mpeg2extractor", + "libstagefright_foundation_colorutils_ndk", "libstagefright_foundation", "libstagefright_metadatautils", diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp index d91fffa36b2031a1ce8ef3f64839ddd42f4eaca7..84ec1f2167b5a9dde5f5018b4c83f2df196fb83d 100644 --- a/media/extractors/tests/ExtractorUnitTest.cpp +++ b/media/extractors/tests/ExtractorUnitTest.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "ExtractorUnitTest" #include +#include + #include #include #include @@ -503,7 +505,7 @@ TEST_P(ExtractorFunctionalityTest, MetaDataComparisonTest) { &trackSampleRate)); ASSERT_EQ(exChannelCount, trackChannelCount) << "ChannelCount not as expected"; ASSERT_EQ(exSampleRate, trackSampleRate) << "SampleRate not as expected"; - } else { + } else if (!strncmp(extractorMime, "video/", 6)) { int32_t exWidth, exHeight; int32_t trackWidth, trackHeight; ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_WIDTH, &exWidth)); @@ -512,6 +514,8 @@ TEST_P(ExtractorFunctionalityTest, MetaDataComparisonTest) { ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight)); ASSERT_EQ(exWidth, trackWidth) << "Width not as expected"; ASSERT_EQ(exHeight, trackHeight) << "Height not as expected"; + } else { + ALOGV("non a/v track"); } status = cTrack->stop(track); ASSERT_EQ(OK, status) << "Failed to stop the track"; @@ -568,8 +572,9 @@ TEST_P(ExtractorFunctionalityTest, MultipleStartStopTest) { TEST_P(ExtractorFunctionalityTest, SeekTest) { if (mDisableTest) return; - ALOGV("Validates %s Extractor behaviour for different seek modes", mContainer.c_str()); string inputFileName = gEnv->getRes() + get<1>(GetParam()); + ALOGV("Validates %s Extractor behaviour for different seek modes filename %s", + mContainer.c_str(), inputFileName.c_str()); int32_t status = setDataSource(inputFileName); ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor"; @@ -680,7 +685,8 @@ TEST_P(ExtractorFunctionalityTest, SeekTest) { if (seekIdx >= seekablePointsSize) seekIdx = seekablePointsSize - 1; int64_t seekToTimeStamp = seekablePoints[seekIdx]; - if (seekablePointsSize > 1) { + if (seekIdx > 1) { + // pick a time just earlier than this seek point int64_t prevTimeStamp = seekablePoints[seekIdx - 1]; seekToTimeStamp = seekToTimeStamp - ((seekToTimeStamp - prevTimeStamp) >> 3); } @@ -711,11 +717,7 @@ TEST_P(ExtractorFunctionalityTest, SeekTest) { // CMediaTrackReadOptions::SEEK is 8. Using mask 0111b to get true modes switch (mode & 0x7) { case CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC: - if (seekablePointsSize == 1) { - EXPECT_EQ(timeStamp, seekablePoints[seekIdx]); - } else { - EXPECT_EQ(timeStamp, seekablePoints[seekIdx - 1]); - } + EXPECT_EQ(timeStamp, seekablePoints[seekIdx > 0 ? (seekIdx - 1) : 0]); break; case CMediaTrackReadOptions::SEEK_NEXT_SYNC: case CMediaTrackReadOptions::SEEK_CLOSEST_SYNC: @@ -743,8 +745,9 @@ TEST_P(ExtractorFunctionalityTest, MonkeySeekTest) { // TODO(b/155630778): Enable test for wav extractors if (mExtractorName == WAV) return; - ALOGV("Validates %s Extractor behaviour for invalid seek points", mContainer.c_str()); string inputFileName = gEnv->getRes() + get<1>(GetParam()); + ALOGV("Validates %s Extractor behaviour for invalid seek points, filename %s", + mContainer.c_str(), inputFileName.c_str()); int32_t status = setDataSource(inputFileName); ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor"; @@ -832,8 +835,9 @@ TEST_P(ExtractorFunctionalityTest, SanityTest) { // TODO(b/155626946): Enable test for MPEG2 TS/PS extractors if (mExtractorName == MPEG2TS || mExtractorName == MPEG2PS) return; - ALOGV("Validates %s Extractor behaviour for invalid tracks", mContainer.c_str()); string inputFileName = gEnv->getRes() + get<1>(GetParam()); + ALOGV("Validates %s Extractor behaviour for invalid tracks - file %s", + mContainer.c_str(), inputFileName.c_str()); int32_t status = setDataSource(inputFileName); ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor"; @@ -872,13 +876,17 @@ TEST_P(ExtractorFunctionalityTest, SanityTest) { TEST_P(ConfigParamTest, ConfigParamValidation) { if (mDisableTest) return; + const int trackNumber = 0; + string container = GetParam().first; - ALOGV("Validates %s Extractor for input's file properties", container.c_str()); string inputFileName = gEnv->getRes(); inputID inputFileId = GetParam().second; configFormat configParam; getFileProperties(inputFileId, inputFileName, configParam); + ALOGV("Validates %s Extractor for input's file properties, file %s", + container.c_str(), inputFileName.c_str()); + int32_t status = setDataSource(inputFileName); ASSERT_EQ(status, 0) << "SetDataSource failed for " << container << "extractor"; @@ -888,7 +896,7 @@ TEST_P(ConfigParamTest, ConfigParamValidation) { int32_t numTracks = mExtractor->countTracks(); ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip"; - MediaTrackHelper *track = mExtractor->getTrack(0); + MediaTrackHelper *track = mExtractor->getTrack(trackNumber); ASSERT_NE(track, nullptr) << "Failed to get track for index 0"; AMediaFormat *trackFormat = AMediaFormat_new(); @@ -910,7 +918,7 @@ TEST_P(ConfigParamTest, ConfigParamValidation) { AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &trackSampleRate)); ASSERT_EQ(configParam.sampleRate, trackSampleRate) << "SampleRate not as expected"; ASSERT_EQ(configParam.channelCount, trackChannelCount) << "ChannelCount not as expected"; - } else { + } else if (!strncmp(trackMime, "video/", 6)) { int32_t trackWidth, trackHeight; ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth)); ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight)); diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 901b29d051b62a757bfe82b2ca4875d6d62538cd..9e94587dd03db973f2c2f1cf27452a525153ce97 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -440,19 +440,22 @@ media_status_t WAVSource::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { - int64_t pos = 0; - + int64_t pos; + int64_t sampleNumber; + bool overflowed = __builtin_mul_overflow(seekTimeUs, mSampleRate, &sampleNumber); + sampleNumber /= 1000000; if (mWaveFormat == WAVE_FORMAT_MSGSM) { // 65 bytes decode to 320 8kHz samples - int64_t samplenumber = (seekTimeUs * mSampleRate) / 1000000; - int64_t framenumber = samplenumber / 320; - pos = framenumber * 65; + pos = sampleNumber / 320 * 65; } else { - pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3); + int64_t bytesPerFrame; + overflowed |= __builtin_mul_overflow(mNumChannels, mBitsPerSample >> 3, &bytesPerFrame); + overflowed |= __builtin_mul_overflow(bytesPerFrame, sampleNumber, &pos); } - if (pos > (off64_t)mSize) { - pos = mSize; + if (overflowed) { + return AMEDIA_ERROR_MALFORMED; } + pos = std::clamp(pos, (int64_t) 0, (int64_t) mSize); mCurrentPos = pos + mOffset; } diff --git a/media/libaaudio/Android.bp b/media/libaaudio/Android.bp index 05c788c09d4b963b59005bed09c26a9372cd9504..add28e014a1cea7eb5fa50b4397ebdef4554da64 100644 --- a/media/libaaudio/Android.bp +++ b/media/libaaudio/Android.bp @@ -41,4 +41,6 @@ ndk_library { cc_library_headers { name: "libaaudio_headers", export_include_dirs: ["include"], + export_shared_lib_headers: ["aaudio-aidl-cpp"], + shared_libs: ["aaudio-aidl-cpp"], } diff --git a/media/libaaudio/Doxyfile.orig b/media/libaaudio/Doxyfile.orig deleted file mode 100644 index 137facb0be07d65bdcaa85e63aa7ca2804a661d8..0000000000000000000000000000000000000000 --- a/media/libaaudio/Doxyfile.orig +++ /dev/null @@ -1,2303 +0,0 @@ -# Doxyfile 1.8.6 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "My Project" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# 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 = - -# 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 -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = - -# 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 -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# 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 -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /